Last active
May 30, 2022 12:53
-
-
Save trkhanh/e1cf8b80bd4ec00409577be903195962 to your computer and use it in GitHub Desktop.
blob-example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import '@mohayonao/web-audio-api-shim' | |
import React, { Component } from 'react' | |
import ReactDOM from 'react-dom' | |
import Player from './components/player' | |
import FilePicker from './components/file' | |
import { isAudio, readBlobURL, download, rename, sliceAudioBuffer } from './utils' | |
import { encode } from './worker-client' | |
import WebAudio from './webaudio' | |
import { Box, Button, ButtonGroup, FormControl, InputLabel, Select, MenuItem } from "@mui/material"; | |
import PlayArrow from '@mui/icons-material/PlayArrow' | |
import Pause from '@mui/icons-material/Pause' | |
import ReplayIcon from '@mui/icons-material/Replay'; | |
import { WrapperCamera, WrapperApp, WrapperControllerAndPlayer, WrapperPlayer } from './index.style' | |
import Camera from './components/camera' | |
import Projects from './components/projects/index' | |
import { useNavigate } from 'react-router-dom'; | |
function withMyHook(Component) { | |
return function WrappedComponent(props) { | |
const myHookNavigate = useNavigate(); | |
return <Component {...props} myHookNavigate={myHookNavigate} />; | |
} | |
} | |
class App extends Component { | |
constructor() { | |
super(); | |
this.appConfig = { | |
appName: 'Text Editor', | |
file: { | |
handle: null, | |
name: null, | |
isModified: false, | |
}, | |
options: { | |
captureTabs: true, | |
fontSize: 14, | |
monoSpace: false, | |
wordWrap: true, | |
}, | |
hasFSAccess: 'chooseFileSystemEntries' in window || | |
'showOpenFilePicker' in window, | |
isMac: navigator.userAgent.includes('Mac OS X'), | |
}; | |
this.DEFAULT = 1; | |
this.state = { | |
file: null, | |
currentSegId: "", | |
decoding: false, | |
audioBuffer: null, | |
paused: true, | |
startTime: 0, | |
isBeatPick: false, | |
endTime: Infinity, | |
currentTime: 0, | |
processing: false, | |
segments: [], | |
filesRecorded: {}, | |
videoSelected: null, | |
currentSegmentSelectorId: undefined, | |
} | |
// MODE | |
this._isModeDevelop = 0; // process.env.NODE_ENV === 'develop' | |
// Web socket connection | |
this.ws = new WebSocket('ws://localhost:8000/ws/chat/12/') | |
this.camFunction = () => this.funcCamRef(); | |
} | |
getFormattedDate = () => { | |
const date = new Date(); | |
const str = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate() + " " + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds(); | |
return str; | |
} | |
get startByte() { | |
return parseInt(this.audioBuffer.length * this.state.start / this.widthDurationRatio / this.duration, 10) | |
} | |
get endByte() { | |
return parseInt(this.audioBuffer.length * this.state.end / this.widthDurationRatio / this.duration, 10) | |
} | |
addFile = (file) => { | |
file.name = `${this.state.file.name}_${this.state.currentSegId}}` | |
this.setState(preState => ({ | |
filesRecorded: { | |
...preState.filesRecorded, [this.state.currentSegId]: file | |
} | |
})) | |
} | |
componentDidMount() { | |
this.ws.onopen = () => { | |
// on connecting, do nothing but log it to the console | |
console.log('connected') | |
} | |
this.ws.onmessage = evt => { | |
// listen to data sent from the websocket server | |
const { message } = JSON.parse(evt.data) | |
this.setState({ dataFromServer: message }) | |
console.log(message) | |
if (message === 'start') { | |
this.handlePlayPauseClick() | |
} | |
} | |
this.ws.onclose = () => { | |
console.log('disconnected') | |
} | |
} | |
handleFileChange = async file => { | |
if (!isAudio(file)) { | |
return alert('Please select a legal audio file') | |
} | |
this.setState({ | |
file, | |
paused: true, | |
decoding: true, | |
audioBuffer: null, | |
}) | |
const audioBuffer = await WebAudio.decode(file) | |
window.audioBuffer = audioBuffer | |
this.setState({ | |
paused: false, | |
decoding: false, | |
audioBuffer, | |
startTime: 0, | |
currentTime: 0, | |
endTime: audioBuffer.duration / DEFAULT, | |
}) | |
} | |
handleStartTimeChange = time => { | |
this.setState({ | |
segments: [...this.updateTimeSegments(time, 'start')], | |
}) | |
} | |
_getid(idTemplate) { | |
return idTemplate.split('_')[0] | |
} | |
updateTimeSegments = (time, startOrEnd) => { | |
if (!startOrEnd) throw new Error('Must have Start or End time') | |
const idToUpdate = this._getid(this.state.currentSegmentSelectorId) | |
const newSegments = this.state.segments | |
let previousIndex | |
newSegments.map((s, index) => { | |
if (s.id === idToUpdate) { | |
previousIndex = index - 1 | |
if (startOrEnd === ('end' || 'e')) { | |
s.end = time | |
} | |
if (startOrEnd === ('start' || 's')) { | |
s.start = time | |
} | |
} | |
}) | |
if (previousIndex === -1) { | |
if (newSegments[0].end === time && newSegments[0].start !== time) { | |
newSegments[0].end = time | |
} | |
} else { | |
newSegments[previousIndex].end = time | |
} | |
return newSegments | |
} | |
handleEndTimeChange = (time) => { | |
this.setState({ | |
segments: [...this.updateTimeSegments(time, 'end')], | |
}) | |
} | |
handleCurrentTimeChange = time => { | |
this.setState({ | |
currentTime: time, | |
}) | |
} | |
handlePlayPauseClick = () => { | |
this.setState({ | |
paused: !this.state.paused, | |
}) | |
} | |
handleBeatClick = () => { | |
this.setState({ | |
isBeatPick: !this.state.isBeatPick, | |
}) | |
} | |
handleReplayClick = () => { | |
this.setState({ | |
currentTime: this.state.startTime, | |
}) | |
} | |
_handleEncode = (e) => { | |
const type = e.currentTarget.dataset.type | |
const { startTime, endTime, audioBuffer } = this.state | |
const { length, duration } = audioBuffer | |
const audioSliced = sliceAudioBuffer( | |
audioBuffer, | |
~~(length * startTime / duration), | |
~~(length * endTime / duration), | |
) | |
this.setState({ | |
processing: true, | |
}) | |
encode(audioSliced, type) | |
.then(readBlobURL) | |
.then(url => { | |
download(url, rename(this.state.file.name, type)) | |
}) | |
.catch((e) => console.error(e)) | |
.then(() => { | |
this.setState({ | |
processing: false, | |
}) | |
}) | |
} | |
displaySeconds(seconds) { | |
return seconds.toFixed(2) + 's' | |
} | |
handleSegmentChange = (segment) => { | |
const segments = this.state.segments | |
if (!segments.includes(segment)) { | |
segments.push(segment) | |
} else { | |
console.log('DUPLICATE') | |
} | |
this.setState(segments) | |
} | |
handleCurrentSegmentSelector = (segmentId) => { | |
this.setState({ currentSegmentSelectorId: segmentId }) | |
} | |
handleBeatRemove = (beat) => { | |
const _beat = Number(beat) | |
const segments = this.state.segments | |
segments.map( | |
(segment) => { | |
if (segment && segment.beats && segment.beats.includes(_beat)) { | |
const index = segment.beats.indexOf(_beat); | |
if (index > -1) { | |
segment.beats.splice(index, 1); | |
} | |
} | |
} | |
) | |
this.setState({ segments: [...segments] }) | |
} | |
handleSegmentClick = (id) => { | |
console.log('handleSegmentClick', id); | |
this.setState({ currentSegId: id }) | |
this.handlePlaySegment(id); | |
} | |
handleSelectedVideo = (event) => { | |
this.setState({ | |
selectedVideo: this.state.filesRecorded[event.target.value], | |
}) | |
console.log("handleSelectedVideo1", this.state.selectedVideo) | |
} | |
handleSegmentRemove = (id) => { | |
const _getid = (id) => id.split('_')[0]; | |
// this.setState({ currentSegId: id }) | |
const newSegments = this.state.segments.filter(seg => seg.id != _getid(id)) | |
this.setState({ segments: newSegments }) | |
// this.handlePlaySegment(id); | |
} | |
handlePlaySegment = (id) => { | |
this.state.segments.map(seg => { | |
if (seg.id == id) { | |
console.log('handlePlaySegment', { currentTime: seg.start, startTime: seg.start, endTime: seg.end }); | |
this.setState({ currentTime: seg.start, startTime: seg.start, endTime: seg.end }) | |
} else { | |
this.setState({ paused: false }) | |
} | |
}) | |
} | |
handleStartRecording = () => { | |
console.log('handleStartRecordClick', this.state.currentSegId); | |
this.camFunction().start() | |
this.handlePlaySegment(this.state.currentSegId) | |
} | |
handleStopRecording = () => { | |
console.log('handleStopRecordClick', this.state.currentSegId); | |
this.camFunction().stop(); | |
this.handlePlaySegment(this.state.currentSegId) | |
} | |
handlePickSegments = () => { | |
this.setState({ currentTime: 0, startTime: 0, endTime: 800 }) | |
} | |
handlePickSegment = (end) => { | |
if (!this.state.isBeatPick) { | |
console.log('end', end) | |
const colors = ['Blue ', 'Green', 'Red', 'Orange', 'Violet', 'Indigo', 'Yellow '] | |
const randomColor = colors[Math.floor(Math.random() * colors.length)] | |
const currentSegments = this.state.segments | |
const lastSegment = currentSegments[currentSegments.length - 1] | |
const startNewSegment = lastSegment?.end || 0; | |
const newSegment = { | |
start: startNewSegment, | |
end: end, | |
id: `${end.toString().slice(0, 3)}`, | |
color: randomColor, | |
} | |
currentSegments.push(newSegment) | |
console.log('currentSegments', currentSegments) | |
this.setState({ segments: [...currentSegments] }) | |
} else { | |
const beatTime = end | |
let segments = this.state.segments | |
segments.map(seg => { | |
if (seg.start < beatTime && seg.end >= beatTime) { | |
if (!seg['beats']) { | |
seg['beats'] = [beatTime] | |
} else { | |
seg['beats'].push(beatTime) | |
} | |
} | |
}) | |
this.setState({ segments: [...segments] }) | |
} | |
} | |
render() { | |
return ( | |
<WrapperApp> | |
{/* CAMERA */} | |
<WrapperCamera> | |
<Camera | |
forwardRef={c => { this.funcCamRef = c }} | |
addFile={this.addFile} | |
selectedVideo={this.state.selectedVideo} /> | |
</WrapperCamera> | |
{/* PLAYER */} | |
<WrapperControllerAndPlayer> | |
<WrapperPlayer> { | |
!this.state.decoding && this.state.audioBuffer ? | |
(<Player | |
segments={this.state.segments} | |
audioBuffer={this.state.audioBuffer} | |
paused={this.state.paused} | |
startTime={this.state.startTime} | |
endTime={this.state.endTime} | |
currentTime={this.state.currentTime} | |
onStartTimeChange={this.handleStartTimeChange} | |
onEndTimeChange={this.handleEndTimeChange} | |
onCurrentTimeChange={this.handleCurrentTimeChange} | |
onSetPaused={this.handlePlayPauseClick} | |
onSegmentChanges={this.handleSegmentChange} | |
onCurrentSegmentSelector={this.handleCurrentSegmentSelector} | |
onPickSegmentEnd={this.handlePickSegment} | |
onBeatRemove={this.handleBeatRemove} | |
onSegmentRemove={this.handleSegmentRemove} | |
ref='player' | |
/>) : <span>Select Music</span> | |
}</WrapperPlayer> | |
<Box > | |
<ButtonGroup | |
style={{ width: '100px' }} | |
orientation="vertical" | |
aria-label="vertical outlined button group" | |
> | |
{/* <Button variant="outlined" startIcon={<AlarmIcon />}> | |
</Button> | |
<Button variant="outlined" onClick={this.handleStartRecording} startIcon={<PhotoCamera />}> | |
</Button> | |
<Button variant="outlined" startIcon={<DeleteIcon />}> | |
</Button> */} | |
{/* <Button variant="outlined" onClick={this.handleStopRecording} startIcon={<SaveIcon />}> | |
</Button> */} | |
<Button variant="outlined" startIcon={this.state.paused ? <PlayArrow /> : <Pause />} onClick={this.handlePlayPauseClick}> | |
</Button> | |
<Button variant="outlined" onClick={this.handlePickSegments} >Segments | |
</Button> | |
<Button variant="outlined" onClick={this.handleBeatClick}> | |
{this.state.isBeatPick ? 'Segment' : 'Beats'} | |
</Button> | |
<Button variant="outlined" onClick={this.handleReplayClick} startIcon={<ReplayIcon />}> | |
</Button> | |
<Button variant="outlined" onClick={() => this.props.myHookNavigate("/project")} > | |
BACK | |
</Button> | |
<FilePicker | |
onChange={this.handleFileChange}> | |
</FilePicker> | |
</ButtonGroup> | |
</Box> | |
</WrapperControllerAndPlayer> | |
{this.state.segments.map((seg, index) => { | |
return (<a className='ctrl-item' title={`Segment ${index}`} onClick={() => this.handleSegmentClick(seg.id)}> | |
<span>{`Segment ${index}`} </span> | |
<FormControl fullWidth> | |
<InputLabel id="demo-simple-select-label">Videos</InputLabel> | |
<Select | |
labelId="demo-simple-select-label" | |
id="demo-simple-select" | |
// value={videos} | |
label="videos" | |
onChange={this.handleSelectedVideo} | |
> | |
<MenuItem value={seg.id}>Video {seg.id} </MenuItem> | |
<MenuItem value={seg.id}>change </MenuItem> | |
</Select> | |
</FormControl> | |
</a>) | |
})} | |
</WrapperApp> | |
) | |
} | |
} | |
export default withMyHook(App) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment