Fabio Grasso commited on
Commit
59e5631
1 Parent(s): 4f6e486
Files changed (41) hide show
  1. Dockerfile +11 -0
  2. README.md +2 -2
  3. app.py +3 -0
  4. app/__init__.py +0 -0
  5. app/demucs_runner.py +172 -0
  6. app/main.py +168 -0
  7. lib/__init__.py +0 -0
  8. lib/st_audiorec/.DS_Store +0 -0
  9. lib/st_audiorec/__init__.py +1 -0
  10. lib/st_audiorec/frontend/.DS_Store +0 -0
  11. lib/st_audiorec/frontend/.env +6 -0
  12. lib/st_audiorec/frontend/.prettierrc +5 -0
  13. lib/st_audiorec/frontend/build/.DS_Store +0 -0
  14. lib/st_audiorec/frontend/build/asset-manifest.json +22 -0
  15. lib/st_audiorec/frontend/build/bootstrap.min.css +0 -0
  16. lib/st_audiorec/frontend/build/index.html +1 -0
  17. lib/st_audiorec/frontend/build/precache-manifest.4829c060d313d0b0d13d9af3b0180289.js +26 -0
  18. lib/st_audiorec/frontend/build/service-worker.js +39 -0
  19. lib/st_audiorec/frontend/build/static/.DS_Store +0 -0
  20. lib/st_audiorec/frontend/build/static/css/2.bfbf028b.chunk.css +2 -0
  21. lib/st_audiorec/frontend/build/static/css/2.bfbf028b.chunk.css.map +1 -0
  22. lib/st_audiorec/frontend/build/static/js/2.270b84d8.chunk.js +0 -0
  23. lib/st_audiorec/frontend/build/static/js/2.270b84d8.chunk.js.LICENSE.txt +58 -0
  24. lib/st_audiorec/frontend/build/static/js/2.270b84d8.chunk.js.map +0 -0
  25. lib/st_audiorec/frontend/build/static/js/main.833ba252.chunk.js +2 -0
  26. lib/st_audiorec/frontend/build/static/js/main.833ba252.chunk.js.map +1 -0
  27. lib/st_audiorec/frontend/build/static/js/runtime-main.11ec9aca.js +2 -0
  28. lib/st_audiorec/frontend/build/static/js/runtime-main.11ec9aca.js.map +1 -0
  29. lib/st_audiorec/frontend/build/styles.css +59 -0
  30. lib/st_audiorec/frontend/package-lock.json +0 -0
  31. lib/st_audiorec/frontend/package.json +44 -0
  32. lib/st_audiorec/frontend/public/.DS_Store +0 -0
  33. lib/st_audiorec/frontend/public/bootstrap.min.css +0 -0
  34. lib/st_audiorec/frontend/public/index.html +21 -0
  35. lib/st_audiorec/frontend/public/styles.css +59 -0
  36. lib/st_audiorec/frontend/src/StreamlitAudioRecorder.tsx +168 -0
  37. lib/st_audiorec/frontend/src/index.tsx +10 -0
  38. lib/st_audiorec/frontend/src/react-app-env.d.ts +2 -0
  39. lib/st_audiorec/frontend/tsconfig.json +21 -0
  40. lib/st_custom_components.py +33 -0
  41. requirements.txt +3 -0
Dockerfile ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9
2
+
3
+ WORKDIR /code
4
+
5
+ COPY ./requirements.txt /code/requirements.txt
6
+
7
+ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
8
+
9
+ COPY . .
10
+
11
+ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860"]
README.md CHANGED
@@ -1,8 +1,8 @@
1
  ---
2
  title: Test Space
3
- emoji: 👀
4
  colorFrom: indigo
5
- colorTo: blue
6
  sdk: docker
7
  pinned: false
8
  ---
 
1
  ---
2
  title: Test Space
3
+ emoji: 🎶
4
  colorFrom: indigo
5
+ colorTo: yellow
6
  sdk: docker
7
  pinned: false
8
  ---
