Skip to content

Instantly share code, notes, and snippets.

@trkhanh
Last active May 30, 2022 12:53
Show Gist options
  • Save trkhanh/e1cf8b80bd4ec00409577be903195962 to your computer and use it in GitHub Desktop.
Save trkhanh/e1cf8b80bd4ec00409577be903195962 to your computer and use it in GitHub Desktop.
blob-example
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