Skip to content

Commit

Permalink
fix: Merge pull request #7 from UniversalDataTool/audio-playback
Browse files Browse the repository at this point in the history
Audio Playback
  • Loading branch information
seveibar committed Nov 23, 2020
2 parents f54111d + b7b95e5 commit 9f6503e
Show file tree
Hide file tree
Showing 13 changed files with 259 additions and 7 deletions.
6 changes: 6 additions & 0 deletions src/components/MainLayout/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ export const MainLayout = ({
onChangeTimestamps,
enabledTools = defaultEnabledTools,
showValues = false,
onStartPlayback,
onStopPlayback,
isPlayingMedia,
}) => {
const themeColors = useColors()
const [activeDurationGroup, setActiveDurationGroup] = useState(null)
Expand Down Expand Up @@ -173,6 +176,9 @@ export const MainLayout = ({
selectedDurationIndex={selectedDurationIndex}
durationGroups={durationGroups}
allowCustomLabels={allowCustomLabels}
onStartPlayback={onStartPlayback}
onStopPlayback={onStopPlayback}
isPlayingMedia={isPlayingMedia}
/>
<Timeline
timeFormat={timeFormat}
Expand Down
29 changes: 29 additions & 0 deletions src/components/MainLayout/index.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,32 @@ export const ExampleMiscLayer = () => {
/>
)
}

export const AudioPlayback = () => {
const [durationGroups, setDurationGroups] = useState([])

const [timestamps, setTimestamps] = useState([])

const [isPlayingMedia, setIsPlayingMedia] = useState(false)

return (
<MainLayout
timeFormat="dates"
curveGroups={[
[
{
data: tesla.curve2017.sort((a, b) => a[0] - b[0]),
color: solarized.green,
},
],
]}
durationGroups={durationGroups}
timestamps={timestamps}
onChangeTimestamps={setTimestamps}
onChangeDurationGroups={setDurationGroups}
onStartPlayback={() => setIsPlayingMedia(true)}
onStopPlayback={() => setIsPlayingMedia(false)}
isPlayingMedia={isPlayingMedia}
/>
)
}
14 changes: 10 additions & 4 deletions src/components/MouseTransformHandler/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useRef, useEffect } from "react"
import React, { useState, useRef, useEffect, useCallback } from "react"
import { styled } from "@material-ui/core/styles"
import useEventCallback from "use-event-callback"
import useToolMode from "../../hooks/use-tool-mode"
Expand Down Expand Up @@ -146,14 +146,20 @@ export const MouseTransformHandler = ({
e.preventDefault()
})

// TODO
const containerMountCallback = useCallback((ref) => {
if (ref === null) {
containerRef.current.removeEventListener("wheel", onWheel)
}
containerRef.current = ref
ref.addEventListener("wheel", onWheel, { passive: false })
}, [])

return (
<Container
ref={containerRef}
ref={containerMountCallback}
onMouseMove={onMouseMove}
onMouseDown={onMouseDown}
onMouseUp={onMouseUp}
onWheel={onWheel}
onContextMenu={onContextMenu}
>
{children}
Expand Down
16 changes: 16 additions & 0 deletions src/components/ReactTimeSeries/ReactTimeSeries.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,19 @@ export const LargeTimeNoneFormat = () => {
/>
)
}

export const AudioPlayback = () => {
return (
<ReactTimeSeries
interface={{
timeFormat: "none",
allowCustomLabels: true,
}}
sample={{
audioUrl:
"https://s3.amazonaws.com/datasets.workaround.online/voice-samples/001/voice.mp3",
}}
onModifySample={() => null}
/>
)
}
45 changes: 44 additions & 1 deletion src/components/ReactTimeSeries/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import React, { useMemo, useState } from "react"
import React, { useMemo, useState, useEffect, useRef } from "react"
import { setIn } from "seamless-immutable"
import useEventCallback from "use-event-callback"
import { useAsyncMemo } from "use-async-memo"
import { RecoilRoot } from "recoil"
import useGetRandomColorUsingHash from "../../hooks/use-get-random-color-using-hash"
import Measure from "react-measure"
import { useSetTimeCursorTime } from "../../hooks/use-time-cursor-time"
import useRootAudioElm from "../../hooks/use-root-audio-elm"

import MainLayout from "../MainLayout"

Expand Down Expand Up @@ -52,6 +54,7 @@ export const ReactTimeSeriesWithoutContext = ({

const timeDataAvailable = [sampleTimeData, audioUrl, csvUrl].some(Boolean)

const [isPlayingMedia, setIsPlayingMedia] = useState(false)
const [error, setError] = useState(null)
const timeData = useAsyncMemo(
async () => {
Expand Down Expand Up @@ -195,6 +198,43 @@ export const ReactTimeSeriesWithoutContext = ({
if (!widthProp) setWidth(bounds.width)
})

const audioSource = useRef()

const [, setRootAudioElm] = useRootAudioElm()
const setTimeCursorTime = useSetTimeCursorTime()

useEffect(() => {
if (audioUrl) {
audioSource.current = new Audio(audioUrl)
setTimeCursorTime(0)
}
}, [])

const onAudioTimeChanged = useEventCallback(() => {
setTimeCursorTime(audioSource.current.currentTime * 1000)
})
const onAudioPaused = useEventCallback(() => {
audioSource.current.removeEventListener("timeupdate", onAudioTimeChanged)
audioSource.current.removeEventListener("pause", onAudioPaused)
})

const onStartPlayback = useEventCallback(() => {
setIsPlayingMedia(true)
if (audioSource.current) {
audioSource.current.play()
audioSource.current.addEventListener("timeupdate", onAudioTimeChanged)
audioSource.current.addEventListener("pause", onAudioPaused)
setRootAudioElm(audioSource.current)
}
})

const onStopPlayback = useEventCallback(() => {
setIsPlayingMedia(false)
if (audioSource.current) {
audioSource.current.pause()
}
})

if (timeDataLoading) return "loading" // TODO real loader

if (!timeData) {
Expand Down Expand Up @@ -228,6 +268,9 @@ export const ReactTimeSeriesWithoutContext = ({
allowCustomLabels={allowCustomLabels}
enabledTools={enabledTools}
showValues={showValues}
onStartPlayback={onStartPlayback}
onStopPlayback={onStopPlayback}
isPlayingMedia={isPlayingMedia}
/>
</div>
)}
Expand Down
19 changes: 19 additions & 0 deletions src/components/Timeline/Timeline.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,22 @@ export const TimeWithTextMarkers = (args) => {
/>
)
}

export const TimeWithCurrentTimeCursor = (args) => {
const colors = useColors()
return (
<Timeline
{...args}
timeFormat="timecolons"
width={500}
visibleTimeStart={0}
visibleTimeEnd={60000 * 80}
timeCursorTime={20 * 60000}
timestamps={[
{ time: 10 * 60000, color: colors.cyan, label: "Timestamp 1" },
{ time: 50 * 60000, color: colors.red, label: "Another Timestamp" },
]}
gridLineMetrics={getMinorMajorDurationLines(new Matrix(), 500)}
/>
)
}
54 changes: 52 additions & 2 deletions src/components/Timeline/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import React from "react"
import React, { useRef } from "react"
import range from "lodash/range"
import { styled } from "@material-ui/core/styles"
import useColors from "../../hooks/use-colors"
import TimeStamp from "../TimeStamp"
import {
useTimeCursorTime,
useSetTimeCursorTime,
} from "../../hooks/use-time-cursor-time"
import useRootAudioElm from "../../hooks/use-root-audio-elm"
import useEventCallback from "use-event-callback"

import { formatTime } from "../../utils/format-time"

Expand All @@ -11,6 +17,7 @@ const Container = styled("div")(({ width, themeColors }) => ({
overflow: "hidden",
position: "relative",
height: 64,
cursor: "pointer",
borderBottom: `1px solid ${themeColors.Selection}`,
color: themeColors.fg,
}))
Expand All @@ -21,13 +28,25 @@ const TimeText = styled("div")(({ x, faded }) => ({
fontSize: 12,
fontVariantNumeric: "tabular-nums",
position: "absolute",
top: 16,
left: x,
borderLeft: "1px solid rgba(255,255,255,0.5)",
paddingLeft: 4,
whiteSpace: "pre-wrap",
opacity: faded ? 0.25 : 0.75,
}))

const TimeCursor = styled("div")(({ left, themeColors }) => ({
position: "absolute",
width: 0,
height: 0,
top: 0,
left: left - 6,
borderLeft: "8px solid transparent",
borderRight: "8px solid transparent",
borderTop: `12px solid ${themeColors.green}`,
}))

const Svg = styled("svg")({
position: "absolute",
left: 0,
Expand All @@ -43,6 +62,7 @@ export const Timeline = ({
gridLineMetrics,
onClickTimestamp,
onRemoveTimestamp,
timeCursorTime: timeCursorTimeProp,
}) => {
const themeColors = useColors()
const visibleDuration = visibleTimeEnd - visibleTimeStart
Expand All @@ -51,15 +71,39 @@ export const Timeline = ({
const timeTextTimes = range(timeTextCount).map(
(i) => visibleTimeStart + (visibleDuration / timeTextCount) * i
)
const recoilTimeCursorTime = useTimeCursorTime()
const setTimeCursorTime = useSetTimeCursorTime()
const [rootAudioElm] = useRootAudioElm()
const timeCursorTime =
timeCursorTimeProp === undefined ? recoilTimeCursorTime : timeCursorTimeProp

const {
numberOfMajorGridLines,
majorGridLinePixelOffset,
majorGridLinePixelDistance,
} = gridLineMetrics

const containerRef = useRef()

const onClickTimeline = useEventCallback((e) => {
if (!rootAudioElm) return
const { clientX } = e
const pxDistanceFromStart =
clientX - containerRef.current.getBoundingClientRect().left
const time =
(pxDistanceFromStart / width) * (visibleTimeEnd - visibleTimeStart) +
visibleTimeStart
rootAudioElm.currentTime = time / 1000
setTimeCursorTime(time)
})

return (
<Container themeColors={themeColors} width={width}>
<Container
ref={containerRef}
themeColors={themeColors}
width={width}
onClick={rootAudioElm ? onClickTimeline : undefined}
>
{range(timeTextCount).map((timeTextIndex) => (
<TimeText
key={timeTextIndex}
Expand Down Expand Up @@ -102,6 +146,12 @@ export const Timeline = ({
/>
)
})}
{timeCursorTime !== undefined && (
<TimeCursor
themeColors={themeColors}
left={((timeCursorTime - visibleTimeStart) / visibleDuration) * width}
/>
)}
</Container>
)
}
Expand Down
3 changes: 3 additions & 0 deletions src/components/Toolbar/Toolbar.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ export const Primary = () => {
)
)
}}
onStartPlayback={() => null}
onStopPlayback={() => null}
isPlayingMedia={false}
/>
)
}
21 changes: 21 additions & 0 deletions src/components/Toolbar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import NormalSelect from "react-select"
import LocationOnIcon from "@material-ui/icons/LocationOn"
import TimelapseIcon from "@material-ui/icons/Timelapse"
import ZoomInIcon from "@material-ui/icons/ZoomIn"
import PlayCircleOutlineIcon from "@material-ui/icons/PlayCircleOutline"
import PauseCircleOutlineIcon from "@material-ui/icons/PauseCircleOutline"
import Color from "color"

const Container = styled("div")(({ themeColors }) => ({
Expand Down Expand Up @@ -96,6 +98,9 @@ export const Toolbar = ({
selectedDurationIndex,
onChangeSelectedItemLabel,
allowCustomLabels = false,
onStartPlayback,
onStopPlayback,
isPlayingMedia = false,
}) => {
const themeColors = useColors()
const [mode, setToolMode] = useToolMode()
Expand Down Expand Up @@ -219,6 +224,22 @@ export const Toolbar = ({
)}
</Box>
<ButtonGroup size="small">
{onStartPlayback && !isPlayingMedia && (
<Button
onClick={onStartPlayback}
className={classnames({ active: isPlayingMedia })}
>
<PlayCircleOutlineIcon />
</Button>
)}
{onStopPlayback && isPlayingMedia && (
<Button
onClick={onStopPlayback}
className={classnames({ active: isPlayingMedia })}
>
<PauseCircleOutlineIcon />
</Button>
)}
<Button
onClick={onSelectCreateTool}
className={classnames({ active: mode === "create" })}
Expand Down
Loading

0 comments on commit 9f6503e

Please sign in to comment.