app.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ from app import main
2
+
3
+ main.run()
app/__init__.py ADDED
File without changes
app/demucs_runner.py ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import sys
3
+ from pathlib import Path
4
+ from typing import List
5
+
6
+ from dora.log import fatal
7
+ import torch as th
8
+
9
+ from demucs.apply import apply_model, BagOfModels
10
+ from demucs.audio import save_audio
11
+ from demucs.pretrained import get_model_from_args, ModelLoadingError
12
+ from demucs.separate import load_track
13
+
14
+
15
+ def separator(
16
+ tracks: List[Path],
17
+ out: Path,
18
+ model: str,
19
+ device: str,
20
+ shifts: int,
21
+ overlap: float,
22
+ stem: str,
23
+ int24: bool,
24
+ float32: bool,
25
+ clip_mode: str,
26
+ mp3: bool,
27
+ mp3_bitrate: int,
28
+ jobs: int,
29
+ verbose: bool,
30
+ ):
31
+ """Separate the sources for the given tracks
32
+
33
+ Args:
34
+ tracks (Path): Path to tracks
35
+ out (Path): Folder where to put extracted tracks. A subfolder with the model name will be created.
36
+ model (str): Model name
37
+ device (str): Device to use, default is cuda if available else cpu
38
+ shifts (int): Number of random shifts for equivariant stabilization. Increase separation time but improves quality for Demucs. 10 was used in the original paper.
39
+ overlap (float): Overlap
40
+ stem (str): Only separate audio into {STEM} and no_{STEM}.
41
+ int24 (bool): Save wav output as 24 bits wav.
42
+ float32 (bool): Save wav output as float32 (2x bigger).
43
+ clip_mode (str): Strategy for avoiding clipping: rescaling entire signal if necessary (rescale) or hard clipping (clamp).
44
+ mp3 (bool): Convert the output wavs to mp3.
45
+ mp3_bitrate (int): Bitrate of converted mp3.
46
+ jobs (int): Number of jobs. This can increase memory usage but will be much faster when multiple cores are available.
47
+ verbose (bool): Verbose
48
+ """
49
+ args = argparse.Namespace()
50
+ args.tracks = tracks
51
+ args.out = out
52
+ args.model = model
53
+ args.device = device
54
+ args.shifts = shifts
55
+ args.overlap = overlap
56
+ args.stem = stem
57
+ args.int24 = int24
58
+ args.float32 = float32
59
+ args.clip_mode = clip_mode
60
+ args.mp3 = mp3
61
+ args.mp3_bitrate = mp3_bitrate
62
+ args.jobs = jobs
63
+ args.verbose = verbose
64
+ args.filename = "{track}/{stem}.{ext}"
65
+ args.split = True
66
+ args.segment = None
67
+ args.name = model
68
+ args.repo = None
69
+
70
+ try:
71
+ model = get_model_from_args(args)
72
+ except ModelLoadingError as error:
73
+ fatal(error.args[0])
74
+
75
+ if args.segment is not None and args.segment < 8:
76
+ fatal("Segment must greater than 8. ")
77
+
78
+ if ".." in args.filename.replace("\\", "/").split("/"):
79
+ fatal('".." must not appear in filename. ')
80
+
81
+ if isinstance(model, BagOfModels):
82
+ print(
83
+ f"Selected model is a bag of {len(model.models)} models. "
84
+ "You will see that many progress bars per track."
85
+ )
86
+ if args.segment is not None:
87
+ for sub in model.models:
88
+ sub.segment = args.segment
89
+ else:
90
+ if args.segment is not None:
91
+ model.segment = args.segment
92
+
93
+ model.cpu()
94
+ model.eval()
95
+
96
+ if args.stem is not None and args.stem not in model.sources:
97
+ fatal(
98
+ 'error: stem "{stem}" is not in selected model. STEM must be one of {sources}.'.format(
99
+ stem=args.stem, sources=", ".join(model.sources)
100
+ )
101
+ )
102
+ out = args.out / args.name
103
+ out.mkdir(parents=True, exist_ok=True)
104
+ print(f"Separated tracks will be stored in {out.resolve()}")
105
+ for track in args.tracks:
106
+ if not track.exists():
107
+ print(
108
+ f"File {track} does not exist. If the path contains spaces, "
109
+ 'please try again after surrounding the entire path with quotes "".',
110
+ file=sys.stderr,
111
+ )
112
+ continue
113
+ print(f"Separating track {track}")
114
+ wav = load_track(track, model.audio_channels, model.samplerate)
115
+
116
+ ref = wav.mean(0)
117
+ wav = (wav - ref.mean()) / ref.std()
118
+ sources = apply_model(
119
+ model,
120
+ wav[None],
121
+ device=args.device,
122
+ shifts=args.shifts,
123
+ split=args.split,
124
+ overlap=args.overlap,
125
+ progress=True,
126
+ num_workers=args.jobs,
127
+ )[0]
128
+ sources = sources * ref.std() + ref.mean()
129
+
130
+ if args.mp3:
131
+ ext = "mp3"
132
+ else:
133
+ ext = "wav"
134
+ kwargs = {
135
+ "samplerate": model.samplerate,
136
+ "bitrate": args.mp3_bitrate,
137
+ "clip": args.clip_mode,
138
+ "as_float": args.float32,
139
+ "bits_per_sample": 24 if args.int24 else 16,
140
+ }
141
+ if args.stem is None:
142
+ for source, name in zip(sources, model.sources):
143
+ stem = out / args.filename.format(
144
+ track=track.name.rsplit(".", 1)[0],
145
+ trackext=track.name.rsplit(".", 1)[-1],
146
+ stem=name,
147
+ ext=ext,
148
+ )
149
+ stem.parent.mkdir(parents=True, exist_ok=True)
150
+ save_audio(source, str(stem), **kwargs)
151
+ else:
152
+ sources = list(sources)
153
+ stem = out / args.filename.format(
154
+ track=track.name.rsplit(".", 1)[0],
155
+ trackext=track.name.rsplit(".", 1)[-1],
156
+ stem=args.stem,
157
+ ext=ext,
158
+ )
159
+ stem.parent.mkdir(parents=True, exist_ok=True)
160
+ save_audio(sources.pop(model.sources.index(args.stem)), str(stem), **kwargs)
161
+ # Warning : after poping the stem, selected stem is no longer in the list 'sources'
162
+ other_stem = th.zeros_like(sources[0])
163
+ for i in sources:
164
+ other_stem += i
165
+ stem = out / args.filename.format(
166
+ track=track.name.rsplit(".", 1)[0],
167
+ trackext=track.name.rsplit(".", 1)[-1],
168
+ stem="no_" + args.stem,
169
+ ext=ext,
170
+ )
171
+ stem.parent.mkdir(parents=True, exist_ok=True)
172
+ save_audio(other_stem, str(stem), **kwargs)
app/main.py ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ import os
3
+ from pathlib import Path
4
+ from shutil import rmtree
5
+
6
+ from pydub import AudioSegment
7
+ import streamlit as st
8
+
9
+ # from lib.st_custom_components import st_audiorec
10
+
11
+ # try:
12
+ # from app.demucs_runner import separator
13
+ # except ImportError:
14
+ # from demucs_runner import separator
15
+
16
+
17
+ logging.basicConfig(
18
+ format="%(asctime)s %(levelname)-8s %(message)s",
19
+ level=logging.DEBUG,
20
+ datefmt="%Y-%m-%d %H:%M:%S",
21
+ )
22
+
23
+ max_duration = 10 # in seconds
24
+
25
+ model = "htdemucs"
26
+ extensions = ["mp3", "wav", "ogg", "flac"] # we will look for all those file types.
27
+ two_stems = None # only separate one stems from the rest, for instance
28
+
29
+ # Options for the output audio.
30
+ mp3 = True
31
+ mp3_rate = 320
32
+ float32 = False # output as float 32 wavs, unsused if 'mp3' is True.
33
+ int24 = False # output as int24 wavs, unused if 'mp3' is True.
34
+ # You cannot set both `float32 = True` and `int24 = True` !!
35
+
36
+
37
+ def find_files(in_path):
38
+ out = []
39
+ for file in Path(in_path).iterdir():
40
+ if file.suffix.lower().lstrip(".") in extensions:
41
+ out.append(file)
42
+ return out
43
+
44
+ out_path = Path("separated")
45
+ in_path = Path("tmp_in")
46
+
47
+ def clean_folders():
48
+ if in_path.exists():
49
+ rmtree(in_path)
50
+ in_path.mkdir()
51
+ if out_path.exists():
52
+ rmtree(out_path)
53
+ out_path.mkdir()
54
+
55
+ def url_is_valid(url):
56
+ import requests
57
+ try:
58
+ r = requests.get(url)
59
+ r.raise_for_status()
60
+ return True
61
+ except Exception:
62
+ st.error("URL is not valid.")
63
+
64
+
65
+ def run():
66
+ st.markdown("<h1><center>🎶 Music Source Splitter</center></h1>", unsafe_allow_html=True)
67
+
68
+ st.markdown("""
69
+ <style>
70
+ .st-af {
71
+ font-size: 1.5rem;
72
+ align-items: center;
73
+ padding-right: 2rem;
74
+ }
75
+
76
+ </style>
77
+ """,
78
+ unsafe_allow_html=True,
79
+ )
80
+ filename = None
81
+ choice = st.radio(label=" ", options=["🔗 From URL", "⬆️ Upload File", "🎤 Record Audio"], horizontal=True)
82
+ if choice == "🔗 From URL":
83
+ url = st.text_input("Paste the URL of the audio file", key="url")
84
+ if url != "":
85
+ # check if the url is valid
86
+ if url_is_valid(url):
87
+ with st.spinner("Downloading audio..."):
88
+ clean_folders()
89
+ filename = url.split("/")[-1]
90
+ os.system(f"wget -O {in_path / filename} {url}")
91
+
92
+ elif choice == "⬆️ Upload File":
93
+ uploaded_file = st.file_uploader("Choose a file")
94
+ if uploaded_file is not None:
95
+ clean_folders()
96
+ with open(in_path / uploaded_file.name, "wb") as f:
97
+ f.write(uploaded_file.getbuffer())
98
+ filename = uploaded_file.name
99
+ elif choice == "🎤 Record Audio":
100
+ wav_audio_data = st_audiorec()
101
+ if wav_audio_data is not None:
102
+ if wav_audio_data != b'RIFF,\x00\x00\x00WAVEfmt \x10\x00\x00\x00\x01\x00\x02\x00\x80>\x00\x00\x00\xfa\x00\x00\x04\x00\x10\x00data\x00\x00\x00\x00':
103
+ clean_folders()
104
+ filename = "recording.wav"
105
+ with open(in_path / filename, "wb") as f:
106
+ f.write(wav_audio_data)
107
+
108
+ if filename is not None:
109
+ st.markdown("<hr>", unsafe_allow_html=True)
110
+ cols = st.columns(2)
111
+ with cols[0]:
112
+ st.markdown("<h3>Original Audio</h3>", unsafe_allow_html=True)
113
+ with cols[1]:
114
+ audio_file = open(in_path / filename, "rb")
115
+ audio_bytes = audio_file.read()
116
+ _ = st.audio(audio_bytes)
117
+
118
+ song = AudioSegment.from_file(in_path / filename, filename.split(".")[-1])
119
+ n_secs = round(len(song) / 1000)
120
+ start_time = st.slider("Choose the start time", min_value=0, max_value=n_secs, value=0, help=f"Maximum duration is {max_duration} seconds.")
121
+ end_time = min(start_time + max_duration, n_secs)
122
+ tot_time = end_time - start_time
123
+ st.info(f"Audio source will be processed from {start_time} to {end_time} seconds.", icon="⏱")
124
+
125
+ execute = st.button("Split Music 🎶", type="primary")
126
+ if execute:
127
+ song = song[start_time*1000:end_time*1000]
128
+ song.export(in_path / filename, format=filename.split(".")[-1])
129
+ with st.spinner(f"Splitting source audio, it will take almost {round(tot_time*3.6)} seconds..."):
130
+ separator(
131
+ tracks=[in_path / filename],
132
+ out=out_path,
133
+ model=model,
134
+ device="cpu",
135
+ shifts=1,
136
+ overlap=0.5,
137
+ stem=two_stems,
138
+ int24=int24,
139
+ float32=float32,
140
+ clip_mode="rescale",
141
+ mp3=mp3,
142
+ mp3_bitrate=mp3_rate,
143
+ jobs=os.cpu_count(),
144
+ verbose=True,
145
+ )
146
+
147
+ last_dir = ".".join(filename.split(".")[:-1])
148
+ for file in ["vocals.mp3", "drums.mp3", "bass.mp3", "other.mp3"]:
149
+ file = out_path / Path(model) / last_dir / file
150
+ st.markdown("<hr>", unsafe_allow_html=True)
151
+ cols = st.columns(2)
152
+ with cols[0]:
153
+ label = file.name.split(".")[0].replace("_", " ").capitalize()
154
+ # add emoji to label
155
+ label = {
156
+ "Drums": "🥁",
157
+ "Bass": "🎸",
158
+ "Other": "🎹",
159
+ "Vocals": "🎤",
160
+ }.get(label) + " " + label
161
+ st.markdown("<h3>" + label + "</h3>", unsafe_allow_html=True)
162
+ with cols[1]:
163
+ audio_file = open(file, "rb")
164
+ audio_bytes = audio_file.read()
165
+ st.audio(audio_bytes)
166
+
167
+ if __name__ == "__main__":
168
+ run()
lib/__init__.py ADDED
File without changes
lib/st_audiorec/.DS_Store ADDED
Binary file (6.15 kB). View file
 
