| 
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -3,8 +3,6 @@ import moment from 'moment';
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				import {  SessionIconButton, SessionIconSize, SessionIconType } from '../icon';
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				import { SessionButton, SessionButtonType, SessionButtonColor } from '../SessionButton';
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				import { Timestamp } from '../../conversation/Timestamp';
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				interface Props {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  onStoppedRecording: any;
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -30,6 +28,18 @@ interface State {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    processor: any;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  canvasParams: {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    width: number;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    height: number;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    barRadius: number;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    barWidth: number;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    barPadding: number;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    barColorInit: string;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    barColorPlay: string;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    maxBarHeight: number;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    minBarHeight: number;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  volumeArray?: Array<number>;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  startTimestamp: number;
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -46,25 +56,32 @@ export class SessionRecording extends React.Component<Props, State> {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  constructor(props: any) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    super(props);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // Mouse interaction
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    this.handleHoverActions = this.handleHoverActions.bind(this);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    this.handleUnhoverActions = this.handleUnhoverActions.bind(this);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // Component actions
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    this.playAudio = this.playAudio.bind(this);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    this.pauseAudio = this.pauseAudio.bind(this);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    this.stopRecording = this.stopRecording.bind(this);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // Voice message actions
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    this.onSendVoiceMessage = this.onSendVoiceMessage.bind(this);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    this.onDeleteVoiceMessage = this.onDeleteVoiceMessage.bind(this);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // Stream monitors
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    this.timerUpdate = this.timerUpdate.bind(this);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    this.onRecordingStream = this.onRecordingStream.bind(this);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    this.stopRecordingStream = this.stopRecordingStream.bind(this);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // Refs
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    this.visualisationRef = React.createRef();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    this.visualisationCanvas = React.createRef();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    this.playbackCanvas = React.createRef();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // Listeners
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    this.onKeyDown = this.onKeyDown.bind(this);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    this.updateCanvasDimensions = this.updateCanvasDimensions.bind(this);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const now = Number(moment().format('x')) / 1000;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const updateTimerInterval = setInterval(this.timerUpdate, 1000);
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -84,6 +101,19 @@ export class SessionRecording extends React.Component<Props, State> {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      startTimestamp: now,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      nowTimestamp: now,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      updateTimerInterval,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      // Initial width of 0 until bounds are located
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      canvasParams: {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        width: 0,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        height: 35,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        barRadius: 15,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        barWidth: 4,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        barPadding: 3,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        barColorInit: '#AFAFAF',
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        barColorPlay: '#FFFFFF',
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        maxBarHeight: 30,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        minBarHeight: 3,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    };
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -93,8 +123,14 @@ export class SessionRecording extends React.Component<Props, State> {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    this.initiateRecordingStream();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  public componentDidMount() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    window.addEventListener('resize', this.updateCanvasDimensions);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    this.updateCanvasDimensions();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  public componentWillUnmount(){
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    clearInterval(this.state.updateTimerInterval);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    window.removeEventListener('resize', this.updateCanvasDimensions);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  public componentDidUpdate() {
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -286,20 +322,51 @@ export class SessionRecording extends React.Component<Props, State> {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      };
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      return audioElement;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const audioElement = this.state.audioElement || generateAudioElement();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (!audioElement) return;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // Start playing recording
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // FIXME VINCE: Prevent looping of playing
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    audioElement.play();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // Draw canvas
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    this.onPlaybackStream();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // Draw sweeping timeline
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const drawSweepingTimeline = () => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const { isPaused } = this.state;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        width,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        height,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        barColorPlay,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      } = this.state.canvasParams;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const canvas = this.playbackCanvas.current;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      if ( !canvas || isPaused ) return;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      // Once audioElement is fully buffered, we get the true duration
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      let audioDuration = this.state.recordDuration
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      if (audioElement.duration !== Infinity) audioDuration = audioElement.duration;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const progress = width * (audioElement.currentTime / audioDuration);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      console.log(`[details] Current Time:`, audioElement.currentTime);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      console.log(`[details] Record Duration:`, audioDuration);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      console.log(`[details] Audio element duration`, audioElement.duration);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const canvasContext = canvas.getContext(`2d`);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      if (!canvasContext) return;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      canvasContext.beginPath();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      canvasContext.fillStyle = barColorPlay
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      canvasContext.globalCompositeOperation = 'source-atop';
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      canvasContext.fillRect(0, 0, progress, height);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      // Pause audio when it reaches the end of the blob
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      if (audioElement.duration && audioElement.currentTime === audioElement.duration){
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        this.pauseAudio();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      requestAnimationFrame(drawSweepingTimeline);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    this.setState({
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      audioElement,
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -308,14 +375,20 @@ export class SessionRecording extends React.Component<Props, State> {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      isPlaying: true,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // If end of audio reached, reset the position of the sweeping timeline
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (audioElement.duration && audioElement.currentTime === audioElement.duration){
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      this.initPlaybackView();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    audioElement.play();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    requestAnimationFrame(drawSweepingTimeline);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  private pauseAudio() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    this.state.audioElement?.pause();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // STOP ANIMAION FRAME
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // cancelAnimationFrame(playbackAnimationID);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    this.setState({
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      isPlaying: false,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      isPaused: true,
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -380,7 +453,10 @@ export class SessionRecording extends React.Component<Props, State> {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // Start recording the stream
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const media = new window.MediaRecorder(stream);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    media.ondataavailable = (mediaBlob: any) => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      this.setState({mediaBlob});
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      this.setState({mediaBlob}, () => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // Generate PCM waveform for playback
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        this.initPlaybackView();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    };
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    media.start();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -399,34 +475,34 @@ export class SessionRecording extends React.Component<Props, State> {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const streamParams = {stream, media, input, processor};
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      this.setState({streamParams});
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        width,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        height,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        barWidth,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        barPadding,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        barColorInit,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        maxBarHeight,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        minBarHeight 
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      } = this.state.canvasParams;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      // Array of volumes by frequency (not in Hz, arbitrary unit)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const freqTypedArray = new Uint8Array(analyser.frequencyBinCount);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      analyser.getByteFrequencyData(freqTypedArray);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const freqArray = Array.from(freqTypedArray);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const VISUALISATION_WIDTH = this.visualisationRef.current?.clientWidth;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const maxVisualisationHeight = 30;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const minVisualisationHeight = 3;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      // CANVAS CONTEXT
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const drawRecordingCanvas = () => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        const canvas = this.visualisationCanvas.current;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        const CANVAS_HEIGHT = 35;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        const CANVAS_WIDTH = VISUALISATION_WIDTH || 600;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        const barPadding = 3;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        const barWidth = 4;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        const numBars = CANVAS_WIDTH / (barPadding + barWidth);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        const numBars = width / (barPadding + barWidth);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        let volumeArray = freqArray.map(n => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          const maxVal = Math.max(...freqArray);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          const initialHeight = maxVisualisationHeight * (n / maxVal);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          const freqBarHeight = initialHeight > minVisualisationHeight
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          const initialHeight = maxBarHeight * (n / maxVal);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          const freqBarHeight = initialHeight > minBarHeight
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            ? initialHeight
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            : minVisualisationHeight;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            : minBarHeight;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          return freqBarHeight;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        });
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -440,25 +516,22 @@ export class SessionRecording extends React.Component<Props, State> {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // Chop off values which exceed the bounds of the container
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        volumeArray = volumeArray.slice(0, numBars);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        canvas && (canvas.height = CANVAS_HEIGHT);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        canvas && (canvas.width = CANVAS_WIDTH);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        canvas && (canvas.height = height);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        canvas && (canvas.width = width);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        const canvasContext = canvas && (canvas.getContext(`2d`));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        for (var i = 0; i < volumeArray.length; i++) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          const barHeight = Math.ceil(volumeArray[i]);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          const offset_x = Math.ceil(i * (barWidth + barPadding));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          const offset_y = Math.ceil((CANVAS_HEIGHT / 2 ) - (barHeight / 2 ));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          const radius = 15;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          const offset_y = Math.ceil((height / 2 ) - (barHeight / 2 ));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          // FIXME VINCE - Globalise JS references to colors
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          canvasContext && (canvasContext.fillStyle = '#AFAFAF');
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          canvasContext && (canvasContext.fillStyle = barColorInit);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          canvasContext && this.drawRoundedRect(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            canvasContext,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            offset_x,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            offset_y,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            barWidth,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            barHeight,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            radius,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      }
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -475,7 +548,7 @@ export class SessionRecording extends React.Component<Props, State> {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return error;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  private compactPCM(array: Float32Array<number>, numGroups: number) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  private compactPCM(array: Float32Array, numGroups: number) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // Takes an array of arbitary size and compresses it down into a smaller
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // array, by grouping elements into bundles of groupSize and taking their
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // average.
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -503,15 +576,23 @@ export class SessionRecording extends React.Component<Props, State> {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return compacted;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  private async onPlaybackStream() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const VISUALISATION_WIDTH = this.playbackCanvas.current?.clientWidth;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const CANVAS_WIDTH = VISUALISATION_WIDTH || 600;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const barPadding = 3;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const barWidth = 4;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const numBars = CANVAS_WIDTH / (barPadding + barWidth);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  private async initPlaybackView() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      width,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      height,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      barWidth,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      barPadding,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      barColorInit,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      maxBarHeight,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      minBarHeight 
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    } = this.state.canvasParams;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const numBars = width / (barPadding + barWidth);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    //FIXME VINCE
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // update numbars with animation so that changing width of screen 
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // accomodates
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const startPlayingTimestamp = moment().format('x') / 1000;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // Then scan through audio file getting average volume per bar
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // to display amplitude over time as a static image
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const blob = this.state.mediaBlob.data;
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -519,10 +600,9 @@ export class SessionRecording extends React.Component<Props, State> {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const arrayBuffer = await new Response(blob).arrayBuffer();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const audioContext = new window.AudioContext();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    let audioDuration = 0;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    audioContext.decodeAudioData(arrayBuffer, (buffer: AudioBuffer) => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      audioDuration = buffer.duration;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      this.setState({recordDuration: buffer.duration});
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      // Get audio amplitude with PCM Data in Float32
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      // Grab single channel only to save compuation
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const channelData = buffer.getChannelData(0);
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -531,76 +611,57 @@ export class SessionRecording extends React.Component<Props, State> {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const pcmDataArrayNormalised = pcmDataArray.map(v => Math.abs(v));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      // Prepare values for drawing to canvas
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const maxVisualisationHeight = 30;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const minVisualisationHeight = 3;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const maxAmplitude = Math.max(...pcmDataArrayNormalised);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const barSizeArray = pcmDataArrayNormalised.map(amplitude => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        let barSize = maxVisualisationHeight * (amplitude / maxAmplitude);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        let barSize = maxBarHeight * (amplitude / maxAmplitude);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // Prevent values that are too small
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (barSize < minVisualisationHeight){
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          barSize = minVisualisationHeight;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (barSize < minBarHeight){
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          barSize = minBarHeight;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return barSize;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      // CANVAS CONTEXT
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      let playbackAnimationID;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const drawPlaybackCanvas = () => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        const canvas = this.playbackCanvas.current;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        const CANVAS_HEIGHT = 35;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        canvas.height = CANVAS_HEIGHT;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        canvas.width = CANVAS_WIDTH;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (!canvas) return;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        canvas.height = height;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        canvas.width = width;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        const canvasContext = canvas.getContext(`2d`);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (!canvasContext) return;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        for (let i = 0; i < barSizeArray.length; i++){
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          const barHeight = Math.ceil(barSizeArray[i]);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          const offset_x = Math.ceil(i * (barWidth + barPadding));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          const offset_y = Math.ceil((CANVAS_HEIGHT / 2 ) - (barHeight / 2 ));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          const radius = 15;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          const offset_y = Math.ceil((height / 2 ) - (barHeight / 2 ));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          // FIXME VINCE - Globalise JS references to colors
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          canvasContext.fillStyle = '#AFAFAF';
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          canvasContext.fillStyle = barColorInit;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          this.drawRoundedRect(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            canvasContext,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            offset_x,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            offset_y,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            barWidth,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            barHeight,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            radius,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // Draw sweeping timeline
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        const now = moment().format('x') / 1000;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        const progress = CANVAS_WIDTH * ((now - startPlayingTimestamp) / audioDuration);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        canvasContext.beginPath();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        canvasContext.fillStyle = '#FFFFFF';
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        canvasContext.globalCompositeOperation = 'source-atop';
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        canvasContext.fillRect(0, 0, progress, CANVAS_HEIGHT);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        playbackAnimationID = requestAnimationFrame(drawPlaybackCanvas);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      requestAnimationFrame(drawPlaybackCanvas);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      drawPlaybackCanvas();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // FIXME VINCE SET ASUIDO DURATION TO STATE
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    await this.setState({recordDuration: audioDuration});
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // this.state.isPlaying && requestAnimationFrame(drawPlaybackCanvas);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  private drawRoundedRect (ctx: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, r: number) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  private drawRoundedRect (ctx: CanvasRenderingContext2D, x: number, y: number, h: number) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    let r = this.state.canvasParams.barRadius;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const w = this.state.canvasParams.barWidth;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (w < 2 * r) r = w / 2;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (h < 2 * r) r = h / 2;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    ctx.beginPath();
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -613,6 +674,15 @@ export class SessionRecording extends React.Component<Props, State> {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    ctx.fill();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  private updateCanvasDimensions(){
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const canvas = this.visualisationCanvas.current || this.playbackCanvas.current;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const width = canvas?.clientWidth || 0;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    this.setState({
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      canvasParams: {...this.state.canvasParams, width}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  private onKeyDown(event: any) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (event.key === 'Escape') {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      // FIXME VINCE: Add SessionConfirm
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
						
					 | 
				
			
			 | 
			 | 
			
				
 
 |