From ce3e3b1b217a02a65a3bc4eae5eccb7ca0842fac Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 9 Aug 2020 10:57:25 -0400 Subject: cleaning up AudioBox --- src/Utils.ts | 4 +- src/client/views/nodes/AudioBox.scss | 3 +- src/client/views/nodes/AudioBox.tsx | 357 +++++++++++------------------------ 3 files changed, 117 insertions(+), 247 deletions(-) (limited to 'src') diff --git a/src/Utils.ts b/src/Utils.ts index 0be27b25d..d9a5353e8 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -547,7 +547,7 @@ export function setupMoveUpEvents( target: object, e: React.PointerEvent, moveEvent: (e: PointerEvent, down: number[], delta: number[]) => boolean, - upEvent: (e: PointerEvent) => void, + upEvent: (e: PointerEvent, movement: number[]) => void, clickEvent: (e: PointerEvent, doubleTap?: boolean) => void, stopPropagation: boolean = true, stopMovePropagation: boolean = true @@ -571,7 +571,7 @@ export function setupMoveUpEvents( const _upEvent = (e: PointerEvent): void => { (target as any)._doubleTap = (Date.now() - (target as any)._lastTap < 300); (target as any)._lastTap = Date.now(); - upEvent(e); + upEvent(e, [e.clientX - (target as any)._downX, e.clientY - (target as any)._downY]); if (Math.abs(e.clientX - (target as any)._downX) < 4 && Math.abs(e.clientY - (target as any)._downY) < 4) { clickEvent(e, (target as any)._doubleTap); } diff --git a/src/client/views/nodes/AudioBox.scss b/src/client/views/nodes/AudioBox.scss index c0743933e..973de979e 100644 --- a/src/client/views/nodes/AudioBox.scss +++ b/src/client/views/nodes/AudioBox.scss @@ -179,7 +179,8 @@ height: 100%; overflow: hidden; z-index: -1000; - bottom: -30%; + bottom: 0; + pointer-events: none; } .audiobox-linker, diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 4805b8643..0fb191c05 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -31,9 +31,7 @@ declare class MediaRecorder { // whatever MediaRecorder has constructor(e: any); } -export const audioSchema = createSchema({ - playOnSelect: "boolean" -}); +export const audioSchema = createSchema({ playOnSelect: "boolean" }); type AudioDocument = makeInterface<[typeof documentSchema, typeof audioSchema]>; const AudioDocument = makeInterface(documentSchema, audioSchema); @@ -66,13 +64,11 @@ export class AudioBox extends ViewBoxAnnotatableComponent = []; _timeline: Opt; _duration = 0; - _containerX: number = 0; - _invertedX: boolean = false; - private _isPointerDown = false; + _markerStart: number = 0; private _currMarker: any; @observable _visible: boolean = false; - @observable _currX: number = 0; + @observable _markerEnd: number = 0; @observable _position: number = 0; @observable _buckets: Array = new Array(); @observable _waveHeight: number | undefined = this.layoutDoc._height; @@ -82,6 +78,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent { runInAction(() => AudioBox._scrubTime = 0); runInAction(() => AudioBox._scrubTime = timeInMillisFrom1970); }; @computed get recordingStart() { return Cast(this.dataDoc[this.props.fieldKey + "-recordingStart"], DateField)?.date.getTime(); } + @computed get audioDuration() { return NumCast(this.dataDoc.duration); } async slideTemplate() { return (await Cast((await Cast(Doc.UserDoc().slidesBtn, Doc) as Doc).dragFactory, Doc) as Doc); } constructor(props: Readonly) { @@ -137,21 +134,11 @@ export class AudioBox extends ViewBoxAnnotatableComponent { + playFrom = (seekTimeInSeconds: number, endTime: number = this.audioDuration) => { let play; clearTimeout(play); this._duration = endTime - seekTimeInSeconds; @@ -218,7 +205,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent this.audioState = "playing"); - if (endTime !== this.dataDoc.duration) { + if (endTime !== this.audioDuration) { play = setTimeout(() => this.pause(), (this._duration) * 1000); // use setTimeout to play a specific duration } } else { @@ -230,11 +217,10 @@ export class AudioBox extends ViewBoxAnnotatableComponent { if (this.audioState === "recording") { + setTimeout(this.updateRecordTime, 30); if (this._paused) { - setTimeout(this.updateRecordTime, 30); this._pausedTime += (new Date().getTime() - this._recordStart) / 1000; } else { - setTimeout(this.updateRecordTime, 30); this.layoutDoc.currentTimecode = (new Date().getTime() - this._recordStart - this.pauseTime) / 1000; } } @@ -353,74 +339,55 @@ export class AudioBox extends ViewBoxAnnotatableComponent { - e.stopPropagation(); - e.preventDefault(); - this._isPointerDown = true; - this._timeline?.setPointerCapture(e.pointerId); - - this.start(this._ele!.currentTime); - const rect = (e.target as any).getBoundingClientRect(); - this._containerX = this._currX = (e.clientX - rect.x) / rect.width * NumCast(this.dataDoc.duration); - - document.removeEventListener("pointermove", this.onPointerMoveTimeline); - document.addEventListener("pointermove", this.onPointerMoveTimeline); - document.removeEventListener("pointerup", this.onPointerUpTimeline); - document.addEventListener("pointerup", this.onPointerUpTimeline); + const toTimeline = (screen_delta: number) => screen_delta / rect.width * this.audioDuration; + this._markerStart = this._markerEnd = toTimeline(e.clientX - rect.x); + setupMoveUpEvents(this, e, action((e: PointerEvent) => { + this._visible = true; + this._markerEnd = toTimeline(e.clientX - rect.x); + if (this._markerEnd < this._markerStart) { + const tmp = this._markerStart; + this._markerStart = this._markerEnd; + this._markerEnd = tmp; + } + return false; + }), + action((e: PointerEvent, movement: number[]) => { + if (Math.abs(movement[0]) > 15) { + this.createNewMarker(this._markerStart, toTimeline(e.clientX - rect.x)); + } + this._visible = false; + }), + emptyFunction); } - - // ending the drag event for marker resizing - @action - onPointerUpTimeline = (e: PointerEvent): void => { - e.stopPropagation(); - e.preventDefault(); - this._isPointerDown = false; - - const rect = (e.target as any).getBoundingClientRect(); - const time = (e.clientX - rect.x) / rect.width * NumCast(this.dataDoc.duration); - - // if drag is greater than 15px (didn't use setupMoveEvent) - (this._visible && Math.abs(this._currX - this._containerX) * rect.width / NumCast(this.dataDoc.duration) > 15) ? this.end(time) : this._start = 0; - this._visible = false; - - this._containerX = 0; - this._timeline?.releasePointerCapture(e.pointerId); - - document.removeEventListener("pointermove", this.onPointerMoveTimeline); - document.removeEventListener("pointerup", this.onPointerUpTimeline); + // returns the selection container + @computed get container() { + return
} - // resizes the marker while dragging + // creates a new marker @action - onPointerMoveTimeline = (e: PointerEvent) => { - e.stopPropagation(); - e.preventDefault(); - - if (!this._isPointerDown) { - return; - } - this._visible = true; - const rect = (e.target as any).getBoundingClientRect(); - - this._currX = (e.clientX - rect.x) / rect.width * NumCast(this.dataDoc.duration); - - (this._currX - this._containerX < 0) ? this._invertedX = true : this._invertedX = false; + createNewMarker(audioStart: number, audioEnd: number) { + const newMarker = Docs.Create.LabelDocument({ + title: ComputedField.MakeFunction(`formatToTime(self.audioStart) + "-" + formatToTime(self.audioEnd)`) as any, isLabel: false, + useLinkSmallAnchor: true, hideLinkButton: true, audioStart, audioEnd, _showSidebar: false, + _autoHeight: true, annotationOn: this.props.Document + }) + this.addMark(newMarker); } - // returns the selection container - @computed get container() { - return
- } - - // creates a new label + // adds an annotation marker or label @action - newMarker(marker: Doc) { + addMark(marker: Doc) { marker.data = ""; if (this.dataDoc[this.annotationKey]) { this.dataDoc[this.annotationKey].push(marker); @@ -429,104 +396,39 @@ export class AudioBox extends ViewBoxAnnotatableComponent([newMarker]); - } - - this._start = 0; - } - // starting the drag event for marker resizing onPointerDown = (e: React.PointerEvent, m: any, left: boolean): void => { - e.stopPropagation(); - e.preventDefault(); - this._isPointerDown = true; this._currMarker = m; - this._timeline?.setPointerCapture(e.pointerId); this._left = left; - - document.removeEventListener("pointermove", this.onPointerMove); - document.addEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); - document.addEventListener("pointerup", this.onPointerUp); - } - - // ending the drag event for marker resizing - @action - onPointerUp = (e: PointerEvent): void => { - e.stopPropagation(); - e.preventDefault(); - this._isPointerDown = false; - this._dragging = false; - const rect = (e.target as any).getBoundingClientRect(); - this._ele!.currentTime = this.layoutDoc.currentTimecode = (e.clientX - rect.x) / rect.width * NumCast(this.dataDoc.duration); - - this._timeline?.releasePointerCapture(e.pointerId); - - document.removeEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); - } - - // resizes the marker while dragging - onPointerMove = async (e: PointerEvent) => { - e.stopPropagation(); - e.preventDefault(); - - if (!this._isPointerDown) { - return; - } - - const rect = await (e.target as any).getBoundingClientRect(); - - const newTime = (e.clientX - rect.x) / rect.width * NumCast(this.dataDoc.duration); - - this.changeMarker(this._currMarker, newTime); + const toTimeline = (screen_delta: number) => screen_delta / rect.width * this.audioDuration; + setupMoveUpEvents(this, e, () => { + this.changeMarker(this._currMarker, toTimeline(e.clientX - rect.x)); + return false; + }, + () => this._ele!.currentTime = this.layoutDoc.currentTimecode = toTimeline(e.clientX - rect.x), + emptyFunction); } // updates the marker with the new time @action changeMarker = (m: any, time: any) => { - DocListCast(this.dataDoc[this.annotationKey]).forEach((marker: Doc) => { - if (this.isSame(marker, m)) { - this._left ? marker.audioStart = time : marker.audioEnd = time; - } - }); + DocListCast(this.dataDoc[this.annotationKey]).filter(marker => this.isSame(marker, m)).forEach(marker => + this._left ? marker.audioStart = time : marker.audioEnd = time); } // checks if the two markers are the same with start and end time isSame = (m1: any, m2: any) => { - if (m1.audioStart === m2.audioStart && m1.audioEnd === m2.audioEnd) { - return true; - } - return false; + return m1.audioStart === m2.audioStart && m1.audioEnd === m2.audioEnd; } // instantiates a new array of size 500 for marker layout markers = () => { - const increment = NumCast(this.layoutDoc.duration) / 500; + const increment = this.audioDuration / 500; this._count = []; for (let i = 0; i < 500; i++) { this._count.push([increment * i, 0]); } - } // makes sure no markers overlaps each other by setting the correct position and width @@ -551,7 +453,6 @@ export class AudioBox extends ViewBoxAnnotatableComponent= m.audioStart && this._count[i][0] <= m.audioEnd) { this._count[i][1] = max; } - } if (this.dataDoc.markerAmount < max) { @@ -567,8 +468,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent; } @@ -612,10 +513,10 @@ export class AudioBox extends ViewBoxAnnotatableComponent { const observer = new _global.ResizeObserver(action((entries: any) => { - for (const entry of entries) { - this.update(entry.contentRect.width, entry.contentRect.height); - this._position = entry.contentRect.width; - } + Array.from(entries).map(e => e as any).filter(e => e.contentRect.width).forEach(e => { + this.update(e.contentRect.width, e.contentRect.height); + this._position = e.contentRect.width; + }) })); timeline && observer.observe(timeline); @@ -625,36 +526,29 @@ export class AudioBox extends ViewBoxAnnotatableComponent { + const scaleCanvas = (canvas: HTMLCanvasElement) => { + let oldWidth = canvas.width; + let oldHeight = canvas.height; + canvas.style.height = `${height}`; + canvas.style.width = `${width}`; + + const ratio1 = oldWidth / window.innerWidth; + const ratio2 = oldHeight / window.innerHeight; + const context = canvas.getContext('2d'); + if (context) { + context.scale(ratio1, ratio2); + } + } if (height) { const height = 0.8 * NumCast(this.layoutDoc._height); let canvas2 = document.getElementsByTagName("canvas")[0]; if (canvas2) { - let oldWidth = canvas2.width; - let oldHeight = canvas2.height; - canvas2.style.height = `${height}`; - canvas2.style.width = `${width}`; - - const ratio1 = oldWidth / window.innerWidth; - const ratio2 = oldHeight / window.innerHeight; - const context = canvas2.getContext('2d'); - if (context) { - context.scale(ratio1, ratio2); - } + scaleCanvas(canvas2); } const canvas1 = document.getElementsByTagName("canvas")[1]; if (canvas1) { - const oldWidth = canvas1.width; - const oldHeight = canvas1.height; - canvas1.style.height = `${height}`; - canvas1.style.width = `${width}`; - - const ratio1 = oldWidth / window.innerWidth; - const ratio2 = oldHeight / window.innerHeight; - const context = canvas1.getContext('2d'); - if (context) { - context.scale(ratio1, ratio2); - } + scaleCanvas(canvas1); const parent = canvas1.parentElement; if (parent) { @@ -666,16 +560,28 @@ export class AudioBox extends ViewBoxAnnotatableComponent AudioBox.RangeScript; - labelScript = () => AudioBox.LabelScript; - // for indicating the first marker that is rendered - reset = () => this._first = true; - render() { const interactive = this.active() ? "-interactive" : ""; - this.reset(); + this._first = true; // for indicating the first marker that is rendered this.path && this._buckets.length !== 100 ? this.peaks : null; // render waveform if audio is done recording + const markerDoc = (mark: Doc, script: undefined | (() => ScriptField)) => { + return + } return
{!this.path ?
@@ -708,81 +614,44 @@ export class AudioBox extends ViewBoxAnnotatableComponent -
+
{this.waveform}
- {DocListCast(this.dataDoc[this.annotationKey]).map((m, i) => { - let rect; + {DocListCast(this.dataDoc[this.annotationKey]).map((m, i) => (!m.isLabel) ? (this.layoutDoc.hideMarkers) ? (null) : - rect = -
{ this.playFrom(NumCast(m.audioStart), NumCast(m.audioEnd)); e.stopPropagation(); }} >
this.onPointerDown(e, m, true)}>
- + {markerDoc(m, this.rangeScript)}
this.onPointerDown(e, m, false)}>
: (this.layoutDoc.hideLabels) ? (null) : - rect = -
- -
; - return rect; - })} +
+ {markerDoc(m, this.labelScript)} +
+ )} {DocListCast(this.dataDoc.links).map((l, i) => { - let la1 = l.anchor1 as Doc; let la2 = l.anchor2 as Doc; let linkTime = NumCast(l.anchor2_timecode); @@ -797,7 +666,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent e.stopPropagation()}> +
e.stopPropagation()}>
Doc.linkFollowHighlight(la1)} - onPointerDown={e => { if (e.button === 0 && !e.ctrlKey) { const wasPaused = this.audioState === "paused"; this.playFrom(linkTime); e.stopPropagation(); e.preventDefault(); } }} /> + onPointerDown={e => { if (e.button === 0 && !e.ctrlKey) { this.playFrom(linkTime); e.stopPropagation(); e.preventDefault(); } }} />
; })} {this._visible ? this.container : null} -
{ e.stopPropagation(); e.preventDefault(); }} style={{ left: `${NumCast(this.layoutDoc.currentTimecode) / NumCast(this.dataDoc.duration, 1) * 100}%`, pointerEvents: "none" }} /> +
{ e.stopPropagation(); e.preventDefault(); }} style={{ left: `${NumCast(this.layoutDoc.currentTimecode) / this.audioDuration * 100}%`, pointerEvents: "none" }} /> {this.audio}
{formatTime(Math.round(NumCast(this.layoutDoc.currentTimecode)))}
- {formatTime(Math.round(NumCast(this.dataDoc.duration)))} + {formatTime(Math.round(this.audioDuration))}
-- cgit v1.2.3-70-g09d2