lib/st_audiorec/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+
lib/st_audiorec/frontend/.DS_Store ADDED
Binary file (8.2 kB). View file
 
lib/st_audiorec/frontend/.env ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ # Run the component's dev server on :3001
2
+ # (The Streamlit dev server already runs on :3000)
3
+ PORT=3001
4
+
5
+ # Don't automatically open the web browser on `npm run start`.
6
+ BROWSER=none
lib/st_audiorec/frontend/.prettierrc ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ {
2
+ "endOfLine": "lf",
3
+ "semi": false,
4
+ "trailingComma": "es5"
5
+ }
lib/st_audiorec/frontend/build/.DS_Store ADDED
Binary file (6.15 kB). View file
 
lib/st_audiorec/frontend/build/asset-manifest.json ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "files": {
3
+ "main.js": "./static/js/main.833ba252.chunk.js",
4
+ "main.js.map": "./static/js/main.833ba252.chunk.js.map",
5
+ "runtime-main.js": "./static/js/runtime-main.11ec9aca.js",
6
+ "runtime-main.js.map": "./static/js/runtime-main.11ec9aca.js.map",
7
+ "static/css/2.bfbf028b.chunk.css": "./static/css/2.bfbf028b.chunk.css",
8
+ "static/js/2.270b84d8.chunk.js": "./static/js/2.270b84d8.chunk.js",
9
+ "static/js/2.270b84d8.chunk.js.map": "./static/js/2.270b84d8.chunk.js.map",
10
+ "index.html": "./index.html",
11
+ "precache-manifest.4829c060d313d0b0d13d9af3b0180289.js": "./precache-manifest.4829c060d313d0b0d13d9af3b0180289.js",
12
+ "service-worker.js": "./service-worker.js",
13
+ "static/css/2.bfbf028b.chunk.css.map": "./static/css/2.bfbf028b.chunk.css.map",
14
+ "static/js/2.270b84d8.chunk.js.LICENSE.txt": "./static/js/2.270b84d8.chunk.js.LICENSE.txt"
15
+ },
16
+ "entrypoints": [
17
+ "static/js/runtime-main.11ec9aca.js",
18
+ "static/css/2.bfbf028b.chunk.css",
19
+ "static/js/2.270b84d8.chunk.js",
20
+ "static/js/main.833ba252.chunk.js"
21
+ ]
22
+ }
lib/st_audiorec/frontend/build/bootstrap.min.css ADDED
The diff for this file is too large to render. See raw diff
 
