Skip to content

Commit 070ea30

Browse files
committed
fix: support audio loading, only draw visible decompressed samples to graph
1 parent d868793 commit 070ea30

File tree

5 files changed

+97
-3
lines changed

5 files changed

+97
-3
lines changed

src/components/ReactTimeSeries/ReactTimeSeries.stories.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export default {
66
component: ReactTimeSeries,
77
}
88

9-
export const Primary = () => {
9+
export const SimpleTimeSeries = () => {
1010
const [sample, setSample] = useState({
1111
timeData: [
1212
{ time: 0, value: 0 },
@@ -30,3 +30,17 @@ export const Primary = () => {
3030
/>
3131
)
3232
}
33+
34+
export const WithAudioURL = () => {
35+
const [sample, setSample] = useState({
36+
audioUrl:
37+
"https://s3.amazonaws.com/datasets.workaround.online/voice-samples/001/voice.mp3",
38+
})
39+
return (
40+
<ReactTimeSeries
41+
interface={{}}
42+
sample={sample}
43+
onModifySample={setSample}
44+
/>
45+
)
46+
}

src/components/ReactTimeSeries/index.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import useGetRandomColorUsingHash from "../../hooks/use-get-random-color-using-h
77

88
import MainLayout from "../MainLayout"
99

10+
import fetchAudioData from "../../utils/fetch-audio-data"
11+
1012
const emptyAr = []
1113

1214
// This is a cloudflare CORs proxy from the project maintainer @seveibar
@@ -50,6 +52,7 @@ export const ReactTimeSeriesWithoutContext = ({
5052
const timeData = useAsyncMemo(
5153
async () => {
5254
if (sampleTimeData) return sampleTimeData
55+
return await fetchAudioData(audioUrl)
5356
// TODO load audioUrl
5457
// TODO load csvUrl
5558
},
@@ -103,7 +106,14 @@ export const ReactTimeSeriesWithoutContext = ({
103106
}, [annotation?.timestamps, getRandomColorUsingHash])
104107

105108
const durationGroups = useMemo(() => {
106-
if (!annotation?.durations) return []
109+
if (!annotation?.durations)
110+
return [
111+
{
112+
color: "#888888",
113+
misc: true,
114+
durations: [],
115+
},
116+
]
107117

108118
const availableLabels = Array.from(
109119
new Set(

src/components/Wave/index.js

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,45 @@ const userSelectOffStyle = { userSelect: "none" }
99

1010
const Container = styled("div")({})
1111

12+
const reduceForVisibleDuration = (data, startTime, visibleDuration) => {
13+
const firstInnerIndex = data.findIndex(([t]) => t >= startTime)
14+
let visibleSamples = data
15+
.slice(firstInnerIndex)
16+
.findIndex(([t]) => t >= startTime + visibleDuration)
17+
visibleSamples =
18+
visibleSamples === -1 ? data.length - firstInnerIndex : visibleSamples
19+
const lastInnerIndex = firstInnerIndex + visibleSamples
20+
21+
data = data.slice(Math.max(0, firstInnerIndex - 1), lastInnerIndex + 1)
22+
23+
const minDistance = visibleDuration / 200
24+
const points = [data[0]]
25+
let lastAddedPointIndex = 0
26+
for (let i = 1; i < data.length; i++) {
27+
if (data[i][0] - points[points.length - 1][0] > minDistance) {
28+
// points.push(data[i])
29+
const timeSinceLastPoint = data[i][0] - points[points.length - 1][0]
30+
31+
points.push([
32+
data[i][0] - timeSinceLastPoint / 2,
33+
Math.max(
34+
...data.slice(lastAddedPointIndex + 1, i + 1).map(([, v]) => v)
35+
),
36+
])
37+
38+
points.push([
39+
data[i][0],
40+
Math.min(
41+
...data.slice(lastAddedPointIndex + 1, i + 1).map(([, v]) => v)
42+
),
43+
])
44+
45+
lastAddedPointIndex = i
46+
}
47+
}
48+
return points
49+
}
50+
1251
export const Wave = ({
1352
curves,
1453
width,
@@ -124,7 +163,11 @@ export const Wave = ({
124163
key={i}
125164
stroke={curve.color}
126165
fill="transparent"
127-
points={curve.data
166+
points={reduceForVisibleDuration(
167+
curve.data,
168+
startTimeOnGraph,
169+
visibleDuration
170+
)
128171
.map(([t, y]) => {
129172
const p = transformMatrix.applyToPoint(t, y)
130173
return `${p.x},${p.y}`

src/utils/fetch-audio-data.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
export default async (audioUrl) => {
2+
const AudioContext = window.AudioContext || window.webkitAudioContext
3+
const audioContext = new AudioContext()
4+
const audioBuffer = await fetch(audioUrl)
5+
.then((res) => res.arrayBuffer())
6+
.then((arBuf) => audioContext.decodeAudioData(arBuf))
7+
8+
// Convert into timeData
9+
const channel = audioBuffer.getChannelData(0)
10+
11+
const blockDuration = 1
12+
const samplesPerBlock = audioBuffer.sampleRate * (blockDuration / 1000)
13+
const timeData = []
14+
for (let i = 0; i < channel.length; i += samplesPerBlock) {
15+
const avg =
16+
channel.slice(i, i + samplesPerBlock).reduce((acc, a) => acc + a, 0) /
17+
samplesPerBlock
18+
19+
timeData.push({ time: i * (1 / audioBuffer.sampleRate) * 1000, value: avg })
20+
}
21+
22+
return timeData
23+
}

src/utils/get-minor-major-duration-lines.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ const months = 30 * days
66
const years = 12 * months
77
const timeIntervals = [
88
["1 ns", 0.000001],
9+
["10 ns", 0.00001],
10+
["100 ns", 0.0001],
911
["1 us", 0.001],
12+
["10 us", 0.01],
13+
["100 us", 0.1],
1014
["1 ms", 1],
1115
["5 ms", 5],
1216
["50 ms", 50],

0 commit comments

Comments
 (0)