Liyainterview / StreamlitAudioRecorder.tsx
Ntabukiraniro's picture
Upload 3 files
be77781 verified
import {
Streamlit,
StreamlitComponentBase,
withStreamlitConnection,
} from "streamlit-component-lib"
import React, { ReactNode } from "react"
import AudioReactRecorder, { RecordState } from 'audio-react-recorder'
import 'audio-react-recorder/dist/index.css'
interface State {
isFocused: boolean
recordState: null
audioDataURL: string
reset: boolean
}
class StAudioRec extends StreamlitComponentBase<State> {
public state = { isFocused: false, recordState: null, audioDataURL: '', reset: false}
public render = (): ReactNode => {
// Arguments that are passed to the plugin in Python are accessible
// Streamlit sends us a theme object via props that we can use to ensure
// that our component has visuals that match the active theme in a
// streamlit app.
const { theme } = this.props
const style: React.CSSProperties = {}
const { recordState } = this.state
// compatibility with older vers of Streamlit that don't send theme object.
if (theme) {
// Use the theme object to style our button border. Alternatively, the
// theme style is defined in CSS vars.
const borderStyling = `1px solid ${
this.state.isFocused ? theme.primaryColor : "gray"}`
style.border = borderStyling
style.outline = borderStyling
}
return (
<span>
<div>
<button id='record' onClick={this.onClick_start}>
Start Recording
</button>
<button id='stop' onClick={this.onClick_stop}>
Stop
</button>
<button id='reset' onClick={this.onClick_reset}>
Reset
</button>
<button id='continue' onClick={this.onClick_continue}>
Download
</button>
<AudioReactRecorder
state={recordState}
onStop={this.onStop_audio}
type='audio/wav'
backgroundColor='rgb(255, 255, 255)'
foregroundColor='rgb(255,76,75)'
canvasWidth={450}
canvasHeight={100}
/>
<audio
id='audio'
controls
src={this.state.audioDataURL}
/>
</div>
</span>
)
}
private onClick_start = () => {
this.setState({
reset: false,
audioDataURL: '',
recordState: RecordState.START
})
Streamlit.setComponentValue('')
}
private onClick_stop = () => {
this.setState({
reset: false,
recordState: RecordState.STOP
})
}
private onClick_reset = () => {
this.setState({
reset: true,
audioDataURL: '',
recordState: RecordState.STOP
})
Streamlit.setComponentValue('')
}
private onClick_continue = () => {
if (this.state.audioDataURL !== '')
{
// get datetime string for filename
let datetime = new Date().toLocaleString();
datetime = datetime.replace(' ', '');
datetime = datetime.replace(/_/g, '');
datetime = datetime.replace(',', '');
var filename = 'streamlit_audio_' + datetime + '.wav';
// auromatically trigger download
const a = document.createElement('a');
a.style.display = 'none';
a.href = this.state.audioDataURL;
a.download = filename;
document.body.appendChild(a);
a.click();
}
}
private onStop_audio = (data) => {
if (this.state.reset === true)
{
this.setState({
audioDataURL: ''
})
Streamlit.setComponentValue('')
}else{
this.setState({
audioDataURL: data.url
})
fetch(data.url).then(function(ctx){
return ctx.blob()
}).then(function(blob){
// converting blob to arrayBuffer, this process step needs to be be improved
// this operation's time complexity scales exponentially with audio length
return (new Response(blob)).arrayBuffer()
}).then(function(buffer){
Streamlit.setComponentValue({
"arr": new Uint8Array(buffer)
})
})
}
}
}
// "withStreamlitConnection" is a wrapper function. It bootstraps the
// connection between your component and the Streamlit app, and handles
// passing arguments from Python -> Component.
// You don't need to edit withStreamlitConnection (but you're welcome to!).
export default withStreamlitConnection(StAudioRec)
// Tell Streamlit we're ready to start receiving data. We won't get our
// first RENDER_EVENT until we call this function.
Streamlit.setComponentReady()
// Finally, tell Streamlit to update our initial height. We omit the
// `height` parameter here to have it default to our scrollHeight.
Streamlit.setFrameHeight()