From 26c973ecb47de86d94c39d71ad00ece38a85d623 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 4 Mar 2020 18:20:46 -0500
Subject: changed  text sampling to marking every second to get audio to
 synchronize better

---
 src/client/views/nodes/AudioBox.tsx | 66 ++++++++++++++++++++++++++-----------
 1 file changed, 47 insertions(+), 19 deletions(-)

(limited to 'src/client/views/nodes/AudioBox.tsx')

diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index c4c6365e3..e2002a596 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -17,6 +17,8 @@ import { ContextMenu } from "../ContextMenu";
 import { Id } from "../../../new_fields/FieldSymbols";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { DocumentView } from "./DocumentView";
+import { Docs } from "../../documents/Documents";
+import { ComputedField } from "../../../new_fields/ScriptField";
 
 interface Window {
     MediaRecorder: MediaRecorder;
@@ -45,12 +47,15 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
     _recorder: any;
     _recordStart = 0;
 
+    public static START = 0;
     @observable private static _scrubTime = 0;
-    @observable private _audioState: "unrecorded" | "recording" | "recorded" = "unrecorded";
-    public static SetScrubTime = action((timeInMillisFrom1970: number) => AudioBox._scrubTime = timeInMillisFrom1970);
+    @computed get audioState(): undefined | "recording" | "paused" | "playing" { return this.dataDoc.audioState as (undefined | "recording" | "paused" | "playing"); }
+    set audioState(value) { this.dataDoc.audioState = value; }
+    public static SetScrubTime = (timeInMillisFrom1970: number) => { runInAction(() => AudioBox._scrubTime = 0); runInAction(() => AudioBox._scrubTime = timeInMillisFrom1970); };
     public static ActiveRecordings: Doc[] = [];
 
     @computed get recordingStart() { return Cast(this.dataDoc[this.props.fieldKey + "-recordingStart"], DateField)?.date.getTime(); }
+    async slideTemplate() { return (await Cast((await Cast(Doc.UserDoc().slidesBtn, Doc) as Doc).dragFactory, Doc) as Doc); }
 
     componentWillUnmount() {
         this._reactionDisposer?.();
@@ -58,7 +63,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
         this._scrubbingDisposer?.();
     }
     componentDidMount() {
-        runInAction(() => this._audioState = this.path ? "recorded" : "unrecorded");
+        runInAction(() => this.audioState = this.path ? "paused" : undefined);
         this._linkPlayDisposer = reaction(() => this.layoutDoc.scrollToLinkID,
             scrollLinkId => {
                 if (scrollLinkId) {
@@ -73,13 +78,14 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
             selected => {
                 const sel = selected.length ? selected[0].props.Document : undefined;
                 this.Document.playOnSelect && this.recordingStart && sel && !Doc.AreProtosEqual(sel, this.props.Document) && this.playFromTime(DateCast(sel.creationDate).date.getTime());
+                this.Document.playOnSelect && this.recordingStart && !sel && this.pause();
             });
         this._scrubbingDisposer = reaction(() => AudioBox._scrubTime, (time) => this.Document.playOnSelect && this.playFromTime(AudioBox._scrubTime));
     }
 
     timecodeChanged = () => {
         const htmlEle = this._ele;
-        if (this._audioState === "recorded" && htmlEle) {
+        if (this.audioState !== "recording" && htmlEle) {
             htmlEle.duration && htmlEle.duration !== Infinity && runInAction(() => this.dataDoc.duration = htmlEle.duration);
             DocListCast(this.dataDoc.links).map(l => {
                 let la1 = l.anchor1 as Doc;
@@ -98,7 +104,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
 
     pause = action(() => {
         this._ele!.pause();
-        this.props.Document._audioState = "paused";
+        this.audioState = "paused";
     });
 
     playFromTime = (absoluteTime: number) => {
@@ -107,11 +113,15 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
     playFrom = (seekTimeInSeconds: number) => {
         if (this._ele && AudioBox.Enabled) {
             if (seekTimeInSeconds < 0) {
-                this.pause();
+                if (seekTimeInSeconds > -1) {
+                    setTimeout(() => this.playFrom(0), -seekTimeInSeconds * 1000);
+                } else {
+                    this.pause();
+                }
             } else if (seekTimeInSeconds <= this._ele.duration) {
                 this._ele.currentTime = seekTimeInSeconds;
                 this._ele.play();
-                runInAction(() => this.props.Document._audioState = "playing");
+                runInAction(() => this.audioState = "playing");
             } else {
                 this.pause();
             }
@@ -120,7 +130,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
 
 
     updateRecordTime = () => {
-        if (this._audioState === "recording") {
+        if (this.audioState === "recording") {
             setTimeout(this.updateRecordTime, 30);
             this.Document.currentTimecode = (new Date().getTime() - this._recordStart) / 1000;
         }
@@ -135,6 +145,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
             gumStream = stream;
             self._recorder = new MediaRecorder(stream);
             self.dataDoc[self.props.fieldKey + "-recordingStart"] = new DateField(new Date());
+            AudioBox.START = new DateField(new Date()).date.getTime();
             AudioBox.ActiveRecordings.push(self.props.Document);
             self._recorder.ondataavailable = async function (e: any) {
                 const formData = new FormData();
@@ -151,8 +162,9 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
                     self.props.Document[self.props.fieldKey] = new AudioField(url);
                 });
             };
-            runInAction(() => self._audioState = "recording");
             self._recordStart = new Date().getTime();
+            console.log("RECORD START = " + self._recordStart);
+            runInAction(() => self.audioState = "recording");
             setTimeout(self.updateRecordTime, 0);
             self._recorder.start();
             setTimeout(() => {
@@ -172,7 +184,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
     stopRecording = action(() => {
         this._recorder.stop();
         this.dataDoc.duration = (new Date().getTime() - this._recordStart) / 1000;
-        this._audioState = "recorded";
+        this.audioState = "paused";
         const ind = AudioBox.ActiveRecordings.indexOf(this.props.Document);
         ind !== -1 && (AudioBox.ActiveRecordings.splice(ind, 1));
     });
@@ -189,8 +201,19 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
         e.stopPropagation();
     }
     onStop = (e: any) => {
-        this.pause();
-        this._ele!.currentTime = 0;
+        this.Document.playOnSelect = !this.Document.playOnSelect;
+        e.stopPropagation();
+    }
+    onFile = (e: any) => {
+        const newDoc = Docs.Create.TextDocument("", {
+            title: "", _chromeStatus: "disabled",
+            x: NumCast(this.props.Document.x), y: NumCast(this.props.Document.y) + NumCast(this.props.Document._height) + 10,
+            _width: NumCast(this.props.Document._width), _height: 3 * NumCast(this.props.Document._height)
+        });
+        Doc.GetProto(newDoc).recordingSource = this.dataDoc;
+        Doc.GetProto(newDoc).recordingStart = 0;
+        Doc.GetProto(newDoc).audioState = ComputedField.MakeFunction("this.recordingSource.audioState");
+        this.props.addDocument?.(newDoc);
         e.stopPropagation();
     }
 
@@ -218,21 +241,26 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
         const interactive = this.active() ? "-interactive" : "";
         return <div className={`audiobox-container`} onContextMenu={this.specificContextMenu}
             onClick={!this.path ? this.recordClick : undefined}>
-            <div className="audiobox-handle" style={{ backgroundColor: this._audioState === "recording" ? "green" : undefined }} />
             {!this.path ?
-                <button className={`audiobox-record${interactive}`} style={{ backgroundColor: this._audioState === "recording" ? "red" : "black" }}>
-                    {this._audioState === "recording" ? "STOP" : "RECORD"}
-                </button> :
+                <div style={{ display: "flex", width: "100%", alignItems: "center" }}>
+                    <div className="audiobox-playhead" style={{ alignItems: "center", display: "inherit", background: "dimgray" }} onClick={this.onFile}>
+                        <FontAwesomeIcon style={{ width: "30px", background: this.Document.playOnSelect ? "yellow" : "dimGray" }} icon="file-alt" size={this.props.PanelHeight() < 36 ? "1x" : "2x"} />
+                    </div>
+                    <button className={`audiobox-record${interactive}`} style={{ position: "relative", backgroundColor: this.audioState === "recording" ? "red" : "black" }}>
+                        {this.audioState === "recording" ? "STOP" : "RECORD"}
+                    </button>
+                </div> :
                 <div className="audiobox-controls">
                     <div className="audiobox-player" onClick={this.onPlay}>
-                        <div className="audiobox-playhead"> <FontAwesomeIcon style={{ width: "100%" }} icon={this.props.Document._audioState === "paused" ? "play" : "pause"} size={this.props.PanelHeight() < 36 ? "1x" : "2x"} /></div>
-                        <div className="audiobox-playhead" onClick={this.onStop}><FontAwesomeIcon style={{ width: "100%" }} icon="stop" size={this.props.PanelHeight() < 36 ? "1x" : "2x"} /></div>
+                        <div className="audiobox-playhead"> <FontAwesomeIcon style={{ width: "100%" }} icon={this.audioState === "paused" ? "play" : "pause"} size={this.props.PanelHeight() < 36 ? "1x" : "2x"} /></div>
+                        <div className="audiobox-playhead" onClick={this.onStop}><FontAwesomeIcon style={{ width: "100%", background: this.Document.playOnSelect ? "yellow" : "dimGray" }} icon="hand-point-left" size={this.props.PanelHeight() < 36 ? "1x" : "2x"} /></div>
                         <div className="audiobox-timeline" onClick={e => e.stopPropagation()}
                             onPointerDown={e => {
                                 if (e.button === 0 && !e.ctrlKey) {
                                     const rect = (e.target as any).getBoundingClientRect();
+                                    const wasPaused = this.audioState === "paused";
                                     this._ele!.currentTime = this.Document.currentTimecode = (e.clientX - rect.x) / rect.width * NumCast(this.dataDoc.duration);
-                                    this.pause();
+                                    wasPaused && this.pause();
                                     e.stopPropagation();
                                 }
                             }} >
-- 
cgit v1.2.3-70-g09d2

