Ntabukiraniro commited on
Commit
be77781
1 Parent(s): 7675fb1

Upload 3 files

Browse files
Files changed (3) hide show
  1. StreamlitAudioRecorder.tsx +168 -0
  2. index.tsx +10 -0
  3. 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';