lib/st_audiorec/frontend/build/index.html ADDED
@@ -0,0 +1 @@
 
 
1
+ <!doctype html><html lang="en"><head><title>Streamlit Audio Recorder Component</title><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Streamlit Audio Recorder Component"/><link rel="stylesheet" href="bootstrap.min.css"/><link rel="stylesheet" href="./styles.css"/><link href="./static/css/2.bfbf028b.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function t(t){for(var n,l,a=t[0],p=t[1],i=t[2],c=0,s=[];c<a.length;c++)l=a[c],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in p)Object.prototype.hasOwnProperty.call(p,n)&&(e[n]=p[n]);for(f&&f(t);s.length;)s.shift()();return u.push.apply(u,i||[]),r()}function r(){for(var e,t=0;t<u.length;t++){for(var r=u[t],n=!0,a=1;a<r.length;a++){var p=r[a];0!==o[p]&&(n=!1)}n&&(u.splice(t--,1),e=l(l.s=r[0]))}return e}var n={},o={1:0},u=[];function l(t){if(n[t])return n[t].exports;var r=n[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,l),r.l=!0,r.exports}l.m=e,l.c=n,l.d=function(e,t,r){l.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,t){if(1&t&&(e=l(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(l.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)l.d(r,n,function(t){return e[t]}.bind(null,n));return r},l.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(t,"a",t),t},l.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},l.p="./";var a=this.webpackJsonpstreamlit_component_template=this.webpackJsonpstreamlit_component_template||[],p=a.push.bind(a);a.push=t,a=a.slice();for(var i=0;i<a.length;i++)t(a[i]);var f=p;r()}([])</script><script src="./static/js/2.270b84d8.chunk.js"></script><script src="./static/js/main.833ba252.chunk.js"></script></body></html>
lib/st_audiorec/frontend/build/precache-manifest.4829c060d313d0b0d13d9af3b0180289.js ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ self.__precacheManifest = (self.__precacheManifest || []).concat([
2
+ {
3
+ "revision": "de27ef444ab2ed520b64cb0c988a478a",
4
+ "url": "./index.html"
5
+ },
6
+ {
7
+ "revision": "1a47c80c81698454dced",
8
+ "url": "./static/css/2.bfbf028b.chunk.css"
9
+ },
10
+ {
11
+ "revision": "1a47c80c81698454dced",
12
+ "url": "./static/js/2.270b84d8.chunk.js"
13
+ },
14
+ {
15
+ "revision": "3fc7fb5bfeeec1534560a2c962e360a7",
16
+ "url": "./static/js/2.270b84d8.chunk.js.LICENSE.txt"
17
+ },
18
+ {
19
+ "revision": "3478f4c246f37a2cbb97",
20
+ "url": "./static/js/main.833ba252.chunk.js"
21
+ },
22
+ {
23
+ "revision": "7c26bca7e16783d14d15",
24
+ "url": "./static/js/runtime-main.11ec9aca.js"
25
+ }
26
+ ]);
lib/st_audiorec/frontend/build/service-worker.js ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Welcome to your Workbox-powered service worker!
3
+ *
4
+ * You'll need to register this file in your web app and you should
5
+ * disable HTTP caching for this file too.
6
+ * See https://goo.gl/nhQhGp
7
+ *
8
+ * The rest of the code is auto-generated. Please don't update this file
9
+ * directly; instead, make changes to your Workbox build configuration
10
+ * and re-run your build process.
11
+ * See https://goo.gl/2aRDsh
12
+ */
13
+
14
+ importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
15
+
16
+ importScripts(
17
+ "./precache-manifest.4829c060d313d0b0d13d9af3b0180289.js"
18
+ );
19
+
20
+ self.addEventListener('message', (event) => {
21
+ if (event.data && event.data.type === 'SKIP_WAITING') {
22
+ self.skipWaiting();
23
+ }
24
+ });
25
+
26
+ workbox.core.clientsClaim();
27
+
28
+ /**
29
+ * The workboxSW.precacheAndRoute() method efficiently caches and responds to
30
+ * requests for URLs in the manifest.
31
+ * See https://goo.gl/S9QRab
32
+ */
33
+ self.__precacheManifest = [].concat(self.__precacheManifest || []);
34
+ workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
35
+
36
+ workbox.routing.registerNavigationRoute(workbox.precaching.getCacheKeyForURL("./index.html"), {
37
+
38
+ blacklist: [/^\/_/,/\/[^/?]+\.[^/]+$/],
39
+ });
lib/st_audiorec/frontend/build/static/.DS_Store ADDED
Binary file (6.15 kB). View file
 
lib/st_audiorec/frontend/build/static/css/2.bfbf028b.chunk.css ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ ._3ybTi{margin:2em;padding:.5em;border:2px solid #000;font-size:2em;text-align:center}
2
+ /*# sourceMappingURL=2.bfbf028b.chunk.css.map */
lib/st_audiorec/frontend/build/static/css/2.bfbf028b.chunk.css.map ADDED
@@ -0,0 +1 @@
 
 
1
+ {"version":3,"sources":["index.css"],"names":[],"mappings":"AAEA,QACE,UAAW,CACX,YAAc,CACd,qBAAsB,CACtB,aAAc,CACd,iBACF","file":"2.bfbf028b.chunk.css","sourcesContent":["/* add css module styles here (optional) */\n\n._3ybTi {\n margin: 2em;\n padding: 0.5em;\n border: 2px solid #000;\n font-size: 2em;\n text-align: center;\n}\n"]}
lib/st_audiorec/frontend/build/static/js/2.270b84d8.chunk.js ADDED
The diff for this file is too large to render. See raw diff
 
lib/st_audiorec/frontend/build/static/js/2.270b84d8.chunk.js.LICENSE.txt ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ object-assign
3
+ (c) Sindre Sorhus
4
+ @license MIT
5
+ */
6
+
7
+ /**
8
+ * @license
9
+ * Copyright 2018-2021 Streamlit Inc.
10
+ *
11
+ * Licensed under the Apache License, Version 2.0 (the "License");
12
+ * you may not use this file except in compliance with the License.
13
+ * You may obtain a copy of the License at
14
+ *
15
+ * http://www.apache.org/licenses/LICENSE-2.0
16
+ *
17
+ * Unless required by applicable law or agreed to in writing, software
18
+ * distributed under the License is distributed on an "AS IS" BASIS,
19
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
+ * See the License for the specific language governing permissions and
21
+ * limitations under the License.
22
+ */
23
+
24
+ /** @license React v0.19.1
25
+ * scheduler.production.min.js
26
+ *
27
+ * Copyright (c) Facebook, Inc. and its affiliates.
28
+ *
29
+ * This source code is licensed under the MIT license found in the
30
+ * LICENSE file in the root directory of this source tree.
31
+ */
32
+
33
+ /** @license React v16.13.1
34
+ * react-is.production.min.js
35
+ *
36
+ * Copyright (c) Facebook, Inc. and its affiliates.
37
+ *
38
+ * This source code is licensed under the MIT license found in the
39
+ * LICENSE file in the root directory of this source tree.
40
+ */
41
+
42
+ /** @license React v16.14.0
43
+ * react-dom.production.min.js
44
+ *
45
+ * Copyright (c) Facebook, Inc. and its affiliates.
46
+ *
47
+ * This source code is licensed under the MIT license found in the
48
+ * LICENSE file in the root directory of this source tree.
49
+ */
50
+
51
+ /** @license React v16.14.0
52
+ * react.production.min.js
53
+ *
54
+ * Copyright (c) Facebook, Inc. and its affiliates.
55
+ *
56
+ * This source code is licensed under the MIT license found in the
57
+ * LICENSE file in the root directory of this source tree.
58
+ */
lib/st_audiorec/frontend/build/static/js/2.270b84d8.chunk.js.map ADDED
The diff for this file is too large to render. See raw diff
 
lib/st_audiorec/frontend/build/static/js/main.833ba252.chunk.js ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ (this.webpackJsonpstreamlit_component_template=this.webpackJsonpstreamlit_component_template||[]).push([[0],{17:function(t,e,a){t.exports=a(28)},28:function(t,e,a){"use strict";a.r(e);var n=a(6),o=a.n(n),r=a(15),c=a.n(r),i=a(0),l=a(1),s=a(2),u=a(3),d=a(8),p=a(11),m=(a(27),function(t){Object(s.a)(a,t);var e=Object(u.a)(a);function a(){var t;Object(l.a)(this,a);for(var n=arguments.length,r=new Array(n),c=0;c<n;c++)r[c]=arguments[c];return(t=e.call.apply(e,[this].concat(r))).state={isFocused:!1,recordState:null,audioDataURL:"",reset:!1},t.render=function(){var e=t.props.theme,a={},n=t.state.recordState;if(e){var r="1px solid ".concat(t.state.isFocused?e.primaryColor:"gray");a.border=r,a.outline=r}return o.a.createElement("span",null,o.a.createElement("div",null,o.a.createElement("button",{id:"record",onClick:t.onClick_start},"Start Recording"),o.a.createElement("button",{id:"stop",onClick:t.onClick_stop},"Stop"),o.a.createElement("button",{id:"reset",onClick:t.onClick_reset},"Reset"),o.a.createElement("button",{id:"continue",onClick:t.onClick_continue},"Download"),o.a.createElement(p.b,{state:n,onStop:t.onStop_audio,type:"audio/wav",backgroundColor:"rgb(255, 255, 255)",foregroundColor:"rgb(255,76,75)",canvasWidth:450,canvasHeight:100}),o.a.createElement("audio",{id:"audio",controls:!0,src:t.state.audioDataURL})))},t.onClick_start=function(){t.setState({reset:!1,audioDataURL:"",recordState:p.a.START}),d.a.setComponentValue("")},t.onClick_stop=function(){t.setState({reset:!1,recordState:p.a.STOP})},t.onClick_reset=function(){t.setState({reset:!0,audioDataURL:"",recordState:p.a.STOP}),d.a.setComponentValue("")},t.onClick_continue=function(){if(""!==t.state.audioDataURL){var e=(new Date).toLocaleString(),a="streamlit_audio_"+(e=(e=(e=e.replace(" ","")).replace(/_/g,"")).replace(",",""))+".wav",n=document.createElement("a");n.style.display="none",n.href=t.state.audioDataURL,n.download=a,document.body.appendChild(n),n.click()}},t.onStop_audio=function(e){!0===t.state.reset?(t.setState({audioDataURL:""}),d.a.setComponentValue("")):(t.setState({audioDataURL:e.url}),fetch(e.url).then((function(t){return t.blob()})).then((function(t){return new Response(t).arrayBuffer()})).then((function(t){d.a.setComponentValue({arr:new Uint8Array(t)})})))},t}return Object(i.a)(a)}(d.b)),f=Object(d.c)(m);d.a.setComponentReady(),d.a.setFrameHeight(),c.a.render(o.a.createElement(o.a.StrictMode,null,o.a.createElement(f,null)),document.getElementById("root"))}},[[17,1,2]]]);
2
+ //# sourceMappingURL=main.833ba252.chunk.js.map
lib/st_audiorec/frontend/build/static/js/main.833ba252.chunk.js.map ADDED
@@ -0,0 +1 @@
 
 
1
+ {"version":3,"sources":["StreamlitAudioRecorder.tsx","index.tsx"],"names":["StAudioRec","state","isFocused","recordState","audioDataURL","reset","render","theme","props","style","borderStyling","primaryColor","border","outline","id","onClick","onClick_start","onClick_stop","onClick_reset","onClick_continue","onStop","onStop_audio","type","backgroundColor","foregroundColor","canvasWidth","canvasHeight","controls","src","setState","RecordState","START","Streamlit","setComponentValue","STOP","datetime","Date","toLocaleString","filename","replace","a","document","createElement","display","href","download","body","appendChild","click","data","url","fetch","then","ctx","blob","Response","arrayBuffer","buffer","Uint8Array","StreamlitComponentBase","withStreamlitConnection","setComponentReady","setFrameHeight","ReactDOM","StrictMode","getElementById"],"mappings":"wQAiBMA,G,kNACGC,MAAQ,CAAEC,WAAW,EAAOC,YAAa,KAAMC,aAAc,GAAIC,OAAO,G,EAExEC,OAAS,WAMd,IAAQC,EAAU,EAAKC,MAAfD,MACFE,EAA6B,GAE3BN,EAAgB,EAAKF,MAArBE,YAGR,GAAII,EAAO,CAGT,IAAMG,EAAa,oBACjB,EAAKT,MAAMC,UAAYK,EAAMI,aAAe,QAC9CF,EAAMG,OAASF,EACfD,EAAMI,QAAUH,EAGlB,OACE,8BACE,6BACE,4BAAQI,GAAG,SAASC,QAAS,EAAKC,eAAlC,mBAGA,4BAAQF,GAAG,OAAOC,QAAS,EAAKE,cAAhC,QAGA,4BAAQH,GAAG,QAAQC,QAAS,EAAKG,eAAjC,SAIA,4BAAQJ,GAAG,WAAWC,QAAS,EAAKI,kBAApC,YAIA,kBAAC,IAAD,CACElB,MAAOE,EACPiB,OAAQ,EAAKC,aACbC,KAAK,YACLC,gBAAgB,qBAChBC,gBAAgB,iBAChBC,YAAa,IACbC,aAAc,MAGhB,2BACEZ,GAAG,QACHa,UAAQ,EACRC,IAAK,EAAK3B,MAAMG,kB,EASlBY,cAAgB,WACtB,EAAKa,SAAS,CACZxB,OAAO,EACPD,aAAc,GACdD,YAAa2B,IAAYC,QAE3BC,IAAUC,kBAAkB,K,EAGtBhB,aAAe,WACrB,EAAKY,SAAS,CACZxB,OAAO,EACPF,YAAa2B,IAAYI,Q,EAIrBhB,cAAgB,WACtB,EAAKW,SAAS,CACZxB,OAAO,EACPD,aAAc,GACdD,YAAa2B,IAAYI,OAE3BF,IAAUC,kBAAkB,K,EAGtBd,iBAAmB,WACzB,GAAgC,KAA5B,EAAKlB,MAAMG,aACf,CAEE,IAAI+B,GAAW,IAAIC,MAAOC,iBAItBC,EAAW,oBADfH,GADAA,GADAA,EAAWA,EAASI,QAAQ,IAAK,KACbA,QAAQ,KAAM,KACdA,QAAQ,IAAK,KACc,OAGzCC,EAAIC,SAASC,cAAc,KACjCF,EAAE/B,MAAMkC,QAAU,OAClBH,EAAEI,KAAO,EAAK3C,MAAMG,aACpBoC,EAAEK,SAAWP,EACbG,SAASK,KAAKC,YAAYP,GAC1BA,EAAEQ,U,EAIE3B,aAAe,SAAC4B,IACG,IAArB,EAAKhD,MAAMI,OAEb,EAAKwB,SAAS,CACZzB,aAAc,KAEhB4B,IAAUC,kBAAkB,MAE5B,EAAKJ,SAAS,CACZzB,aAAc6C,EAAKC,MAGrBC,MAAMF,EAAKC,KAAKE,MAAK,SAASC,GAC5B,OAAOA,EAAIC,UACVF,MAAK,SAASE,GAGf,OAAQ,IAAIC,SAASD,GAAOE,iBAC3BJ,MAAK,SAASK,GACfzB,IAAUC,kBAAkB,CAC1B,IAAO,IAAIyB,WAAWD,U,yBAhIPE,MA8IVC,cAAwB5D,GAIvCgC,IAAU6B,oBAIV7B,IAAU8B,iBCnKVC,IAASzD,OACP,kBAAC,IAAM0D,WAAP,KACE,kBAAC,EAAD,OAEFvB,SAASwB,eAAe,W","file":"static/js/main.833ba252.chunk.js","sourcesContent":["import {\n Streamlit,\n StreamlitComponentBase,\n withStreamlitConnection,\n} from \"streamlit-component-lib\"\nimport React, { ReactNode } from \"react\"\n\nimport AudioReactRecorder, { RecordState } from 'audio-react-recorder'\nimport 'audio-react-recorder/dist/index.css'\n\ninterface State {\n isFocused: boolean\n recordState: null\n audioDataURL: string\n reset: boolean\n}\n\nclass StAudioRec extends StreamlitComponentBase<State> {\n public state = { isFocused: false, recordState: null, audioDataURL: '', reset: false}\n\n public render = (): ReactNode => {\n // Arguments that are passed to the plugin in Python are accessible\n\n // Streamlit sends us a theme object via props that we can use to ensure\n // that our component has visuals that match the active theme in a\n // streamlit app.\n const { theme } = this.props\n const style: React.CSSProperties = {}\n\n const { recordState } = this.state\n\n // compatibility with older vers of Streamlit that don't send theme object.\n if (theme) {\n // Use the theme object to style our button border. Alternatively, the\n // theme style is defined in CSS vars.\n const borderStyling = `1px solid ${\n this.state.isFocused ? theme.primaryColor : \"gray\"}`\n style.border = borderStyling\n style.outline = borderStyling\n }\n\n return (\n <span>\n <div>\n <button id='record' onClick={this.onClick_start}>\n Start Recording\n </button>\n <button id='stop' onClick={this.onClick_stop}>\n Stop\n </button>\n <button id='reset' onClick={this.onClick_reset}>\n Reset\n </button>\n\n <button id='continue' onClick={this.onClick_continue}>\n Download\n </button>\n\n <AudioReactRecorder\n state={recordState}\n onStop={this.onStop_audio}\n type='audio/wav'\n backgroundColor='rgb(255, 255, 255)'\n foregroundColor='rgb(255,76,75)'\n canvasWidth={450}\n canvasHeight={100}\n />\n\n <audio\n id='audio'\n controls\n src={this.state.audioDataURL}\n />\n\n </div>\n </span>\n )\n }\n\n\n private onClick_start = () => {\n this.setState({\n reset: false,\n audioDataURL: '',\n recordState: RecordState.START\n })\n Streamlit.setComponentValue('')\n }\n\n private onClick_stop = () => {\n this.setState({\n reset: false,\n recordState: RecordState.STOP\n })\n }\n\n private onClick_reset = () => {\n this.setState({\n reset: true,\n audioDataURL: '',\n recordState: RecordState.STOP\n })\n Streamlit.setComponentValue('')\n }\n\n private onClick_continue = () => {\n if (this.state.audioDataURL !== '')\n {\n // get datetime string for filename\n let datetime = new Date().toLocaleString();\n datetime = datetime.replace(' ', '');\n datetime = datetime.replace(/_/g, '');\n datetime = datetime.replace(',', '');\n var filename = 'streamlit_audio_' + datetime + '.wav';\n\n // auromatically trigger download\n const a = document.createElement('a');\n a.style.display = 'none';\n a.href = this.state.audioDataURL;\n a.download = filename;\n document.body.appendChild(a);\n a.click();\n }\n }\n\n private onStop_audio = (data) => {\n if (this.state.reset === true)\n {\n this.setState({\n audioDataURL: ''\n })\n Streamlit.setComponentValue('')\n }else{\n this.setState({\n audioDataURL: data.url\n })\n\n fetch(data.url).then(function(ctx){\n return ctx.blob()\n }).then(function(blob){\n // converting blob to arrayBuffer, this process step needs to be be improved\n // this operation's time complexity scales exponentially with audio length\n return (new Response(blob)).arrayBuffer()\n }).then(function(buffer){\n Streamlit.setComponentValue({\n \"arr\": new Uint8Array(buffer)\n })\n })\n\n }\n\n\n }\n}\n\n// \"withStreamlitConnection\" is a wrapper function. It bootstraps the\n// connection between your component and the Streamlit app, and handles\n// passing arguments from Python -> Component.\n// You don't need to edit withStreamlitConnection (but you're welcome to!).\nexport default withStreamlitConnection(StAudioRec)\n\n// Tell Streamlit we're ready to start receiving data. We won't get our\n// first RENDER_EVENT until we call this function.\nStreamlit.setComponentReady()\n\n// Finally, tell Streamlit to update our initial height. We omit the\n// `height` parameter here to have it default to our scrollHeight.\nStreamlit.setFrameHeight()\n","import React from \"react\"\nimport ReactDOM from \"react-dom\"\nimport StAudioRec from \"./StreamlitAudioRecorder\"\n\nReactDOM.render(\n <React.StrictMode>\n <StAudioRec />\n </React.StrictMode>,\n document.getElementById(\"root\")\n)\n"],"sourceRoot":""}
lib/st_audiorec/frontend/build/static/js/runtime-main.11ec9aca.js ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ !function(e){function t(t){for(var n,l,a=t[0],p=t[1],i=t[2],c=0,s=[];c<a.length;c++)l=a[c],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in p)Object.prototype.hasOwnProperty.call(p,n)&&(e[n]=p[n]);for(f&&f(t);s.length;)s.shift()();return u.push.apply(u,i||[]),r()}function r(){for(var e,t=0;t<u.length;t++){for(var r=u[t],n=!0,a=1;a<r.length;a++){var p=r[a];0!==o[p]&&(n=!1)}n&&(u.splice(t--,1),e=l(l.s=r[0]))}return e}var n={},o={1:0},u=[];function l(t){if(n[t])return n[t].exports;var r=n[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,l),r.l=!0,r.exports}l.m=e,l.c=n,l.d=function(e,t,r){l.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},l.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,t){if(1&t&&(e=l(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(l.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)l.d(r,n,function(t){return e[t]}.bind(null,n));return r},l.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(t,"a",t),t},l.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},l.p="./";var a=this.webpackJsonpstreamlit_component_template=this.webpackJsonpstreamlit_component_template||[],p=a.push.bind(a);a.push=t,a=a.slice();for(var i=0;i<a.length;i++)t(a[i]);var f=p;r()}([]);
2
+ //# sourceMappingURL=runtime-main.11ec9aca.js.map
lib/st_audiorec/frontend/build/static/js/runtime-main.11ec9aca.js.map ADDED
@@ -0,0 +1 @@
 
 
1
+ {"version":3,"sources":["../webpack/bootstrap"],"names":["webpackJsonpCallback","data","moduleId","chunkId","chunkIds","moreModules","executeModules","i","resolves","length","Object","prototype","hasOwnProperty","call","installedChunks","push","modules","parentJsonpFunction","shift","deferredModules","apply","checkDeferredModules","result","deferredModule","fulfilled","j","depId","splice","__webpack_require__","s","installedModules","1","exports","module","l","m","c","d","name","getter","o","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","p","jsonpArray","this","oldJsonpFunction","slice"],"mappings":"aACE,SAASA,EAAqBC,GAQ7B,IAPA,IAMIC,EAAUC,EANVC,EAAWH,EAAK,GAChBI,EAAcJ,EAAK,GACnBK,EAAiBL,EAAK,GAIHM,EAAI,EAAGC,EAAW,GACpCD,EAAIH,EAASK,OAAQF,IACzBJ,EAAUC,EAASG,GAChBG,OAAOC,UAAUC,eAAeC,KAAKC,EAAiBX,IAAYW,EAAgBX,IACpFK,EAASO,KAAKD,EAAgBX,GAAS,IAExCW,EAAgBX,GAAW,EAE5B,IAAID,KAAYG,EACZK,OAAOC,UAAUC,eAAeC,KAAKR,EAAaH,KACpDc,EAAQd,GAAYG,EAAYH,IAKlC,IAFGe,GAAqBA,EAAoBhB,GAEtCO,EAASC,QACdD,EAASU,OAATV,GAOD,OAHAW,EAAgBJ,KAAKK,MAAMD,EAAiBb,GAAkB,IAGvDe,IAER,SAASA,IAER,IADA,IAAIC,EACIf,EAAI,EAAGA,EAAIY,EAAgBV,OAAQF,IAAK,CAG/C,IAFA,IAAIgB,EAAiBJ,EAAgBZ,GACjCiB,GAAY,EACRC,EAAI,EAAGA,EAAIF,EAAed,OAAQgB,IAAK,CAC9C,IAAIC,EAAQH,EAAeE,GACG,IAA3BX,EAAgBY,KAAcF,GAAY,GAE3CA,IACFL,EAAgBQ,OAAOpB,IAAK,GAC5Be,EAASM,EAAoBA,EAAoBC,EAAIN,EAAe,KAItE,OAAOD,EAIR,IAAIQ,EAAmB,GAKnBhB,EAAkB,CACrBiB,EAAG,GAGAZ,EAAkB,GAGtB,SAASS,EAAoB1B,GAG5B,GAAG4B,EAAiB5B,GACnB,OAAO4B,EAAiB5B,GAAU8B,QAGnC,IAAIC,EAASH,EAAiB5B,GAAY,CACzCK,EAAGL,EACHgC,GAAG,EACHF,QAAS,IAUV,OANAhB,EAAQd,GAAUW,KAAKoB,EAAOD,QAASC,EAAQA,EAAOD,QAASJ,GAG/DK,EAAOC,GAAI,EAGJD,EAAOD,QAKfJ,EAAoBO,EAAInB,EAGxBY,EAAoBQ,EAAIN,EAGxBF,EAAoBS,EAAI,SAASL,EAASM,EAAMC,GAC3CX,EAAoBY,EAAER,EAASM,IAClC5B,OAAO+B,eAAeT,EAASM,EAAM,CAAEI,YAAY,EAAMC,IAAKJ,KAKhEX,EAAoBgB,EAAI,SAASZ,GACX,qBAAXa,QAA0BA,OAAOC,aAC1CpC,OAAO+B,eAAeT,EAASa,OAAOC,YAAa,CAAEC,MAAO,WAE7DrC,OAAO+B,eAAeT,EAAS,aAAc,CAAEe,OAAO,KAQvDnB,EAAoBoB,EAAI,SAASD,EAAOE,GAEvC,GADU,EAAPA,IAAUF,EAAQnB,EAAoBmB,IAC/B,EAAPE,EAAU,OAAOF,EACpB,GAAW,EAAPE,GAA8B,kBAAVF,GAAsBA,GAASA,EAAMG,WAAY,OAAOH,EAChF,IAAII,EAAKzC,OAAO0C,OAAO,MAGvB,GAFAxB,EAAoBgB,EAAEO,GACtBzC,OAAO+B,eAAeU,EAAI,UAAW,CAAET,YAAY,EAAMK,MAAOA,IACtD,EAAPE,GAA4B,iBAATF,EAAmB,IAAI,IAAIM,KAAON,EAAOnB,EAAoBS,EAAEc,EAAIE,EAAK,SAASA,GAAO,OAAON,EAAMM,IAAQC,KAAK,KAAMD,IAC9I,OAAOF,GAIRvB,EAAoB2B,EAAI,SAAStB,GAChC,IAAIM,EAASN,GAAUA,EAAOiB,WAC7B,WAAwB,OAAOjB,EAAgB,SAC/C,WAA8B,OAAOA,GAEtC,OADAL,EAAoBS,EAAEE,EAAQ,IAAKA,GAC5BA,GAIRX,EAAoBY,EAAI,SAASgB,EAAQC,GAAY,OAAO/C,OAAOC,UAAUC,eAAeC,KAAK2C,EAAQC,IAGzG7B,EAAoB8B,EAAI,KAExB,IAAIC,EAAaC,KAA+C,yCAAIA,KAA+C,0CAAK,GACpHC,EAAmBF,EAAW5C,KAAKuC,KAAKK,GAC5CA,EAAW5C,KAAOf,EAClB2D,EAAaA,EAAWG,QACxB,IAAI,IAAIvD,EAAI,EAAGA,EAAIoD,EAAWlD,OAAQF,IAAKP,EAAqB2D,EAAWpD,IAC3E,IAAIU,EAAsB4C,EAI1BxC,I","file":"static/js/runtime-main.11ec9aca.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tfunction webpackJsonpCallback(data) {\n \t\tvar chunkIds = data[0];\n \t\tvar moreModules = data[1];\n \t\tvar executeModules = data[2];\n\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [];\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(data);\n\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n\n \t\t// add entry modules from loaded chunk to deferred list\n \t\tdeferredModules.push.apply(deferredModules, executeModules || []);\n\n \t\t// run deferred modules when all chunks ready\n \t\treturn checkDeferredModules();\n \t};\n \tfunction checkDeferredModules() {\n \t\tvar result;\n \t\tfor(var i = 0; i < deferredModules.length; i++) {\n \t\t\tvar deferredModule = deferredModules[i];\n \t\t\tvar fulfilled = true;\n \t\t\tfor(var j = 1; j < deferredModule.length; j++) {\n \t\t\t\tvar depId = deferredModule[j];\n \t\t\t\tif(installedChunks[depId] !== 0) fulfilled = false;\n \t\t\t}\n \t\t\tif(fulfilled) {\n \t\t\t\tdeferredModules.splice(i--, 1);\n \t\t\t\tresult = __webpack_require__(__webpack_require__.s = deferredModule[0]);\n \t\t\t}\n \t\t}\n\n \t\treturn result;\n \t}\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// object to store loaded and loading chunks\n \t// undefined = chunk not loaded, null = chunk preloaded/prefetched\n \t// Promise = chunk loading, 0 = chunk loaded\n \tvar installedChunks = {\n \t\t1: 0\n \t};\n\n \tvar deferredModules = [];\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"./\";\n\n \tvar jsonpArray = this[\"webpackJsonpstreamlit_component_template\"] = this[\"webpackJsonpstreamlit_component_template\"] || [];\n \tvar oldJsonpFunction = jsonpArray.push.bind(jsonpArray);\n \tjsonpArray.push = webpackJsonpCallback;\n \tjsonpArray = jsonpArray.slice();\n \tfor(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);\n \tvar parentJsonpFunction = oldJsonpFunction;\n\n\n \t// run deferred modules from other chunks\n \tcheckDeferredModules();\n"],"sourceRoot":""}
lib/st_audiorec/frontend/build/styles.css ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *{
2
+ margin: 0;
3
+ padding: 0;
4
+ }
5
+
6
+ .container{
7
+ margin: 0 auto;
8
+ text-align: center;
9
+ }
10
+
11
+ .display{
12
+ width: 100%;
13
+ padding: 5px 0;
14
+ margin: 15px 0;
15
+ }
16
+
17
+ .controllers{
18
+ width: 100%;
19
+ padding: 5px 0;
20
+ margin-top: 15px;
21
+ margin-bottom: 35px;
22
+ }
23
+
24
+ button{
25
+ padding-top: 0.25rem;
26
+ padding-bottom: 0.25rem;
27
+ padding-right: 0.75rem;
28
+ padding-left: 0.75rem;
29
+ margin-right: 0.5rem;
30
+ margin-left: 0.1rem;
31
+ font-size: 16px;
32
+ background-color: #ffffff;
33
+ color: #000000;
34
+ border: 1px solid rgba(49, 51, 63, 0.2);
35
+ border-radius: 0.25rem;
36
+ margin-top: 0.75rem;
37
+ margin-bottom: 0.25rem;
38
+ }
39
+
40
+ button:hover{
41
+ padding-top: 0.25rem;
42
+ padding-bottom: 0.25rem;
43
+ padding-right: 0.75rem;
44
+ padding-left: 0.75rem;
45
+ margin-right: 0.5rem;
46
+ margin-left: 0.1rem;
47
+ font-size: 16px;
48
+ background-color: #ffffff;
49
+ color: #ff4c4b;
50
+ border: 1px solid #ff4c4b;
51
+ border-radius: 0.25rem;
52
+ margin-top: 0.75rem;
53
+ margin-bottom: 0.25rem;
54
+ }
55
+
56
+ audio {
57
+ width: 450px;
58
+ height: 45px;
59
+ }
lib/st_audiorec/frontend/package-lock.json ADDED
The diff for this file is too large to render. See raw diff
 
lib/st_audiorec/frontend/package.json ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "streamlit_component_template",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "dependencies": {
6
+ "@types/jest": "^24.0.0",
7
+ "@types/node": "^12.0.0",
8
+ "@types/react": "^16.9.0",
9
+ "@types/react-dom": "^16.9.0",
10
+ "audio-react-recorder": "^1.0.4",
11
+ "axios": "^0.27.2",
12
+ "normalize.css": "^8.0.1",
13
+ "react": "^16.13.1",
14
+ "react-dom": "^16.13.1",
15
+ "react-media-recorder": "^1.6.4",
16
+ "react-scripts": "3.4.1",
17
+ "streamlit-component-lib": "^1.4.0",
18
+ "turbodepot-node": "^7.0.1",
19
+ "typescript": "~3.8.0",
20
+ "use-media-recorder": "^2.0.4"
21
+ },
22
+ "scripts": {
23
+ "start": "react-scripts start",
24
+ "build": "react-scripts build",
25
+ "test": "react-scripts test",
26
+ "eject": "react-scripts eject"
27
+ },
28
+ "eslintConfig": {
29
+ "extends": "react-app"
30
+ },
31
+ "browserslist": {
32
+ "production": [
33
+ ">0.2%",
34
+ "not dead",
35
+ "not op_mini all"
36
+ ],
37
+ "development": [
38
+ "last 1 chrome version",
39
+ "last 1 firefox version",
40
+ "last 1 safari version"
41
+ ]
42
+ },
43
+ "homepage": "."
44
+ }
lib/st_audiorec/frontend/public/.DS_Store ADDED
Binary file (6.15 kB). View file
 
lib/st_audiorec/frontend/public/bootstrap.min.css ADDED
The diff for this file is too large to render. See raw diff
 
lib/st_audiorec/frontend/public/index.html ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <title>Streamlit Audio Recorder Component</title>
5
+ <meta charset="UTF-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+ <meta name="theme-color" content="#000000" />
8
+ <meta name="description" content="Streamlit Audio Recorder Component" />
9
+ <link rel="stylesheet" href="bootstrap.min.css" />
10
+ <link rel="stylesheet" href="./styles.css"/>
11
+ </head>
12
+ <body>
13
+ <noscript>You need to enable JavaScript to run this app.</noscript>
14
+ <!-- div for root element as generated by the react render function -->
15
+ <div id="root"></div>
16
+ <!--
17
+ To begin the development, run `npm start` or `yarn start`.
18
+ To create a production bundle, use `npm run build` or `yarn build`.
19
+ -->
20
+ </body>
21
+ </html>
lib/st_audiorec/frontend/public/styles.css ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *{
2
+ margin: 0;
3
+ padding: 0;
4
+ }
5
+
6
+ .container{
7
+ margin: 0 auto;
8
+ text-align: center;
9
+ }
10
+
11
+ .display{
12
+ width: 100%;
13
+ padding: 5px 0;
14
+ margin: 15px 0;
15
+ }
16
+
17
+ .controllers{
18
+ width: 100%;
19
+ padding: 5px 0;
20
+ margin-top: 15px;
21
+ margin-bottom: 35px;
22
+ }
23
+
24
+ button{
25
+ padding-top: 0.25rem;
26
+ padding-bottom: 0.25rem;
27
+ padding-right: 0.75rem;
28
+ padding-left: 0.75rem;
29
+ margin-right: 0.5rem;
30
+ margin-left: 0.1rem;
31
+ font-size: 16px;
32
+ background-color: #ffffff;
33
+ color: #000000;
34
+ border: 1px solid rgba(49, 51, 63, 0.2);
35
+ border-radius: 0.25rem;
36
+ margin-top: 0.75rem;
37
+ margin-bottom: 0.25rem;
38
+ }
39
+
40
+ button:hover{
41
+ padding-top: 0.25rem;
42
+ padding-bottom: 0.25rem;
43
+ padding-right: 0.75rem;
44
+ padding-left: 0.75rem;
45
+ margin-right: 0.5rem;
46
+ margin-left: 0.1rem;
47
+ font-size: 16px;
48
+ background-color: #ffffff;
49
+ color: #ff4c4b;
50
+ border: 1px solid #ff4c4b;
51
+ border-radius: 0.25rem;
52
+ margin-top: 0.75rem;
53
+ margin-bottom: 0.25rem;
54
+ }
55
+
56
+ audio {
57
+ width: 450px;
58
+ height: 45px;
59
+ }
lib/st_audiorec/frontend/src/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()
lib/st_audiorec/frontend/src/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
+ )
lib/st_audiorec/frontend/src/react-app-env.d.ts ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ /// <reference types="react-scripts" />
2
+ declare module 'audio-react-recorder';
lib/st_audiorec/frontend/tsconfig.json ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es5",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "esModuleInterop": true,
8
+ "allowSyntheticDefaultImports": true,
9
+ "strict": true,
10
+ "strictNullChecks":false,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "module": "esnext",
13
+ "moduleResolution": "node",
14
+ "resolveJsonModule": true,
15
+ "isolatedModules": true,
16
+ "noEmit": true,
17
+ "jsx": "react",
18
+ "noImplicitAny": false,
19
+ },
20
+ "include": ["src"]
21
+ }
lib/st_custom_components.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import numpy as np
3
+ import streamlit as st
4
+ from io import BytesIO
5
+ import streamlit.components.v1 as components
6
+
7
+ def st_audiorec():
8
+
9
+ # get parent directory relative to current directory
10
+ parent_dir = os.path.dirname(os.path.abspath(__file__))
11
+ # Custom REACT-based component for recording client audio in browser
12
+ build_dir = os.path.join(parent_dir, "st_audiorec/frontend/build")
13
+ # specify directory and initialize st_audiorec object functionality
14
+ st_audiorec = components.declare_component("st_audiorec", path=build_dir)
15
+
16
+ # Create an instance of the component: STREAMLIT AUDIO RECORDER
17
+ raw_audio_data = st_audiorec() # raw_audio_data: stores all the data returned from the streamlit frontend
18
+ wav_bytes = None # wav_bytes: contains the recorded audio in .WAV format after conversion
19
+
20
+ # the frontend returns raw audio data in the form of arraybuffer
21
+ # (this arraybuffer is derived from web-media API WAV-blob data)
22
+
23
+ if isinstance(raw_audio_data, dict): # retrieve audio data
24
+ with st.spinner('retrieving audio-recording...'):
25
+ ind, raw_audio_data = zip(*raw_audio_data['arr'].items())
26
+ ind = np.array(ind, dtype=int) # convert to np array
27
+ raw_audio_data = np.array(raw_audio_data) # convert to np array
28
+ sorted_ints = raw_audio_data[ind]
29
+ stream = BytesIO(b"".join([int(v).to_bytes(1, "big") for v in sorted_ints]))
30
+ # wav_bytes contains audio data in byte format, ready to be processed further
31
+ wav_bytes = stream.read()
32
+
33
+ return wav_bytes
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ streamlit==1.18.1
2
+ demucs==4.0.0
3
+ pydub==0.25.1