From 019859e20b52b7efa697033838c71bd65fe83ed2 Mon Sep 17 00:00:00 2001 From: Toshihiko Yanase Date: Wed, 29 Nov 2023 18:16:23 +0900 Subject: [PATCH 1/3] Use WaveSurfer instead of audio tag. --- .../ts/components/ArtifactCardMedia.tsx | 10 ++- .../components/WaveSurferArtifactViewer.tsx | 79 +++++++++++++++++++ package-lock.json | 13 ++- package.json | 3 +- 4 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 optuna_dashboard/ts/components/WaveSurferArtifactViewer.tsx diff --git a/optuna_dashboard/ts/components/ArtifactCardMedia.tsx b/optuna_dashboard/ts/components/ArtifactCardMedia.tsx index 282b7432..fecef837 100644 --- a/optuna_dashboard/ts/components/ArtifactCardMedia.tsx +++ b/optuna_dashboard/ts/components/ArtifactCardMedia.tsx @@ -3,6 +3,7 @@ import { ThreejsArtifactViewer, isThreejsArtifact, } from "./ThreejsArtifactViewer" +import { WaveSurferArtifactViewer } from "./WaveSurferArtifactViewer" import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile" import { CardMedia, Box } from "@mui/material" @@ -43,9 +44,12 @@ export const ArtifactCardMedia: FC<{ alignItems: "center", }} > - + ) } else if (artifact.mimetype.startsWith("image")) { diff --git a/optuna_dashboard/ts/components/WaveSurferArtifactViewer.tsx b/optuna_dashboard/ts/components/WaveSurferArtifactViewer.tsx new file mode 100644 index 00000000..44dc6455 --- /dev/null +++ b/optuna_dashboard/ts/components/WaveSurferArtifactViewer.tsx @@ -0,0 +1,79 @@ +import React, { useCallback, useEffect, useState, useRef } from "react" +import WaveSurfer from "wavesurfer.js" +import { Box } from "@mui/material" + +interface WaveSurferArtifactViewerProps { + height: number + waveColor: string + progressColor: string + url: string +} + +// WaveSurfer hook +const useWavesurfer = ( + containerRef: React.MutableRefObject, + options: WaveSurferArtifactViewerProps +) => { + const [wavesurfer, setWavesurfer] = useState(null) + + // Initialize wavesurfer when the container mounts + // or any of the props change + useEffect(() => { + if (!containerRef.current) return + + const ws = WaveSurfer.create({ + ...options, + container: containerRef.current, + }) + + setWavesurfer(ws) + + return () => { + ws.destroy() + } + }, [options, containerRef]) + + return wavesurfer +} + +// Create a React component that will render wavesurfer. +// Props are wavesurfer options. +export const WaveSurferArtifactViewer: React.FC< + WaveSurferArtifactViewerProps +> = (props) => { + const containerRef = useRef(null!) + const [isPlaying, setIsPlaying] = useState(false) + const wavesurfer = useWavesurfer(containerRef, props) + + // On play button click + const onPlayClick = useCallback(() => { + if (!wavesurfer) return + wavesurfer.isPlaying() ? wavesurfer.pause() : wavesurfer.play() + }, [wavesurfer]) + + // Initialize wavesurfer when the container mounts + // or any of the props change + useEffect(() => { + if (!wavesurfer) return + + setIsPlaying(false) + + const subscriptions = [ + wavesurfer.on("play", () => setIsPlaying(true)), + wavesurfer.on("pause", () => setIsPlaying(false)), + ] + + return () => { + subscriptions.forEach((unsub) => unsub()) + } + }, [wavesurfer]) + + return ( + +
+ + + ) +} diff --git a/package-lock.json b/package-lock.json index 409d4225..05c79bb0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,7 +32,8 @@ "rehype-raw": "^6.1.1", "remark-gfm": "^3.0.1", "remark-math": "^5.1.1", - "three": "^0.155.0" + "three": "^0.155.0", + "wavesurfer.js": "^7.4.12" }, "devDependencies": { "@babel/core": "^7.14.3", @@ -14824,6 +14825,11 @@ "node": ">=10.13.0" } }, + "node_modules/wavesurfer.js": { + "version": "7.4.12", + "resolved": "https://registry.npmjs.org/wavesurfer.js/-/wavesurfer.js-7.4.12.tgz", + "integrity": "sha512-KzH4LkcOp8LECs9cOVIPBl6vsSoICKuZz+v5kh/zvxilpaVszU+QKC+4s2KEAqcCxBCecg3cNSg4RqAx278F8g==" + }, "node_modules/web-namespaces": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", @@ -25938,6 +25944,11 @@ "graceful-fs": "^4.1.2" } }, + "wavesurfer.js": { + "version": "7.4.12", + "resolved": "https://registry.npmjs.org/wavesurfer.js/-/wavesurfer.js-7.4.12.tgz", + "integrity": "sha512-KzH4LkcOp8LECs9cOVIPBl6vsSoICKuZz+v5kh/zvxilpaVszU+QKC+4s2KEAqcCxBCecg3cNSg4RqAx278F8g==" + }, "web-namespaces": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", diff --git a/package.json b/package.json index 3e624c56..9e6e59e9 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,8 @@ "rehype-raw": "^6.1.1", "remark-gfm": "^3.0.1", "remark-math": "^5.1.1", - "three": "^0.155.0" + "three": "^0.155.0", + "wavesurfer.js": "^7.4.12" }, "devDependencies": { "@babel/core": "^7.14.3", From dfc699e69cc8faf4fadfe83d77e4a52829e18ad5 Mon Sep 17 00:00:00 2001 From: Toshihiko Yanase Date: Wed, 29 Nov 2023 18:25:00 +0900 Subject: [PATCH 2/3] Update code comments. --- .../ts/components/WaveSurferArtifactViewer.tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/optuna_dashboard/ts/components/WaveSurferArtifactViewer.tsx b/optuna_dashboard/ts/components/WaveSurferArtifactViewer.tsx index 44dc6455..65d87bfb 100644 --- a/optuna_dashboard/ts/components/WaveSurferArtifactViewer.tsx +++ b/optuna_dashboard/ts/components/WaveSurferArtifactViewer.tsx @@ -9,15 +9,12 @@ interface WaveSurferArtifactViewerProps { url: string } -// WaveSurfer hook const useWavesurfer = ( containerRef: React.MutableRefObject, options: WaveSurferArtifactViewerProps ) => { const [wavesurfer, setWavesurfer] = useState(null) - // Initialize wavesurfer when the container mounts - // or any of the props change useEffect(() => { if (!containerRef.current) return @@ -36,8 +33,7 @@ const useWavesurfer = ( return wavesurfer } -// Create a React component that will render wavesurfer. -// Props are wavesurfer options. +// Create a React component of wavesurfer. export const WaveSurferArtifactViewer: React.FC< WaveSurferArtifactViewerProps > = (props) => { @@ -45,14 +41,11 @@ export const WaveSurferArtifactViewer: React.FC< const [isPlaying, setIsPlaying] = useState(false) const wavesurfer = useWavesurfer(containerRef, props) - // On play button click const onPlayClick = useCallback(() => { if (!wavesurfer) return wavesurfer.isPlaying() ? wavesurfer.pause() : wavesurfer.play() }, [wavesurfer]) - // Initialize wavesurfer when the container mounts - // or any of the props change useEffect(() => { if (!wavesurfer) return From afe0299e4ffe6bdc68cddbbed17c76e2067a1368 Mon Sep 17 00:00:00 2001 From: Toshihiko Yanase Date: Wed, 6 Dec 2023 22:31:23 +0900 Subject: [PATCH 3/3] Do not observe options of wavesurfer. --- optuna_dashboard/ts/components/WaveSurferArtifactViewer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/optuna_dashboard/ts/components/WaveSurferArtifactViewer.tsx b/optuna_dashboard/ts/components/WaveSurferArtifactViewer.tsx index 65d87bfb..74177602 100644 --- a/optuna_dashboard/ts/components/WaveSurferArtifactViewer.tsx +++ b/optuna_dashboard/ts/components/WaveSurferArtifactViewer.tsx @@ -28,7 +28,7 @@ const useWavesurfer = ( return () => { ws.destroy() } - }, [options, containerRef]) + }, [containerRef]) return wavesurfer }