diff options
author | bobzel <zzzman@gmail.com> | 2021-09-23 11:16:53 -0400 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2021-09-23 11:16:53 -0400 |
commit | bafc47fe100002e37c7abcada3dc44f3bfb66f62 (patch) | |
tree | 4522253f889f1ea73667ea6ae9d1b09a80f133ef /src/client/views/AudioWaveform.tsx | |
parent | fdb55f95c79d568395a6a106248b0901c67bdb1e (diff) |
a bunch of fixes to audio timelines to support undoing clips, simplifying audioWaveform and having it recompute when a clip is made.
Diffstat (limited to 'src/client/views/AudioWaveform.tsx')
-rw-r--r-- | src/client/views/AudioWaveform.tsx | 117 |
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> ); } |