Spaces:
Sleeping
Sleeping
Commit
•
be77781
1
Parent(s):
7675fb1
Upload 3 files
Browse files- StreamlitAudioRecorder.tsx +168 -0
- index.tsx +10 -0
- react-app-env.d.ts +2 -0
StreamlitAudioRecorder.tsx
ADDED
@@ -0,0 +1,168 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import {
|
2 |
+
Streamlit,
|
3 |
+
StreamlitComponentBase,
|
4 |
+
withStreamlitConnection,
|
5 |
+
} from "streamlit-component-lib"
|
6 |
+
import React, { ReactNode } from "react"
|
7 |
+
|
8 |
+
import AudioReactRecorder, { RecordState } from 'audio-react-recorder'
|
9 |
+
import 'audio-react-recorder/dist/index.css'
|
10 |
+
|
11 |
+
interface State {
|
12 |
+
isFocused: boolean
|
13 |
+
recordState: null
|
14 |
+
audioDataURL: string
|
15 |
+
reset: boolean
|
16 |
+
}
|
17 |
+
|
18 |
+
class StAudioRec extends StreamlitComponentBase<State> {
|
19 |
+
public state = { isFocused: false, recordState: null, audioDataURL: '', reset: false}
|
20 |
+
|
21 |
+
public render = (): ReactNode => {
|
22 |
+
// Arguments that are passed to the plugin in Python are accessible
|
23 |
+
|
24 |
+
// Streamlit sends us a theme object via props that we can use to ensure
|
25 |
+
// that our component has visuals that match the active theme in a
|
26 |
+
// streamlit app.
|
27 |
+
const { theme } = this.props
|
28 |
+
const style: React.CSSProperties = {}
|
29 |
+
|
30 |
+
const { recordState } = this.state
|
31 |
+
|
32 |
+
// compatibility with older vers of Streamlit that don't send theme object.
|
33 |
+
if (theme) {
|
34 |
+
// Use the theme object to style our button border. Alternatively, the
|
35 |
+
// theme style is defined in CSS vars.
|
36 |
+
const borderStyling = `1px solid ${
|
37 |
+
this.state.isFocused ? theme.primaryColor : "gray"}`
|
38 |
+
style.border = borderStyling
|
39 |
+
style.outline = borderStyling
|
40 |
+
}
|
41 |
+
|
42 |
+
return (
|
43 |
+
<span>
|
44 |
+
<div>
|
45 |
+
<button id='record' onClick={this.onClick_start}>
|
46 |
+
Start Recording
|
47 |
+
</button>
|
48 |
+
<button id='stop' onClick={this.onClick_stop}>
|
49 |
+
Stop
|
50 |
+
</button>
|
51 |
+
<button id='reset' onClick={this.onClick_reset}>
|
52 |
+
Reset
|
53 |
+
</button>
|
54 |
+
|
55 |
+
<button id='continue' onClick={this.onClick_continue}>
|
56 |
+
Download
|
57 |
+
</button>
|
58 |
+
|
59 |
+
<AudioReactRecorder
|
60 |
+
state={recordState}
|
61 |
+
onStop={this.onStop_audio}
|
62 |
+
type='audio/wav'
|
63 |
+
backgroundColor='rgb(255, 255, 255)'
|
64 |
+
foregroundColor='rgb(255,76,75)'
|
65 |
+
canvasWidth={450}
|
66 |
+
canvasHeight={100}
|
67 |
+
/>
|
68 |
+
|
69 |
+
<audio
|
70 |
+
id='audio'
|
71 |
+
controls
|
72 |
+
src={this.state.audioDataURL}
|
73 |
+
/>
|
74 |
+
|
75 |
+
</div>
|
76 |
+
</span>
|
77 |
+
)
|
78 |
+
}
|
79 |
+
|
80 |
+
|
81 |
+
private onClick_start = () => {
|
82 |
+
this.setState({
|
83 |
+
reset: false,
|
84 |
+
audioDataURL: '',
|
85 |
+
recordState: RecordState.START
|
86 |
+
})
|
87 |
+
Streamlit.setComponentValue('')
|
88 |
+
}
|
89 |
+
|
90 |
+
private onClick_stop = () => {
|
91 |
+
this.setState({
|
92 |
+
reset: false,
|
93 |
+
recordState: RecordState.STOP
|
94 |
+
})
|
95 |
+
}
|
96 |
+
|
97 |
+
private onClick_reset = () => {
|
98 |
+
this.setState({
|
99 |
+
reset: true,
|
100 |
+
audioDataURL: '',
|
101 |
+
recordState: RecordState.STOP
|
102 |
+
})
|
103 |
+
Streamlit.setComponentValue('')
|
104 |
+
}
|
105 |
+
|
106 |
+
private onClick_continue = () => {
|
107 |
+
if (this.state.audioDataURL !== '')
|
108 |
+
{
|
109 |
+
// get datetime string for filename
|
110 |
+
let datetime = new Date().toLocaleString();
|
111 |
+
datetime = datetime.replace(' ', '');
|
112 |
+
datetime = datetime.replace(/_/g, '');
|
113 |
+
datetime = datetime.replace(',', '');
|
114 |
+
var filename = 'streamlit_audio_' + datetime + '.wav';
|
115 |
+
|
116 |
+
// auromatically trigger download
|
117 |
+
const a = document.createElement('a');
|
118 |
+
a.style.display = 'none';
|
119 |
+
a.href = this.state.audioDataURL;
|
120 |
+
a.download = filename;
|
121 |
+
document.body.appendChild(a);
|
122 |
+
a.click();
|
123 |
+
}
|
124 |
+
}
|
125 |
+
|
126 |
+
private onStop_audio = (data) => {
|
127 |
+
if (this.state.reset === true)
|
128 |
+
{
|
129 |
+
this.setState({
|
130 |
+
audioDataURL: ''
|
131 |
+
})
|
132 |
+
Streamlit.setComponentValue('')
|
133 |
+
}else{
|
134 |
+
this.setState({
|
135 |
+
audioDataURL: data.url
|
136 |
+
})
|
137 |
+
|
138 |
+
fetch(data.url).then(function(ctx){
|
139 |
+
return ctx.blob()
|
140 |
+
}).then(function(blob){
|
141 |
+
// converting blob to arrayBuffer, this process step needs to be be improved
|
142 |
+
// this operation's time complexity scales exponentially with audio length
|
143 |
+
return (new Response(blob)).arrayBuffer()
|
144 |
+
}).then(function(buffer){
|
145 |
+
Streamlit.setComponentValue({
|
146 |
+
"arr": new Uint8Array(buffer)
|
147 |
+
})
|
148 |
+
})
|
149 |
+
|
150 |
+
}
|
151 |
+
|
152 |
+
|
153 |
+
}
|
154 |
+
}
|
155 |
+
|
156 |
+
// "withStreamlitConnection" is a wrapper function. It bootstraps the
|
157 |
+
// connection between your component and the Streamlit app, and handles
|
158 |
+
// passing arguments from Python -> Component.
|
159 |
+
// You don't need to edit withStreamlitConnection (but you're welcome to!).
|
160 |
+
export default withStreamlitConnection(StAudioRec)
|
161 |
+
|
162 |
+
// Tell Streamlit we're ready to start receiving data. We won't get our
|
163 |
+
// first RENDER_EVENT until we call this function.
|
164 |
+
Streamlit.setComponentReady()
|
165 |
+
|
166 |
+
// Finally, tell Streamlit to update our initial height. We omit the
|
167 |
+
// `height` parameter here to have it default to our scrollHeight.
|
168 |
+
Streamlit.setFrameHeight()
|
index.tsx
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React from "react"
|
2 |
+
import ReactDOM from "react-dom"
|
3 |
+
import StAudioRec from "./StreamlitAudioRecorder"
|
4 |
+
|
5 |
+
ReactDOM.render(
|
6 |
+
<React.StrictMode>
|
7 |
+
<StAudioRec />
|
8 |
+
</React.StrictMode>,
|
9 |
+
document.getElementById("root")
|
10 |
+
)
|
react-app-env.d.ts
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
/// <reference types="react-scripts" />
|
2 |
+
declare module 'audio-react-recorder';
|