Taino commited on
Commit
ab63519
·
1 Parent(s): dfcd1df

Delete app-streamlit.py

Browse files
Files changed (1) hide show
  1. app-streamlit.py +0 -280
app-streamlit.py DELETED
@@ -1,280 +0,0 @@
1
- import os, sys, re, json
2
- import argparse
3
- import shutil
4
- import warnings
5
- import whisper_timestamped as wt
6
- from pdb import set_trace as b
7
- from pprint import pprint as pp
8
- from profanity_check import predict, predict_prob
9
- from pydub import AudioSegment
10
- from pydub.playback import play
11
- from subprocess import Popen, PIPE
12
-
13
- def parse_args():
14
- """
15
- """
16
- parser = argparse.ArgumentParser(
17
- description=('Tool to mute profanities in a song (source separation -> speech recognition -> profanity detection -> mask profanities -> re-mix)'),
18
- usage=('see <py main.py --help> or run as local web app with streamlit: <streamlit run main.py>')
19
- )
20
-
21
- parser.add_argument(
22
- '-i',
23
- '--input',
24
- default=None,
25
- nargs='?',
26
- #required=True,
27
- help=("path to a mp3")
28
- )
29
- parser.add_argument(
30
- '-m',
31
- '--model',
32
- default='small',
33
- nargs='?',
34
- help=("model used by whisper for speech recognition: tiny, small (default) or medium")
35
- )
36
- parser.add_argument(
37
- '-p',
38
- '--play',
39
- default=False,
40
- action='store_true',
41
- help=("play output audio at the end")
42
- )
43
- parser.add_argument(
44
- '-v',
45
- '--verbose',
46
- default=True,
47
- action='store_true',
48
- help=("print transcribed text and detected profanities to screen")
49
- )
50
- return parser.parse_args()
51
-
52
-
53
- def main(args, input_file=None, model_size=None, verbose=False, play_output=False, skip_ss=False):
54
- """
55
- """
56
- if not input_file:
57
- input_file = args.input
58
-
59
- if not model_size:
60
- model_size = args.model
61
-
62
- if not verbose:
63
- verbose = args.verbose
64
-
65
- if not play_output:
66
- play_output = args.play
67
-
68
- # exit if input file not found
69
- if len(sys.argv)>1 and not os.path.isfile(input_file):
70
- print('Error: --input file not found')
71
- raise Exception
72
-
73
- print(f'\nProcessing input file: {input_file}')
74
-
75
- if not skip_ss:
76
- # split audio into vocals + accompaniment
77
- print('Running source separation')
78
- stems_dir = source_separation(input_file, use_demucs=False, use_spleeter=True)
79
- vocal_stem = os.path.join(stems_dir, 'vocals.wav')
80
- #instr_stem = os.path.join(stems_dir, 'no_vocals.wav') # demucs
81
- instr_stem = os.path.join(stems_dir, 'accompaniment.wav') # spleeter
82
- print(f'Vocal stem written to: {vocal_stem}')
83
- else:
84
- vocal_stem = input_file
85
- instr_stem = None
86
-
87
- audio = wt.load_audio(vocal_stem)
88
- model = wt.load_model(model_size, device='cpu')
89
- text = wt.transcribe(model, audio, language='en')
90
-
91
- if verbose:
92
- print('\nTranscribed text:')
93
- print(text['text']+'\n')
94
-
95
- # checking for profanities in text
96
- print('Run profanity detection on text')
97
- profanities = profanity_detection(text)
98
- if not profanities:
99
- print(f'No profanities found in {input_file} - exiting')
100
- return 'No profanities found', None, None
101
-
102
- if verbose:
103
- print('profanities found in text:')
104
- pp(profanities)
105
-
106
- # masking
107
- print('Mask profanities in vocal stem')
108
- vocals = mask_profanities(vocal_stem, profanities)
109
-
110
- # re-mixing
111
- print('Merge instrumentals stem and masked vocals stem')
112
- if not skip_ss:
113
- mix = AudioSegment.from_wav(instr_stem).overlay(vocals)
114
- else:
115
- mix = vocals
116
-
117
- # write mix to file
118
- outpath = input_file.replace('.mp3', '_masked.mp3').replace('.wav', '_masked.wav')
119
- if input_file.endswith('.wav'):
120
- mix.export(outpath, format="wav")
121
- elif input_file.endswith('.mp3'):
122
- mix.export(outpath, format="mp3")
123
- print(f'Mixed file written to: {outpath}')
124
-
125
- # play output
126
- if play_output:
127
- print('\nPlaying output...')
128
- play(mix)
129
-
130
- return outpath, vocal_stem, instr_stem
131
-
132
-
133
- def source_separation(inpath, use_demucs=False, use_spleeter=True):
134
- """
135
- Execute shell command to run demucs and pipe stdout/stderr back to python
136
- """
137
- infile = os.path.basename(inpath)
138
-
139
- if use_demucs:
140
- cmd = f'demucs --two-stems=vocals --jobs 8 "{inpath}"'
141
- #stems_dir = os.path.join(re.findall('/.*', stdout)[0], infile.replace('.mp3','').replace('.wav',''))
142
- elif use_spleeter:
143
- outdir = 'audio/separated'
144
- cmd = f'spleeter separate {inpath} -p spleeter:2stems -o {outdir}'
145
- stems_dir = os.path.join(outdir, os.path.splitext(infile)[0])
146
-
147
- stdout, stderr = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True, executable='/bin/bash').communicate()
148
- stdout = stdout.decode('utf8')
149
-
150
- # exit if lib error'd out
151
- if stderr:
152
- stderr = stderr.decode('utf-8').lower()
153
- if 'error' in stderr or 'not exist' in stderr:
154
- print(stderr.decode('utf8').split('\n')[0])
155
- raise Exception
156
-
157
- # parse stems directory path from stdout and return it if successful
158
- if not os.path.isdir(stems_dir):
159
- print(f'Error: output stem directory "{stems_dir}" not found')
160
- raise Exception
161
-
162
- return stems_dir
163
-
164
-
165
- def profanity_detection(text):
166
- """
167
- """
168
- # detect profanities in text
169
- profs = []
170
- for segment in text['segments']:
171
- for word in segment['words']:
172
- #if word['confidence']<.25:
173
- # print(word)
174
- text = word['text'].replace('.','').replace(',','').lower()
175
-
176
- # skip false positives
177
- if text in ['cancer','hell','junk','die','lame','freak','freaky','white','stink','shut','spit','mouth','orders','eat','clouds','ugly','dirty','wet']:
178
- continue
179
-
180
- # assume anything returned by whisper with more than 1 * is profanity e.g n***a
181
- if '**' in text:
182
- profs.append(word)
183
- continue
184
-
185
- # add true negatives
186
- if text in ['bitchy', 'puss']:
187
- profs.append(word)
188
- continue
189
-
190
- # run profanity detection - returns 1 (True) or 0 (False)
191
- if predict([word['text']])[0]:
192
- profs.append(word)
193
-
194
- return profs
195
-
196
-
197
- def mask_profanities(vocal_stem, profanities):
198
- """
199
- """
200
- # load vocal stem and mask profanities
201
- vocals = AudioSegment.from_wav(vocal_stem)
202
- for prof in profanities:
203
- mask = vocals[prof['start']*1000:prof['end']*1000] # pydub works in milliseconds
204
- mask -= 50 # reduce lvl by some dB (enough to ~mute it)
205
- #mask = mask.silent(len(mask))
206
- #mask = mask.fade_in(100).fade_out(100) # it prepends/appends fades so end up with longer mask
207
- start = vocals[:prof['start']*1000]
208
- end = vocals[prof['end']*1000:]
209
- #print(f"masking {prof['text']} from {prof['start']} to {prof['end']}")
210
- vocals = start + mask + end
211
-
212
- return vocals
213
-
214
-
215
- if __name__ == "__main__":
216
- args = parse_args()
217
-
218
- if len(sys.argv)>1:
219
- main(args, skip_ss=False)
220
- else:
221
- import streamlit as st
222
- st.title('Saylss')
223
- with st.expander("About", expanded=False):
224
- st.markdown('''
225
- This app processes an input audio track (.mp3 or .wav) with the purpose of identifying and muting profanities in the song.
226
-
227
- A larger model takes longer to run and is more accurate, and vice-versa.
228
- Simply select the model size and upload your file!
229
- ''')
230
- model = st.selectbox('Choose model size:', ('tiny','small','medium'), index=1)
231
-
232
- uploaded_file = st.file_uploader(
233
- "Choose input track:",
234
- type=[".mp3",".wav"],
235
- accept_multiple_files=False,
236
- )
237
-
238
- if uploaded_file is not None:
239
- uploaded_file.name = uploaded_file.name.replace(' ','_')
240
- ext = os.path.splitext(uploaded_file.name)[1]
241
- if ext == '.wav':
242
- st_format = 'audio/wav'
243
- elif ext == '.mp3':
244
- st_format = 'audio/mp3'
245
-
246
- uploaded_file_content = uploaded_file.getvalue()
247
- with open(uploaded_file.name, 'wb') as f:
248
- f.write(uploaded_file_content)
249
-
250
- audio_bytes_input = uploaded_file_content
251
- st.audio(audio_bytes_input, format=st_format)
252
-
253
- # run code
254
- with st.spinner('Processing input audio...'):
255
- inpath = os.path.abspath(uploaded_file.name)
256
- outpath, vocal_stem, instr_stem = main(args, input_file=inpath, model_size=model)
257
-
258
- if outpath == 'No profanities found':
259
- st.text(outpath + ' - Refresh the page and try a different song or model size')
260
- sys.exit()
261
-
262
- # display output audio
263
- #st.text('Play output Track:')
264
- st.text('\nOutput:')
265
- audio_file = open(outpath, 'rb')
266
- audio_bytes = audio_file.read()
267
- st.audio(audio_bytes, format=st_format)
268
-
269
- # flush all media
270
- if os.path.isfile(inpath):
271
- os.remove(inpath)
272
- if os.path.isfile(outpath):
273
- os.remove(outpath)
274
- if os.path.isfile(vocal_stem):
275
- os.remove(vocal_stem)
276
- if os.path.isfile(instr_stem):
277
- os.remove(instr_stem)
278
- sep_dir = os.path.split(instr_stem)[0]
279
- if os.path.isdir(sep_dir):
280
- os.rmdir(sep_dir)