From 7e0a60dc39ce2ad3fc8f90197f5ade7fe5e5b112 Mon Sep 17 00:00:00 2001 From: mehekj Date: Sat, 11 Sep 2021 11:47:22 -0400 Subject: fixed record buttons and trimmed marker width appearance --- src/client/views/nodes/AudioBox.scss | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/client/views/nodes/AudioBox.scss') diff --git a/src/client/views/nodes/AudioBox.scss b/src/client/views/nodes/AudioBox.scss index ac2b19fd6..0b720ef16 100644 --- a/src/client/views/nodes/AudioBox.scss +++ b/src/client/views/nodes/AudioBox.scss @@ -14,7 +14,6 @@ display: flex; width: 100%; align-items: center; - height: 100%; .audiobox-dictation { position: relative; @@ -69,25 +68,26 @@ .time { position: relative; - height: 100%; width: 100%; font-size: $large-header; text-align: center; - top: 5; } - .buttons { + .recording-buttons { position: relative; margin-top: auto; margin-bottom: auto; - width: 25px; - width: 25px; - padding: 5px; color: $dark-gray; &:hover { color: $black; } } + + .time, .recording-buttons { + display: flex; + align-items: center; + padding: 5px; + } } .audiobox-controls { -- cgit v1.2.3-70-g09d2 From 23db5275a9477d5894e76a2ad6f1795fb0f99b71 Mon Sep 17 00:00:00 2001 From: mehekj Date: Sun, 12 Sep 2021 12:31:19 -0400 Subject: changed record button --- src/client/views/nodes/AudioBox.scss | 5 +++++ src/client/views/nodes/AudioBox.tsx | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'src/client/views/nodes/AudioBox.scss') diff --git a/src/client/views/nodes/AudioBox.scss b/src/client/views/nodes/AudioBox.scss index 0b720ef16..93ad7ebcd 100644 --- a/src/client/views/nodes/AudioBox.scss +++ b/src/client/views/nodes/AudioBox.scss @@ -54,6 +54,11 @@ .audiobox-record { pointer-events: none; + color: white; + display: flex; + align-items: center; + justify-content: center; + font-size: $large-header; } .recording { diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index bcc8e042a..4b1ab9d30 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -643,12 +643,12 @@ export class AudioBox extends ViewBoxAnnotatableComponent< ) : ( - + )} ) : ( -- cgit v1.2.3-70-g09d2 From 64e265d9cba009469081fdf4ba3272c78a3a76a8 Mon Sep 17 00:00:00 2001 From: mehekj Date: Mon, 20 Sep 2021 22:25:03 -0400 Subject: partially added dragging timeline markers --- .../collections/CollectionStackedTimeline.tsx | 11 +- src/client/views/nodes/AudioBox.scss | 61 ++++------ src/client/views/nodes/AudioBox.tsx | 129 +++++++++++---------- src/client/views/nodes/VideoBox.tsx | 10 ++ 4 files changed, 108 insertions(+), 103 deletions(-) (limited to 'src/client/views/nodes/AudioBox.scss') diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 8608700aa..0ef6f852a 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -354,6 +354,13 @@ export class CollectionStackedTimeline extends CollectionSubView< // determine x coordinate of drop and assign it to the documents being dragged --- see internalDocDrop of collectionFreeFormView.tsx for how it's done when dropping onto a 2D freeform view + const x = docDragData.offset[0]; + const timelineContentWidth = this.props.PanelWidth(); + for (let i = 0; i < docDragData.droppedDocuments.length; i++) { + const d = docDragData.droppedDocuments[i]; + d._timecodeToShow = x / timelineContentWidth * this.props.trimDuration + NumCast(d._timecodeToShow); + d._timecodeToHide = x / timelineContentWidth * this.props.trimDuration + NumCast(d._timecodeToHide); + } return true; } @@ -573,14 +580,14 @@ export class CollectionStackedTimeline extends CollectionSubView< const left = this.props.trimming ? (start / this.duration) * timelineContentWidth : Math.max((start - this.trimStart) / this.props.trimDuration * timelineContentWidth, 0); - const top = (d.level / maxLevel) * this.timelineContentHeight(); + const top = (d.level / maxLevel) * this.timelineContentHeight() + 15; const timespan = end - start; let width = (timespan / this.props.trimDuration) * timelineContentWidth; width = (!this.props.trimming && left == 0) ? width - ((this.trimStart - start) / this.props.trimDuration * timelineContentWidth) : width; width = (!this.props.trimming && this.trimEnd < end) ? width - ((end - this.trimEnd) / this.props.trimDuration * timelineContentWidth) : width; - const height = this.timelineContentHeight() / maxLevel + const height = (this.timelineContentHeight()) / maxLevel; return this.props.Document.hideAnchors ? null : (
- (this.layoutDoc.dontAutoPlayFollowedLinks = - !this.layoutDoc.dontAutoPlayFollowedLinks), + (this.layoutDoc.dontAutoPlayFollowedLinks = + !this.layoutDoc.dontAutoPlayFollowedLinks), icon: "expand-arrows-alt", }); funcs.push({ @@ -637,77 +638,77 @@ export class AudioBox extends ViewBoxAnnotatableComponent<
) : ( -
- +
+ RECORD -
- )} +
+ )} ) : ( +
+
-
-
- {" "} - -
-
- -
-
- {this.renderTimeline} -
- {this.audio} -
- {this._trimming ? - formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode))) - : formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode) - NumCast(this._trimStart)))} -
-
- {this._trimming || !this._trimEnd ? - formatTime(Math.round(NumCast(this.duration))) - : formatTime(Math.round(NumCast(this.trimDuration)))} -
+ {" "} + +
+
+ +
+
+ {this.renderTimeline} +
+ {this.audio} +
+ {this._trimming ? + formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode))) + : formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode) - NumCast(this._trimStart)))} +
+
+ {this._trimming || !this._trimEnd ? + formatTime(Math.round(NumCast(this.duration))) + : formatTime(Math.round(NumCast(this.trimDuration)))}
- )} +
+ )}
); } diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 90de3227f..7dd82ad13 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -60,9 +60,17 @@ export class VideoBox extends ViewBoxAnnotatableComponent { this.player && (this.layoutDoc._currentTimecode = this.player.currentTime); + this.layoutDoc.clipEnd = this.layoutDoc.clipEnd ? Math.min(this.duration, NumCast(this.layoutDoc.clipEnd)) : this.duration; + this._trimEnd = this._trimEnd ? Math.min(this.duration, this._trimEnd) : this.duration; try { this._youtubePlayer && (this.layoutDoc._currentTimecode = this._youtubePlayer.getCurrentTime?.()); } catch (e) { -- cgit v1.2.3-70-g09d2 From 9dd7cb1cffdc436d1ac47ab1c6f67c2c88be2718 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 25 Sep 2021 22:01:10 -0400 Subject: more cleanup/refactoring. --- src/client/views/nodes/AudioBox.scss | 14 +- src/client/views/nodes/AudioBox.tsx | 267 ++++++++++++------------------ src/client/views/nodes/VideoBox.tsx | 16 -- src/client/views/nodes/trails/PresBox.tsx | 19 +-- 4 files changed, 117 insertions(+), 199 deletions(-) (limited to 'src/client/views/nodes/AudioBox.scss') diff --git a/src/client/views/nodes/AudioBox.scss b/src/client/views/nodes/AudioBox.scss index 6adda4730..b33c7f506 100644 --- a/src/client/views/nodes/AudioBox.scss +++ b/src/client/views/nodes/AudioBox.scss @@ -41,11 +41,11 @@ } .audiobox-control-interactive { - pointer-events: all; + pointer-events: all; } .audiobox-record-interactive, - .audiobox-record { + .audiobox-record { pointer-events: all; cursor: pointer; width: 100%; @@ -58,11 +58,12 @@ gap: 10px; color: white; font-weight: bold; - } + background-color: $dark-gray; + } - .audiobox-record { + .audiobox-record { pointer-events: none; - } + } .recording { margin-top: auto; @@ -241,6 +242,9 @@ .audiobox-timeline { position: absolute; + top: 0; + background: white; + height: 100%; width: 100%; z-index: 1000; overflow: hidden; diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index e66003f88..6e6558030 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -23,7 +23,6 @@ import { Colors } from "../global/globalEnums"; import "./AudioBox.scss"; import { FieldView, FieldViewProps } from "./FieldView"; import { LinkDocPreview } from "./LinkDocPreview"; -import e = require("connect-flash"); declare class MediaRecorder { constructor(e: any); // whatever MediaRecorder has @@ -44,6 +43,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent !LinkDocPreview.LinkInfo && this.props.renderDepth !== -1 - ? NumCast(this.Document._triggerAudio, null) - : undefined, - (start) => - start !== undefined && setTimeout(() => { - this.playFrom(start); - setTimeout(() => { - this.Document._currentTimecode = start; - this.Document._triggerAudio = undefined; - }, 10); - }), // wait for mainCont and try again to play - { fireImmediately: true } - ); - - this._disposers.audioStop = reaction( - () => this.props.renderDepth !== -1 && !LinkDocPreview.LinkInfo - ? Cast(this.Document._audioStop, "number", null) - : undefined, - (audioStop) => - audioStop !== undefined && setTimeout(() => { - this.Pause(); - setTimeout(() => (this.Document._audioStop = undefined), 10); - }), // wait for mainCont and try again to play - { fireImmediately: true } - ); } // for updating the timecode @@ -305,12 +277,12 @@ export class AudioBox extends ViewBoxAnnotatableComponent { const newDoc = CurrentUserUtils.GetNewTextDoc( "", - NumCast(this.props.Document.x), - NumCast(this.props.Document.y) + - NumCast(this.props.Document._height) + + NumCast(this.rootDoc.x), + NumCast(this.rootDoc.y) + + NumCast(this.layoutDoc._height) + 10, - NumCast(this.props.Document._width), - 2 * NumCast(this.props.Document._height) + NumCast(this.layoutDoc._width), + 2 * NumCast(this.layoutDoc._height) ); Doc.GetProto(newDoc).recordingSource = this.dataDoc; Doc.GetProto(newDoc).recordingStart = ComputedField.MakeFunction( @@ -332,8 +304,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent { - if (r && this.timeline) { - this._dropDisposer?.(); - this._dropDisposer = DragManager.MakeDropTarget(r, - (e, de) => { - const [xp, yp] = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y); - de.complete.docDragData && this.timeline!.internalDocDrop(e, de, de.complete.docDragData, xp); - } - , this.layoutDoc, undefined); - } - }} - className="audiobox-container" - onContextMenu={this.specificContextMenu} - onClick={ - !this.path && !this._recorder ? this.recordAudioAnnotation : undefined - } - style={{ - pointerEvents: - this.props.layerProvider?.(this.layoutDoc) === false - ? "none" - : undefined, - }} - > - {!this.path ? ( -
-
- -
- {this.mediaState === media_state.Recording || this.mediaState === media_state.Playing ? ( -
e.stopPropagation()}> -
- -
-
- -
-
- {formatTime( - Math.round(NumCast(this.layoutDoc._currentTimecode)) - )} -
-
- ) : ( -
- - RECORD -
- )} + @computed get recordingControls() { + return
+
+ +
+ {[media_state.Recording, media_state.Playing].includes(this.mediaState) ? +
e.stopPropagation()}> +
+
- ) : ( -
-
-
-
- {" "} - -
-
- -
-
- {this.renderTimeline} -
- {this.audio} -
- {this.timeline && formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode) - NumCast(this.timeline.clipStart)))} -
-
- {this.timeline && formatTime(Math.round(NumCast(this.timeline?.clipDuration)))} -
-
-
- )} +
+ +
+
+ {formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode)))} +
+
+ : +
+ + RECORD +
} +
; + } + + @computed get playbackControls() { + return
+
+
+
+ {" "} + +
+
+ +
+
+ {this.renderTimeline} +
+ {this.audio} +
+ {this.timeline && formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode) - NumCast(this.timeline.clipStart)))} +
+
+ {this.timeline && formatTime(Math.round(NumCast(this.timeline?.clipDuration)))} +
- ); +
; + } + + setupTimelineDrop = (r: HTMLDivElement | null) => { + if (r && this.timeline) { + this._dropDisposer?.(); + this._dropDisposer = DragManager.MakeDropTarget(r, + (e, de) => { + const [xp, yp] = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y); + de.complete.docDragData && this.timeline!.internalDocDrop(e, de, de.complete.docDragData, xp); + }, + this.layoutDoc, undefined); + } + } + + render() { + return
+ {!this.path ? this.recordingControls : this.playbackControls} +
; } } \ No newline at end of file diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index b00fb75a3..3435c2a24 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -222,22 +222,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent !LinkDocPreview.LinkInfo && this.props.renderDepth !== -1 ? NumCast(this.Document._triggerVideo, null) : undefined, - time => time !== undefined && setTimeout(() => { - this.player && this.Play(); - setTimeout(() => this.Document._triggerVideo = undefined, 10); - }, this.player ? 0 : 250), // wait for mainCont and try again to play - { fireImmediately: true } - ); - this._disposers.triggerStop = reaction( - () => this.props.renderDepth !== -1 && !LinkDocPreview.LinkInfo ? NumCast(this.Document._triggerVideoStop, null) : undefined, - stop => stop !== undefined && setTimeout(() => { - this.player && this.Pause(); - setTimeout(() => this.Document._triggerVideoStop = undefined, 10); - }, this.player ? 0 : 250), // wait for mainCont and try again to play - { fireImmediately: true } - ); if (this.youtubeVideoId) { const youtubeaspect = 400 / 315; const nativeWidth = Doc.NativeWidth(this.layoutDoc); diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 8e61a224c..f4892d77c 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -200,26 +200,13 @@ export class PresBox extends ViewBoxBaseComponent const targMedia = DocumentManager.Instance.getDocumentView(targetDoc); targMedia?.ComponentView?.playFrom?.(NumCast(activeItem.presStartTime), NumCast(activeItem.presStartTime) + duration); } - // if (targetDoc.type === DocumentType.AUDIO) { - // if (this._mediaTimer && this._mediaTimer[1] === targetDoc) clearTimeout(this._mediaTimer[0]); - // targetDoc._triggerAudio = NumCast(activeItem.presStartTime); - // this._mediaTimer = [setTimeout(() => targetDoc._audioStop = true, duration * 1000), targetDoc]; - // } else if (targetDoc.type === DocumentType.VID) { - // targetDoc._triggerVideoStop = true; - // setTimeout(() => targetDoc._currentTimecode = NumCast(activeItem.presStartTime), 10); - // setTimeout(() => targetDoc._triggerVideo = true, 20); - // this._mediaTimer = [setTimeout(() => targetDoc._triggerVideoStop = true, (duration * 1000) + 20), targetDoc]; - // } } stopTempMedia = (targetDocField: FieldResult) => { const targetDoc = Cast(targetDocField, Doc, null); - if (targetDoc?.type === DocumentType.AUDIO) { - if (this._mediaTimer && this._mediaTimer[1] === targetDoc) clearTimeout(this._mediaTimer[0]); - targetDoc._audioStop = true; - } else if (targetDoc?.type === DocumentType.VID) { - if (this._mediaTimer && this._mediaTimer[1] === targetDoc) clearTimeout(this._mediaTimer[0]); - targetDoc._triggerVideoStop = true; + if ([DocumentType.VID, DocumentType.AUDIO].includes(targetDoc.type as any)) { + const targMedia = DocumentManager.Instance.getDocumentView(targetDoc); + targMedia?.ComponentView?.Pause?.(); } } -- cgit v1.2.3-70-g09d2 From 6cec290f98103827727905874c5a9c5ced0bcca8 Mon Sep 17 00:00:00 2001 From: mehekj Date: Thu, 14 Oct 2021 16:21:03 -0400 Subject: pre-redesign changes (zoom but no scroll) --- src/client/views/AudioWaveform.tsx | 26 +-- .../collections/CollectionStackedTimeline.scss | 167 ++++++++++--------- .../collections/CollectionStackedTimeline.tsx | 46 +++--- src/client/views/nodes/AudioBox.scss | 178 +++++++++++++-------- src/client/views/nodes/AudioBox.tsx | 14 ++ 5 files changed, 256 insertions(+), 175 deletions(-) (limited to 'src/client/views/nodes/AudioBox.scss') diff --git a/src/client/views/AudioWaveform.tsx b/src/client/views/AudioWaveform.tsx index 270b3869c..7b9b1aa81 100644 --- a/src/client/views/AudioWaveform.tsx +++ b/src/client/views/AudioWaveform.tsx @@ -18,6 +18,7 @@ export interface AudioWaveformProps { layoutDoc: Doc; clipStart: number; clipEnd: number; + zoomFactor: number; PanelHeight: () => number; } @@ -28,28 +29,30 @@ export class AudioWaveform extends React.Component { @computed get waveHeight() { return Math.max(50, this.props.PanelHeight()); } @computed get clipStart() { return this.props.clipStart; } @computed get clipEnd() { return this.props.clipEnd; } - @computed get audioBuckets() { return Cast(this.props.layoutDoc[this.audioBucketField(this.clipStart, this.clipEnd)], listSpec("number"), []); } + @computed get zoomFactor() { return this.props.zoomFactor; } + @computed get audioBuckets() { return Cast(this.props.layoutDoc[this.audioBucketField(this.clipStart, this.clipEnd, this.zoomFactor)], listSpec("number"), []); } - audioBucketField = (start: number, end: number) => "audioBuckets/" + start.toFixed(2).replace(".", "_") + "/" + end.toFixed(2).replace(".", "_"); + audioBucketField = (start: number, end: number, zoomFactor: number) => "audioBuckets/" + "/" + start.toFixed(2).replace(".", "_") + "/" + end.toFixed(2).replace(".", "_") + "/" + (zoomFactor * 10); componentWillUnmount() { this._disposer?.(); } componentDidMount() { - this._disposer = reaction(() => ({ clipStart: this.clipStart, clipEnd: this.clipEnd, fieldKey: this.audioBucketField(this.clipStart, this.clipEnd) }), - ({ clipStart, clipEnd, fieldKey }) => { + console.log("new waveform"); + this._disposer = reaction(() => ({ clipStart: this.clipStart, clipEnd: this.clipEnd, fieldKey: this.audioBucketField(this.clipStart, this.clipEnd, this.zoomFactor), zoomFactor: this.props.zoomFactor }), + ({ clipStart, clipEnd, fieldKey, zoomFactor }) => { if (!this.props.layoutDoc[fieldKey]) { // setting these values here serves as a "lock" to prevent multiple attempts to create the waveform at nerly the same time. - const waveform = Cast(this.props.layoutDoc[this.audioBucketField(0, this.props.rawDuration)], listSpec("number")); + const waveform = Cast(this.props.layoutDoc[this.audioBucketField(0, this.props.rawDuration, 1)], listSpec("number")); this.props.layoutDoc[fieldKey] = waveform && new List(waveform.slice(clipStart / this.props.rawDuration * waveform.length, clipEnd / this.props.rawDuration * waveform.length)); - setTimeout(() => this.createWaveformBuckets(fieldKey, clipStart, clipEnd)); + setTimeout(() => this.createWaveformBuckets(fieldKey, clipStart, clipEnd, zoomFactor)); } }, { fireImmediately: true }); } // decodes the audio file into peaks for generating the waveform - createWaveformBuckets = async (fieldKey: string, clipStart: number, clipEnd: number) => { + createWaveformBuckets = async (fieldKey: string, clipStart: number, clipEnd: number, zoomFactor: number) => { axios({ url: this.props.mediaPath, responseType: "arraybuffer" }).then( (response) => { const context = new window.AudioContext(); @@ -60,12 +63,15 @@ export class AudioWaveform extends React.Component { const startInd = clipStart / this.props.rawDuration; const endInd = clipEnd / this.props.rawDuration; const decodedAudioData = rawDecodedAudioData.slice(Math.floor(startInd * rawDecodedAudioData.length), Math.floor(endInd * rawDecodedAudioData.length)); + const numBuckets = Math.floor(AudioWaveform.NUMBER_OF_BUCKETS * zoomFactor); + + console.log(numBuckets); const bucketDataSize = Math.floor( - decodedAudioData.length / AudioWaveform.NUMBER_OF_BUCKETS + decodedAudioData.length / numBuckets ); const brange = Array.from(Array(bucketDataSize)); - const bucketList = numberRange(AudioWaveform.NUMBER_OF_BUCKETS).map( + const bucketList = numberRange(numBuckets).map( (i: number) => brange.reduce( (p, x, j) => @@ -87,7 +93,7 @@ export class AudioWaveform extends React.Component { NumCast(anchor._timecodeToShow, NumCast(anchor[this.props.startTag])); @@ -216,7 +220,9 @@ export class CollectionStackedTimeline extends CollectionSubView< @action onPointerDownTimeline = (e: React.PointerEvent): void => { const rect = this._timeline?.getBoundingClientRect(); + const scrollLeft = this._timeline?.scrollLeft; const clientX = e.clientX; + const diff = rect ? clientX - rect?.x : null; const shiftKey = e.shiftKey; if (rect && this.props.isContentActive()) { const wasPlaying = this.props.playing(); @@ -529,7 +535,7 @@ export class CollectionStackedTimeline extends CollectionSubView< } @computed get renderAudioWaveform() { return !this.props.mediaPath ? null : ( -
+
); @@ -556,7 +563,7 @@ export class CollectionStackedTimeline extends CollectionSubView< } render() { - const timelineContentWidth = this.props.PanelWidth(); + const timelineContentWidth = this.props.PanelWidth() * this.zoomFactor; const overlaps: { anchorStartTime: number; anchorEndTime: number; @@ -573,8 +580,9 @@ export class CollectionStackedTimeline extends CollectionSubView< ref={(timeline: HTMLDivElement | null) => (this._timeline = timeline)} onClick={(e) => this.isContentActive() && StopEvent(e)} onPointerDown={(e) => this.isContentActive() && this.onPointerDownTimeline(e)} - style={{ width: `${this._zoomFactor * 100}%` }} + onPointerEnter={(e) => { console.log("scroll"); e.preventDefault(); e.stopPropagation(); }} > + {drawAnchors.map((d) => { const start = this.anchorStart(d.anchor); const end = this.anchorEnd( @@ -868,19 +876,19 @@ class StackedTimelineAnchor extends React.Component {inner.view} {!inner.anchor.view || !SelectionManager.IsSelected(inner.anchor.view) ? null : ( - <> -
this.onAnchorDown(e, this.props.mark, true)} - /> -
this.onAnchorDown(e, this.props.mark, false)} - /> - - )} + <> +
this.onAnchorDown(e, this.props.mark, true)} + /> +
this.onAnchorDown(e, this.props.mark, false)} + /> + + )} ); } diff --git a/src/client/views/nodes/AudioBox.scss b/src/client/views/nodes/AudioBox.scss index b33c7f506..a2fdd38e5 100644 --- a/src/client/views/nodes/AudioBox.scss +++ b/src/client/views/nodes/AudioBox.scss @@ -1,51 +1,50 @@ @import "../global/globalCssVariables.scss"; - .audiobox-container, .audiobox-container-interactive { - width: 100%; - height: 100%; - position: inherit; - display: flex; - position: relative; - cursor: default; - - .audiobox-buttons { - display: flex; width: 100%; - align-items: center; + height: 100%; + position: inherit; + display: flex; + position: relative; + cursor: default; - .audiobox-dictation { - position: relative; - width: 30px; - height: 100%; - align-items: center; - display: inherit; - background: $medium-gray; - left: 0px; - color: $dark-gray; - &:hover { - color: $black; - cursor: pointer; - } + .audiobox-buttons { + display: flex; + width: 100%; + align-items: center; + + .audiobox-dictation { + position: relative; + width: 30px; + height: 100%; + align-items: center; + display: inherit; + background: $medium-gray; + left: 0px; + color: $dark-gray; + &:hover { + color: $black; + cursor: pointer; + } + } } - } - .audiobox-control, - .audiobox-control-interactive { - top: 0; - max-height: 32px; - width: 100%; - display: inline-block; - pointer-events: none; - } + .audiobox-control, + .audiobox-control-interactive { + top: 0; + max-height: 32px; + width: 100%; + display: inline-block; + pointer-events: none; + } - .audiobox-control-interactive { - pointer-events: all; - } + .audiobox-control-interactive { + pointer-events: all; + } - .audiobox-record-interactive, - .audiobox-record { + .audiobox-record-interactive, + .audiobox-record { pointer-events: all; cursor: pointer; width: 100%; @@ -59,45 +58,46 @@ color: white; font-weight: bold; background-color: $dark-gray; - } + } - .audiobox-record { + .audiobox-record { pointer-events: none; - } + } - .recording { - margin-top: auto; - margin-bottom: auto; - width: 100%; - height: 100%; - position: relative; - padding-right: 5px; - display: flex; - background-color: $medium-blue; + .recording { + margin-top: auto; + margin-bottom: auto; + width: 100%; + height: 100%; + position: relative; + padding-right: 5px; + display: flex; + background-color: $medium-blue; - .time { - position: relative; - width: 100%; - font-size: $large-header; - text-align: center; - } + .time { + position: relative; + width: 100%; + font-size: $large-header; + text-align: center; + } - .recording-buttons { - position: relative; - margin-top: auto; - margin-bottom: auto; - color: $dark-gray; - &:hover { - color: $black; - } - } + .recording-buttons { + position: relative; + margin-top: auto; + margin-bottom: auto; + color: $dark-gray; + &:hover { + color: $black; + } + } - .time, .recording-buttons { - display: flex; - align-items: center; - padding: 5px; + .time, + .recording-buttons { + display: flex; + align-items: center; + padding: 5px; + } } - } .audiobox-buttons { display: flex; width: 100%; @@ -267,6 +267,44 @@ right: 2px; } + .toolbar-slider { + position: absolute; + top: 75px; + left: 70px; + } + + input[type="range"] { + width: calc(100% - 100px); + height: 16px; + -webkit-appearance: none; + background: none; + } + + input[type="range"]:focus { + outline: none; + } + + input[type="range"]::-webkit-slider-runnable-track { + width: 100%; + height: 5px; + cursor: pointer; + box-shadow: 0; + background: #dfdfdf; + border-radius: 3px; + } + + input[type="range"]::-webkit-slider-thumb { + box-shadow: 0; + border: 0; + height: 7px; + width: 7px; + border-radius: 10px; + background: #4476f7; + cursor: pointer; + -webkit-appearance: none; + margin: -1px; + } + .audiobox-zoom { bottom: 0; left: 30px; diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 2eb34d27a..f2001adcd 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -45,6 +45,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent { + this.timeline?.setZoom(zoom); + } + setupTimelineDrop = (r: HTMLDivElement | null) => { if (r && this.timeline) { this._dropDisposer?.(); @@ -437,6 +442,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent
+
+
{this.timeline && formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode) - NumCast(this.timeline.clipStart)))}
+ + {/* { e.stopPropagation(); }} + onChange={(e: React.ChangeEvent) => { this.zoom(e.target.value); }} + /> */} +
{this.timeline && formatTime(Math.round(NumCast(this.timeline?.clipDuration)))}
-- cgit v1.2.3-70-g09d2 From 3d76222b8cd41423bcdd7a91e4aee2826f329d9f Mon Sep 17 00:00:00 2001 From: mehekj Date: Sat, 23 Oct 2021 21:36:30 -0400 Subject: audiobox UI update --- src/client/views/AudioWaveform.tsx | 3 - .../collections/CollectionStackedTimeline.scss | 3 +- .../collections/CollectionStackedTimeline.tsx | 10 +- src/client/views/nodes/AudioBox.scss | 349 ++++++--------------- src/client/views/nodes/AudioBox.tsx | 120 ++++--- src/client/views/nodes/ComparisonBox.tsx | 4 +- 6 files changed, 163 insertions(+), 326 deletions(-) (limited to 'src/client/views/nodes/AudioBox.scss') diff --git a/src/client/views/AudioWaveform.tsx b/src/client/views/AudioWaveform.tsx index 7b9b1aa81..ca1dd6f36 100644 --- a/src/client/views/AudioWaveform.tsx +++ b/src/client/views/AudioWaveform.tsx @@ -38,7 +38,6 @@ export class AudioWaveform extends React.Component { this._disposer?.(); } componentDidMount() { - console.log("new waveform"); this._disposer = reaction(() => ({ clipStart: this.clipStart, clipEnd: this.clipEnd, fieldKey: this.audioBucketField(this.clipStart, this.clipEnd, this.zoomFactor), zoomFactor: this.props.zoomFactor }), ({ clipStart, clipEnd, fieldKey, zoomFactor }) => { if (!this.props.layoutDoc[fieldKey]) { @@ -65,8 +64,6 @@ export class AudioWaveform extends React.Component { const decodedAudioData = rawDecodedAudioData.slice(Math.floor(startInd * rawDecodedAudioData.length), Math.floor(endInd * rawDecodedAudioData.length)); const numBuckets = Math.floor(AudioWaveform.NUMBER_OF_BUCKETS * zoomFactor); - console.log(numBuckets); - const bucketDataSize = Math.floor( decodedAudioData.length / numBuckets ); diff --git a/src/client/views/collections/CollectionStackedTimeline.scss b/src/client/views/collections/CollectionStackedTimeline.scss index 0ec5f9aef..34679e9e3 100644 --- a/src/client/views/collections/CollectionStackedTimeline.scss +++ b/src/client/views/collections/CollectionStackedTimeline.scss @@ -4,9 +4,8 @@ position: absolute; width: 100%; height: 100%; + background: $off-white; z-index: 1000; - top: 0px; - // overflow-x: scroll; ::-webkit-scrollbar { position: relative; diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 5c02611bb..c79d21418 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -153,7 +153,6 @@ export class CollectionStackedTimeline extends CollectionSubView< @action public setZoom(zoom: number) { this._zoomFactor = zoom; - // console.log(this._timeline?.scrollWidth); } anchorStart = (anchor: Doc) => NumCast(anchor._timecodeToShow, NumCast(anchor[this.props.startTag])); @@ -534,8 +533,9 @@ export class CollectionStackedTimeline extends CollectionSubView< ); } @computed get renderAudioWaveform() { + console.log(this.props.mediaPath) return !this.props.mediaPath ? null : ( -
+
(this._timeline = timeline)} onClick={(e) => this.isContentActive() && StopEvent(e)} onPointerDown={(e) => this.isContentActive() && this.onPointerDownTimeline(e)} - onPointerEnter={(e) => { console.log("scroll"); e.preventDefault(); e.stopPropagation(); }} + style={{ height: this.props.PanelHeight(), width: this.props.PanelWidth() }} > {drawAnchors.map((d) => { @@ -591,10 +591,10 @@ export class CollectionStackedTimeline extends CollectionSubView< ); if (end < this.clipStart || start > this.clipEnd) return (null); const left = Math.max((start - this.clipStart) / this.clipDuration * timelineContentWidth, 0); - const top = (d.level / maxLevel) * this.timelineContentHeight(); + const top = (d.level / maxLevel) * this.props.PanelHeight(); const timespan = Math.max(0, end - this.clipStart) - Math.max(0, start - this.clipStart); const width = (timespan / this.clipDuration) * timelineContentWidth; - const height = this.timelineContentHeight() / maxLevel; + const height = this.props.PanelHeight() / maxLevel; return this.props.Document.hideAnchors ? null : (
this.props.whenChildContentsActiveChanged(this._isAnyChildContentActive = isActive) timelineScreenToLocal = () => - this.props - .ScreenToLocalTransform() - .translate( - -AudioBox.playheadWidth, - (-(100 - AudioBox.heightPercent) / 200) * this.props.PanelHeight() - ) + this.props.ScreenToLocalTransform().translate(0, -AudioBox.bottomControlsHeight) setPlayheadTime = (time: number) => this._ele!.currentTime = this.layoutDoc._currentTimecode = time; playing = () => this.mediaState === media_state.Playing; isActiveChild = () => this._isAnyChildContentActive; - timelineWidth = () => this.props.PanelWidth() - AudioBox.playheadWidth; - timelineHeight = () => (this.props.PanelHeight() * (AudioBox.heightPercent / 100)) *// panelHeight * heightPercent is player height - (AudioBox.heightPercent / 100) // * heightPercent is timeline height (as per css inline) + timelineWidth = () => this.props.PanelWidth(); + timelineHeight = () => (this.props.PanelHeight() - (AudioBox.topControlsHeight + AudioBox.bottomControlsHeight)) @undoBatch finishTrim = () => { // hides trim controls and displays new clip @@ -365,6 +359,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent { + e.stopPropagation(); this.timeline && setupMoveUpEvents(this, e, returnFalse, returnFalse, action((e: PointerEvent, doubleTap?: boolean) => { if (doubleTap) { this.startTrim(TrimScope.All); @@ -392,90 +387,81 @@ export class AudioBox extends ViewBoxAnnotatableComponent + return
+ size="2x" + icon="file-alt" />
{[media_state.Recording, media_state.Playing].includes(this.mediaState) ? -
e.stopPropagation()}> -
+
e.stopPropagation()}> +
+ size="2x" + icon="stop" />
-
+
+ size="2x" + icon={this._paused ? "play" : "pause"} />
-
+
{formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode)))}
: -
+
RECORD
} -
; +
} @computed get playbackControls() { - return
-
-
-
- {" "} - -
+ return
+
+
+
+ +
-
- +
+ +
+
+ + { e.stopPropagation(); }} + onChange={(e: React.ChangeEvent) => { this.zoom(Number(e.target.value)); }} + /> +
+
-
+
+
{this.renderTimeline}
- {this.audio} -
- {this.timeline && formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode) - NumCast(this.timeline.clipStart)))} -
+
- {/* { e.stopPropagation(); }} - onChange={(e: React.ChangeEvent) => { this.zoom(e.target.value); }} - /> */} + {this.audio} -
+
+
+ {this.timeline && formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode) - NumCast(this.timeline.clipStart)))} +
+
{this.timeline && formatTime(Math.round(NumCast(this.timeline?.clipDuration)))}
-
; + + +
} @computed get renderTimeline() { diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index d80fb44cf..d47e8340c 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -89,8 +89,8 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent { var whichDoc = Cast(this.dataDoc[which], Doc, null); - if (whichDoc?.type === DocumentType.MARKER) whichDoc = Cast(whichDoc.annotationOn, Doc, null); - const targetDoc = Cast(whichDoc.annotationOn, Doc, null) ?? whichDoc; + // if (whichDoc?.type === DocumentType.MARKER) whichDoc = Cast(whichDoc.annotationOn, Doc, null); + const targetDoc = Cast(whichDoc?.annotationOn, Doc, null) ?? whichDoc; return whichDoc ? <> { -- cgit v1.2.3-70-g09d2 From 35157eb87d6d3e23f2392d70c62df9519b682745 Mon Sep 17 00:00:00 2001 From: mehekj Date: Sat, 23 Oct 2021 22:12:15 -0400 Subject: implemented basic audio timeline zoom with scroll --- .../collections/CollectionStackedTimeline.scss | 9 +- .../collections/CollectionStackedTimeline.tsx | 194 +++++++++++---------- src/client/views/nodes/AudioBox.scss | 5 + 3 files changed, 110 insertions(+), 98 deletions(-) (limited to 'src/client/views/nodes/AudioBox.scss') diff --git a/src/client/views/collections/CollectionStackedTimeline.scss b/src/client/views/collections/CollectionStackedTimeline.scss index 34679e9e3..843c5dcb5 100644 --- a/src/client/views/collections/CollectionStackedTimeline.scss +++ b/src/client/views/collections/CollectionStackedTimeline.scss @@ -1,11 +1,16 @@ @import "../global/globalCssVariables.scss"; +.timeline-container { + height: calc(100% - 50px); + overflow-x: scroll; + border: none; +} + .collectionStackedTimeline { position: absolute; - width: 100%; - height: 100%; background: $off-white; z-index: 1000; + height: 100%; ::-webkit-scrollbar { position: relative; diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index c79d21418..82a6b2a66 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -575,108 +575,110 @@ export class CollectionStackedTimeline extends CollectionSubView< })); const maxLevel = overlaps.reduce((m, o) => Math.max(m, o.level), 0) + 2; return (
-
(this._timeline = timeline)} - onClick={(e) => this.isContentActive() && StopEvent(e)} - onPointerDown={(e) => this.isContentActive() && this.onPointerDownTimeline(e)} - style={{ height: this.props.PanelHeight(), width: this.props.PanelWidth() }} - > - - {drawAnchors.map((d) => { - const start = this.anchorStart(d.anchor); - const end = this.anchorEnd( - d.anchor, - start + (10 / timelineContentWidth) * this.clipDuration - ); - if (end < this.clipStart || start > this.clipEnd) return (null); - const left = Math.max((start - this.clipStart) / this.clipDuration * timelineContentWidth, 0); - const top = (d.level / maxLevel) * this.props.PanelHeight(); - const timespan = Math.max(0, end - this.clipStart) - Math.max(0, start - this.clipStart); - const width = (timespan / this.clipDuration) * timelineContentWidth; - const height = this.props.PanelHeight() / maxLevel; - return this.props.Document.hideAnchors ? null : ( -
{ - this.props.playFrom(start, this.anchorEnd(d.anchor)); - e.stopPropagation(); - }} - > - -
- ); - })} - {!this.IsTrimming && this.selectionContainer} - {this.renderAudioWaveform} - {this.renderDictation} - +
- - {this.IsTrimming !== TrimScope.None && ( - <> -
- -
+ className="collectionStackedTimeline" + ref={(timeline: HTMLDivElement | null) => (this._timeline = timeline)} + onClick={(e) => this.isContentActive() && StopEvent(e)} + onPointerDown={(e) => this.isContentActive() && this.onPointerDownTimeline(e)} + style={{ width: timelineContentWidth }}> + + {drawAnchors.map((d) => { + const start = this.anchorStart(d.anchor); + const end = this.anchorEnd( + d.anchor, + start + (10 / timelineContentWidth) * this.clipDuration + ); + if (end < this.clipStart || start > this.clipEnd) return (null); + const left = Math.max((start - this.clipStart) / this.clipDuration * timelineContentWidth, 0); + const top = (d.level / maxLevel) * this.props.PanelHeight(); + const timespan = Math.max(0, end - this.clipStart) - Math.max(0, start - this.clipStart); + const width = (timespan / this.clipDuration) * timelineContentWidth; + const height = this.props.PanelHeight() / maxLevel; + return this.props.Document.hideAnchors ? null : (
+ className={"collectionStackedTimeline-marker-timeline"} + key={d.anchor[Id]} + style={{ + left, + top, + width: `${width}px`, + height: `${height}px`, + }} + onClick={(e) => { + this.props.playFrom(start, this.anchorEnd(d.anchor)); + e.stopPropagation(); + }} + > + +
+ ); + })} + {!this.IsTrimming && this.selectionContainer} + {this.renderAudioWaveform} + {this.renderDictation} + +
+ + {this.IsTrimming !== TrimScope.None && ( + <>
-
-
- - )} +
+
+
+
+ +
+ + )} +
-
); +
); } } diff --git a/src/client/views/nodes/AudioBox.scss b/src/client/views/nodes/AudioBox.scss index 391507796..d466c6c3b 100644 --- a/src/client/views/nodes/AudioBox.scss +++ b/src/client/views/nodes/AudioBox.scss @@ -177,6 +177,11 @@ width: 100%; background: $white; } + + .audiobox-timeline > div { + width: 100%; + height: 100%; + } } .audiobox-timecodes { -- cgit v1.2.3-70-g09d2 From 7f08f0fec32a28e1dc19f00021f99352f55a045c Mon Sep 17 00:00:00 2001 From: mehekj Date: Thu, 28 Oct 2021 15:12:34 -0400 Subject: fixed marker document decorations on scroll in zoomed timeline --- src/client/views/AudioWaveform.tsx | 2 +- src/client/views/MainView.tsx | 2 +- .../collections/CollectionStackedTimeline.scss | 32 +++--- .../collections/CollectionStackedTimeline.tsx | 16 ++- src/client/views/nodes/AudioBox.scss | 123 ++++++++++++--------- src/client/views/nodes/AudioBox.tsx | 43 ++++++- 6 files changed, 138 insertions(+), 80 deletions(-) (limited to 'src/client/views/nodes/AudioBox.scss') diff --git a/src/client/views/AudioWaveform.tsx b/src/client/views/AudioWaveform.tsx index ca1dd6f36..58384792e 100644 --- a/src/client/views/AudioWaveform.tsx +++ b/src/client/views/AudioWaveform.tsx @@ -90,7 +90,7 @@ export class AudioWaveform extends React.Component { { const rect = this._timeline?.getBoundingClientRect(); - const scrollLeft = this._timeline?.scrollLeft; const clientX = e.clientX; const diff = rect ? clientX - rect?.x : null; const shiftKey = e.shiftKey; @@ -343,6 +344,11 @@ export class CollectionStackedTimeline extends CollectionSubView< ); } + @action + setScroll = (e: React.MouseEvent) => { + this._scroll = e.currentTarget.scrollLeft; + } + @action internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData, xp: number) { if (!de.embedKey && this.props.layerProvider?.(this.props.Document) !== false && this.props.Document._isGroup) return false; @@ -533,7 +539,6 @@ export class CollectionStackedTimeline extends CollectionSubView< ); } @computed get renderAudioWaveform() { - console.log(this.props.mediaPath) return !this.props.mediaPath ? null : (
Math.max(m, o.level), 0) + 2; return (
+ style={{ width: this.props.PanelWidth() }} + onScroll={this.setScroll}>
(this._timeline = timeline)} @@ -616,7 +622,7 @@ export class CollectionStackedTimeline extends CollectionSubView< mark={d.anchor} rangeClickScript={this.rangeClickScript} rangePlayScript={this.rangePlayScript} - left={left} + left={left - this._scroll} top={top} width={width} height={height} diff --git a/src/client/views/nodes/AudioBox.scss b/src/client/views/nodes/AudioBox.scss index d466c6c3b..b3df7e79f 100644 --- a/src/client/views/nodes/AudioBox.scss +++ b/src/client/views/nodes/AudioBox.scss @@ -92,6 +92,62 @@ height: 100%; color: $white; + .audiobox-button { + margin: 2.5px; + cursor: pointer; + width: 25px; + height: 25px; + border-radius: 50%; + background: $dark-gray; + display: flex; + align-items: center; + justify-content: center; + + svg { + width: 15px; + } + + &:hover { + background: $black; + } + } + + svg { + width: 10px; + } + + input[type="range"] { + width: 70px; + -webkit-appearance: none; + background: none; + margin: 5px; + } + + input[type="range"]:focus { + outline: none; + } + + input[type="range"]::-webkit-slider-runnable-track { + width: 100%; + height: 6px; + cursor: pointer; + box-shadow: 0; + background: $light-gray; + border-radius: 3px; + } + + input[type="range"]::-webkit-slider-thumb { + box-shadow: 0; + border: 0; + height: 10px; + width: 10px; + border-radius: 10px; + background: $medium-blue; + cursor: pointer; + -webkit-appearance: none; + margin: -2px; + } + .audiobox-controls { display: flex; flex-direction: row; @@ -104,66 +160,20 @@ display: flex; flex-direction: row; width: 100px; - - .audiobox-button { - margin: 2.5px; - cursor: pointer; - width: 25px; - height: 25px; - border-radius: 50%; - background: $dark-gray; - display: flex; - align-items: center; - justify-content: center; - - svg { - width: 15px; - } - - &:hover { - background: $black; - } - } } .controls-right { display: flex; flex-direction: row; - svg { - width: 10px; - } - - input[type="range"] { - width: 70px; - -webkit-appearance: none; - background: none; - margin: 5px; - } - - input[type="range"]:focus { - outline: none; - } - - input[type="range"]::-webkit-slider-runnable-track { - width: 100%; - height: 6px; - cursor: pointer; - box-shadow: 0; - background: $light-gray; - border-radius: 3px; - } + .audiobox-button { + width: 15px; + height: 15px; + margin: 0; - input[type="range"]::-webkit-slider-thumb { - box-shadow: 0; - border: 0; - height: 10px; - width: 10px; - border-radius: 10px; - background: $medium-blue; - cursor: pointer; - -webkit-appearance: none; - margin: -2px; + svg { + width: 10px; + } } } } @@ -171,6 +181,7 @@ .audiobox-playback { width: 100%; height: calc(100% - 50px); + background: $white; .audiobox-timeline { height: 100%; @@ -193,5 +204,11 @@ height: 20px; padding: 3px; font-size: $small-text; + + .bottom-controls-middle { + display: flex; + flex-direction: row; + align-items: center; + } } } diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 48e324971..3a3eb78e1 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -21,6 +21,7 @@ import { ContextMenuProps } from "../ContextMenuItem"; import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent"; import "./AudioBox.scss"; import { FieldView, FieldViewProps } from "./FieldView"; +import { timeStamp } from "console"; declare class MediaRecorder { constructor(e: any); // whatever MediaRecorder has @@ -60,6 +61,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent { - if (fullPlay) this.setPlayheadTime(this.timeline!.trimStart); this.Pause(); + if (fullPlay) this.setPlayheadTime(this.timeline!.trimStart); }, (end - start) * 1000); } else { @@ -260,6 +263,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent { this._ele?.pause(); this.mediaState = media_state.Paused; + // clearTimeout(this._play); // stops clip from jumping back to beginning } // creates a text document for dictation @@ -374,6 +378,23 @@ export class AudioBox extends ViewBoxAnnotatableComponent { + if (this._ele) { + this._volume = volume; + this._ele.volume = volume; + } + } + + @action + toggleMute = (e: React.PointerEvent) => { + e.stopPropagation(); + if (this._ele) { + this._muted = !this._muted; + this._ele.muted = this._muted; + } + } + setupTimelineDrop = (r: HTMLDivElement | null) => { if (r && this.timeline) { this._dropDisposer?.(); @@ -434,11 +455,15 @@ export class AudioBox extends ViewBoxAnnotatableComponent
- - + +
+ { e.stopPropagation(); }} - onChange={(e: React.ChangeEvent) => { this.zoom(Number(e.target.value)); }} + onChange={(e: React.ChangeEvent) => { this.setVolume(Number(e.target.value)) }} />
@@ -455,6 +480,14 @@ export class AudioBox extends ViewBoxAnnotatableComponent {this.timeline && formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode) - NumCast(this.timeline.clipStart)))}
+
+ + { e.stopPropagation(); }} + onChange={(e: React.ChangeEvent) => { this.zoom(Number(e.target.value)); }} + /> +
{this.timeline && formatTime(Math.round(NumCast(this.timeline?.clipDuration)))}
-- cgit v1.2.3-70-g09d2 From 5886662d38619be96d1cb3ed6396dd133c254841 Mon Sep 17 00:00:00 2001 From: mehekj Date: Thu, 28 Oct 2021 15:23:36 -0400 Subject: fixed input sliders --- package-lock.json | 6 +++--- src/client/views/nodes/AudioBox.scss | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/client/views/nodes/AudioBox.scss') diff --git a/package-lock.json b/package-lock.json index d03f8af6b..128d6fba8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7709,14 +7709,14 @@ "resolved": "https://registry.npmjs.org/image-size-stream/-/image-size-stream-1.1.0.tgz", "integrity": "sha1-Ivou2mbG31AQh0bacUkmSy0l+Gs=", "requires": { - "image-size": "image-size@git+https://github.com/netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1", + "image-size": "github:netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1", "readable-stream": "^1.0.33", "tryit": "^1.0.1" }, "dependencies": { "image-size": { - "version": "git+ssh://git@github.com/netroy/image-size.git#da2c863807a3e9602617bdd357b0de3ab4a064c1", - "from": "image-size@git+https://github.com/netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1" + "version": "github:netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1", + "from": "github:netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1" }, "isarray": { "version": "0.0.1", diff --git a/src/client/views/nodes/AudioBox.scss b/src/client/views/nodes/AudioBox.scss index b3df7e79f..458d607a5 100644 --- a/src/client/views/nodes/AudioBox.scss +++ b/src/client/views/nodes/AudioBox.scss @@ -145,7 +145,7 @@ background: $medium-blue; cursor: pointer; -webkit-appearance: none; - margin: -2px; + margin-top: -2px; } .audiobox-controls { -- cgit v1.2.3-70-g09d2 From 1f94d6b33f81b1b2e6140f58f4de749eb4e74478 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 8 Nov 2021 11:07:05 -0500 Subject: fixed vertical height of timeline for video. fixed initial display of timeline by not assigning clipEnd in DidMount --- .../collections/CollectionStackedTimeline.scss | 2 +- .../collections/CollectionStackedTimeline.tsx | 33 ++++++++++++---------- src/client/views/collections/TabDocView.tsx | 4 +-- src/client/views/nodes/AudioBox.scss | 5 ++-- src/client/views/nodes/AudioBox.tsx | 7 +++-- src/client/views/nodes/VideoBox.tsx | 9 +++--- 6 files changed, 33 insertions(+), 27 deletions(-) (limited to 'src/client/views/nodes/AudioBox.scss') diff --git a/src/client/views/collections/CollectionStackedTimeline.scss b/src/client/views/collections/CollectionStackedTimeline.scss index fce105a44..36aa36978 100644 --- a/src/client/views/collections/CollectionStackedTimeline.scss +++ b/src/client/views/collections/CollectionStackedTimeline.scss @@ -1,7 +1,7 @@ @import "../global/globalCssVariables.scss"; .timeline-container { - height: calc(100% - 50px); + height: 100%; overflow-x: auto; overflow-y: hidden; border: none; diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index ced8a68e8..f5c3676e8 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -127,8 +127,11 @@ export class CollectionStackedTimeline extends CollectionSubView< } componentDidMount() { - this.layoutDoc.clipStart = 0; - this.layoutDoc.clipEnd = this.props.rawDuration; + // bcz: setting these shouldn't be necessary since they are the default values of this.clipStart and this.clipEnd. + // also, setting anything on the Document in DidMount or the constructor is not good form since it means that + // someone who has viewing but not edit permissions would not be able to do the assignment. + // this.layoutDoc.clipStart = 0; + // this.layoutDoc.clipEnd = this.props.rawDuration; document.addEventListener("keydown", this.keyEvents, true); } @@ -915,19 +918,19 @@ class StackedTimelineAnchor extends React.Component {inner.view} {!inner.anchor.view || !SelectionManager.IsSelected(inner.anchor.view) ? null : ( - <> -
this.onAnchorDown(e, this.props.mark, true)} - /> -
this.onAnchorDown(e, this.props.mark, false)} - /> - - )} + <> +
this.onAnchorDown(e, this.props.mark, true)} + /> +
this.onAnchorDown(e, this.props.mark, false)} + /> + + )} ); } diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 9f24d60d8..6e1d9b067 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -220,8 +220,8 @@ export class TabDocView extends React.Component { if (!pinProps?.audioRange && duration !== undefined) { pinDoc.mediaStart = "manual"; pinDoc.mediaStop = "manual"; - pinDoc.presStartTime = doc.clipStart; - pinDoc.presEndTime = doc.clipEnd; + pinDoc.presStartTime = NumCast(doc.clipStart); + pinDoc.presEndTime = NumCast(doc.clipEnd, duration); } //save position if (pinProps?.setPosition || pinDoc.isInkMask) { diff --git a/src/client/views/nodes/AudioBox.scss b/src/client/views/nodes/AudioBox.scss index 458d607a5..681a6b022 100644 --- a/src/client/views/nodes/AudioBox.scss +++ b/src/client/views/nodes/AudioBox.scss @@ -180,13 +180,14 @@ .audiobox-playback { width: 100%; - height: calc(100% - 50px); + height: 100%; background: $white; .audiobox-timeline { - height: 100%; + height: calc(100% - 50px); width: 100%; background: $white; + position: absolute; } .audiobox-timeline > div { diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index b51908e20..62958a80b 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -64,8 +64,9 @@ export class AudioBox extends ViewBoxAnnotatableComponent
- {this.timeline && formatTime(Math.round(NumCast(this.timeline?.clipDuration)))} + {this.timeline && formatTime(Math.round(this.timeline.clipDuration))}
@@ -537,7 +538,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent (this._ele?.duration && this._ele?.duration !== Infinity) && - (this.dataDoc[this.fieldKey + "-duration"] = this._ele?.duration) + (this.dataDoc[this.fieldKey + "-duration"] = this.rawDuration = this._ele.duration) )} > diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index c22dbab5c..ec6519abd 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -79,7 +79,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent { + videoLoad = action(() => { const aspect = this.player!.videoWidth / this.player!.videoHeight; Doc.SetNativeWidth(this.dataDoc, this.player!.videoWidth); Doc.SetNativeHeight(this.dataDoc, this.player!.videoHeight); this.layoutDoc._height = (this.layoutDoc._width || 0) / aspect; if (Number.isFinite(this.player!.duration)) { - this.dataDoc[this.fieldKey + "-duration"] = this.player!.duration; + this.rawDuration = this.player!.duration; } - } + }) @action updateTimecode = () => { -- cgit v1.2.3-70-g09d2 From 97a3a38c0fb07b52e70cba459d1b92273fb57b46 Mon Sep 17 00:00:00 2001 From: mehekj Date: Thu, 11 Nov 2021 16:33:47 -0500 Subject: collapses audio controls when node is resized, preliminary setup for currently playing audio "mini player" --- .../collectionLinear/CollectionLinearView.tsx | 9 ++++ src/client/views/nodes/AudioBox.scss | 3 +- src/client/views/nodes/AudioBox.tsx | 58 ++++++++++++++++------ 3 files changed, 52 insertions(+), 18 deletions(-) (limited to 'src/client/views/nodes/AudioBox.scss') diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx index 18a715edf..35b7fb5e9 100644 --- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx @@ -12,6 +12,7 @@ import { emptyFunction, returnEmptyDoclist, returnTrue, Utils } from '../../../. import { DragManager } from '../../../util/DragManager'; import { Transform } from '../../../util/Transform'; import { Colors, Shadows } from '../../global/globalEnums'; +import { AudioBox } from '../../nodes/AudioBox'; import { DocumentLinksButton } from '../../nodes/DocumentLinksButton'; import { DocumentView } from '../../nodes/DocumentView'; import { LinkDescriptionPopup } from '../../nodes/LinkDescriptionPopup'; @@ -219,6 +220,14 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { : null} + {/* TODO: add small player for single clip, dropdown sort of expandable menu for multiple clips + add onclick show audio similar to follow link behavior + add button to close out audio from currently playing */} + {AudioBox.CurrentlyPlaying && AudioBox.CurrentlyPlaying.length != 0 && StrCast(this.layoutDoc.title) === "docked buttons" ? + + Currently listening to: {AudioBox.CurrentlyPlaying.map((clip) => clip.dataDoc.title + ", ")} + + : null}
; } diff --git a/src/client/views/nodes/AudioBox.scss b/src/client/views/nodes/AudioBox.scss index 681a6b022..76043bfd4 100644 --- a/src/client/views/nodes/AudioBox.scss +++ b/src/client/views/nodes/AudioBox.scss @@ -86,7 +86,7 @@ overflow: hidden; display: flex; flex-direction: column; - align-items: space-between; + align-items: center; background: $dark-gray; width: 100%; height: 100%; @@ -159,7 +159,6 @@ .controls-left { display: flex; flex-direction: row; - width: 100px; } .controls-right { diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index dbcd8c8b8..acd025fbd 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -38,6 +38,8 @@ enum media_state { } @observer export class AudioBox extends ViewBoxAnnotatableComponent(AudioDocument) { + @observable public static CurrentlyPlaying: AudioBox[]; + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(AudioBox, fieldKey); } public static SetScrubTime = action((timeInMillisFrom1970: number) => { AudioBox._scrubTime = 0; @@ -70,6 +72,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent { + // IN PROGRESS: current attempt to make interface for keeping track of audio that is playing + if (!AudioBox.CurrentlyPlaying) { + AudioBox.CurrentlyPlaying = []; + } + if (AudioBox.CurrentlyPlaying.indexOf(this) == -1) { + AudioBox.CurrentlyPlaying.push(this); + } + fullPlay = endTime ? fullPlay : true; clearTimeout(this._play); // abort any previous clip ending if (Number.isNaN(this._ele?.duration)) { // audio element isn't loaded yet... wait 1/2 second and try again @@ -166,6 +177,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent { this.Pause(false); if (fullPlay) this.setPlayheadTime(this.timeline!.trimStart); + // removes from currently playing if playback has reached end of range marker + else this.removeCurrentlyPlaying(); }, (end - start) * 1000); } else { @@ -174,6 +187,12 @@ export class AudioBox extends ViewBoxAnnotatableComponent { + AudioBox.CurrentlyPlaying.splice(AudioBox.CurrentlyPlaying.indexOf(this), 1); + } + // update the recording time updateRecordTime = () => { if (this.mediaState === media_state.Recording) { @@ -447,7 +466,11 @@ export class AudioBox extends ViewBoxAnnotatableComponent + return
-
- -
+ {!this.miniPlayer && +
+ +
}
- { e.stopPropagation(); }} onChange={(e: React.ChangeEvent) => { this.setVolume(Number(e.target.value)) }} @@ -476,7 +500,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent
-
+
{this.renderTimeline}
@@ -488,14 +512,16 @@ export class AudioBox extends ViewBoxAnnotatableComponent {this.timeline && formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode) - NumCast(this.timeline.clipStart)))}
-
- - { e.stopPropagation(); }} - onChange={(e: React.ChangeEvent) => { this.zoom(Number(e.target.value)); }} - /> -
+ {!this.miniPlayer && +
+ + { e.stopPropagation(); }} + onChange={(e: React.ChangeEvent) => { this.zoom(Number(e.target.value)); }} + /> +
} +
{this.timeline && formatTime(Math.round(this.timeline.clipDuration))}
-- cgit v1.2.3-70-g09d2 From 2a237ca29c11585ddf51be59be4a48c46c6d4b29 Mon Sep 17 00:00:00 2001 From: mehekj Date: Thu, 11 Nov 2021 16:57:49 -0500 Subject: prevent clips from becoming infinitely small --- src/client/views/collections/CollectionStackedTimeline.tsx | 2 +- src/client/views/nodes/AudioBox.scss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/client/views/nodes/AudioBox.scss') diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 78883b577..c5fa126a3 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -94,7 +94,7 @@ export class CollectionStackedTimeline extends CollectionSubView< @observable _scroll: number = 0; - get minTrimLength() { return this._timeline?.getBoundingClientRect() ? 0.05 * this.clipDuration : 0; } + get minTrimLength() { return Math.max(this._timeline?.getBoundingClientRect() ? 0.05 * this.clipDuration : 0, 0.5) } @computed get trimStart() { return this.IsTrimming !== TrimScope.None ? this._trimStart : this.clipStart; } @computed get trimDuration() { return this.trimEnd - this.trimStart; } @computed get trimEnd() { return this.IsTrimming !== TrimScope.None ? this._trimEnd : this.clipEnd; } diff --git a/src/client/views/nodes/AudioBox.scss b/src/client/views/nodes/AudioBox.scss index 76043bfd4..d40537776 100644 --- a/src/client/views/nodes/AudioBox.scss +++ b/src/client/views/nodes/AudioBox.scss @@ -117,7 +117,7 @@ } input[type="range"] { - width: 70px; + width: 50px; -webkit-appearance: none; background: none; margin: 5px; -- cgit v1.2.3-70-g09d2