aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/AudioWaveform.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/AudioWaveform.tsx')
-rw-r--r--src/client/views/AudioWaveform.tsx117
1 files changed, 45 insertions, 72 deletions
diff --git a/src/client/views/AudioWaveform.tsx b/src/client/views/AudioWaveform.tsx
index 8f3b7c2cd..1e676e1f0 100644
--- a/src/client/views/AudioWaveform.tsx
+++ b/src/client/views/AudioWaveform.tsx
@@ -1,6 +1,6 @@
import React = require("react");
import axios from "axios";
-import { action, computed } from "mobx";
+import { action, computed, reaction, IReactionDisposer } from "mobx";
import { observer } from "mobx-react";
import Waveform from "react-audio-waveform";
import { Doc } from "../../fields/Doc";
@@ -12,7 +12,8 @@ import "./AudioWaveform.scss";
import { Colors } from "./global/globalEnums";
export interface AudioWaveformProps {
- duration: number;
+ duration: number; // length of media clip
+ rawDuration: number; // length of underlying media data
mediaPath: string;
layoutDoc: Doc;
trimming: boolean;
@@ -22,19 +23,28 @@ export interface AudioWaveformProps {
@observer
export class AudioWaveform extends React.Component<AudioWaveformProps> {
public static NUMBER_OF_BUCKETS = 100;
+ _disposer: IReactionDisposer | undefined;
@computed get _waveHeight() {
return Math.max(50, this.props.PanelHeight());
}
+
+ @computed get clipStart() { return NumCast(this.props.layoutDoc.clipStart); }
+ @computed get clipEnd() { return NumCast(this.props.layoutDoc.clipEnd, this.props.rawDuration); }
+ @computed get audioBuckets() { return Cast(this.props.layoutDoc.audioBuckets, listSpec("number"), []); }
+ @computed get audioBucketRange() { return Cast(this.props.layoutDoc.audioBucketRange, listSpec("number"), [-1, -1]); }
+ componentWillUnmount() {
+ this._disposer?.();
+ }
componentDidMount() {
- const audioBuckets = Cast(
- this.props.layoutDoc.audioBuckets,
- listSpec("number"),
- []
- );
- if (!audioBuckets.length) {
- this.props.layoutDoc.audioBuckets = new List<number>([0, 0]); /// "lock" to prevent other views from computing the same data
- setTimeout(this.createWaveformBuckets);
- }
+ this._disposer = reaction(() => [this.clipStart, this.clipEnd, this.audioBuckets.length, ...this.audioBucketRange],
+ (range) => {
+ if (range[2] !== AudioWaveform.NUMBER_OF_BUCKETS || range[3] !== range[0] || range[4] !== range[1]) {
+ this.props.layoutDoc.audioBucketRange = new List<number>([range[0], range[1]]); // setting these values here serves as a "lock" to prevent multiple attempts to create the waveform at nearly the same time.
+ this.props.layoutDoc.audioBuckets = new List<number>(numberRange(AudioWaveform.NUMBER_OF_BUCKETS));
+ setTimeout(this.createWaveformBuckets);
+ }
+ }, { fireImmediately: true });
+
}
// decodes the audio file into peaks for generating the waveform
@@ -45,81 +55,44 @@ export class AudioWaveform extends React.Component<AudioWaveformProps> {
context.decodeAudioData(
response.data,
action((buffer) => {
- const decodedAudioData = buffer.getChannelData(0);
+ const rawDecodedAudioData = buffer.getChannelData(0);
+ const startInd = this.clipStart / this.props.rawDuration;
+ const endInd = this.clipEnd / this.props.rawDuration;
+ const decodedAudioData = rawDecodedAudioData.slice(Math.floor(startInd * rawDecodedAudioData.length), Math.floor(endInd * rawDecodedAudioData.length));
const bucketDataSize = Math.floor(
decodedAudioData.length / AudioWaveform.NUMBER_OF_BUCKETS
);
const brange = Array.from(Array(bucketDataSize));
- this.props.layoutDoc.audioBuckets = new List<number>(
- numberRange(AudioWaveform.NUMBER_OF_BUCKETS).map(
- (i: number) =>
- brange.reduce(
- (p, x, j) =>
- Math.abs(
- Math.max(p, decodedAudioData[i * bucketDataSize + j])
- ),
- 0
- ) / 2
- )
+ const bucketList = numberRange(AudioWaveform.NUMBER_OF_BUCKETS).map(
+ (i: number) =>
+ brange.reduce(
+ (p, x, j) =>
+ Math.abs(
+ Math.max(p, decodedAudioData[i * bucketDataSize + j])
+ ),
+ 0
+ ) / 2
);
+ this.props.layoutDoc.audioBucketRange = new List<number>([this.clipStart, this.clipEnd]);
+ this.props.layoutDoc.audioBuckets = new List<number>(bucketList);
})
);
}
);
}
-
- @action
- createTrimBuckets = () => {
- const audioBuckets = Cast(
- this.props.layoutDoc.audioBuckets,
- listSpec("number"),
- []
- );
-
- const start = Math.floor(
- (NumCast(this.props.layoutDoc.clipStart) / this.props.duration) * 100
- );
- const end = Math.floor(
- (NumCast(this.props.layoutDoc.clipEnd) / this.props.duration) * 100
- );
- return audioBuckets.slice(start, end);
- }
-
render() {
- const audioBuckets = Cast(
- this.props.layoutDoc.audioBuckets,
- listSpec("number"),
- []
- );
-
return (
<div className="audioWaveform">
- {this.props.trimming || !this.props.layoutDoc.clipEnd ? (
- <Waveform
- color={Colors.MEDIUM_BLUE}
- height={this._waveHeight}
- barWidth={0.1}
- pos={this.props.duration}
- duration={this.props.duration}
- peaks={
- audioBuckets.length === AudioWaveform.NUMBER_OF_BUCKETS
- ? audioBuckets
- : undefined
- }
- progressColor={Colors.MEDIUM_BLUE}
- />
- ) : (
- <Waveform
- color={Colors.MEDIUM_BLUE}
- height={this._waveHeight}
- barWidth={0.1}
- pos={this.props.duration}
- duration={this.props.duration}
- peaks={this.createTrimBuckets()}
- progressColor={Colors.MEDIUM_BLUE}
- />
- )}
+ <Waveform
+ color={Colors.MEDIUM_BLUE}
+ height={this._waveHeight}
+ barWidth={0.1}
+ pos={this.props.duration}
+ duration={this.props.duration}
+ peaks={this.audioBuckets}
+ progressColor={Colors.MEDIUM_BLUE}
+ />
</div>
);
}