deepsync commited on
Commit
cf872ac
·
verified ·
1 Parent(s): e73e9bc

Create audio_sep_splitter.py

Browse files
Files changed (1) hide show
  1. audio_sep_splitter.py +247 -0
audio_sep_splitter.py ADDED
@@ -0,0 +1,247 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/python3
2
+
3
+ # Copyright (c) 2021 LALAL.AI
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+
24
+ import cgi
25
+ import json
26
+ import os
27
+ import sys
28
+ import time
29
+ from argparse import ArgumentParser
30
+ from urllib.parse import quote, unquote, urlencode
31
+ from urllib.request import urlopen, Request
32
+ from dotenv import load_dotenv
33
+
34
+
35
+ CURRENT_DIR_PATH = os.path.dirname(os.path.realpath(__file__))
36
+ URL_API = "https://www.lalal.ai/api/"
37
+
38
+
39
+ def update_percent(pct):
40
+ pct = str(pct)
41
+ sys.stdout.write("\b" * len(pct))
42
+ sys.stdout.write(" " * len(pct))
43
+ sys.stdout.write("\b" * len(pct))
44
+ sys.stdout.write(pct)
45
+ sys.stdout.flush()
46
+
47
+
48
+ def make_content_disposition(filename, disposition="attachment"):
49
+ try:
50
+ filename.encode("ascii")
51
+ file_expr = f'filename="{filename}"'
52
+ except UnicodeEncodeError:
53
+ quoted = quote(filename)
54
+ file_expr = f"filename*=utf-8''{quoted}"
55
+ return f"{disposition}; {file_expr}"
56
+
57
+
58
+ def upload_file(file_path, license):
59
+ url_for_upload = URL_API + "upload/"
60
+ _, filename = os.path.split(file_path)
61
+ headers = {
62
+ "Content-Disposition": make_content_disposition(filename),
63
+ "Authorization": f"license {license}",
64
+ }
65
+ with open(file_path, "rb") as f:
66
+ request = Request(url_for_upload, f, headers)
67
+ with urlopen(request) as response:
68
+ upload_result = json.load(response)
69
+ if upload_result["status"] == "success":
70
+ return upload_result["id"]
71
+ else:
72
+ raise RuntimeError(upload_result["error"])
73
+
74
+
75
+ def split_file(file_id, license, stem, filter_type, splitter):
76
+ url_for_split = URL_API + "split/"
77
+ headers = {
78
+ "Authorization": f"license {license}",
79
+ }
80
+ query_args = {
81
+ "id": file_id,
82
+ "stem": stem,
83
+ "filter": filter_type,
84
+ "splitter": splitter,
85
+ }
86
+ encoded_args = urlencode(query_args).encode("utf-8")
87
+ request = Request(url_for_split, encoded_args, headers=headers)
88
+ with urlopen(request) as response:
89
+ split_result = json.load(response)
90
+ if split_result["status"] == "error":
91
+ raise RuntimeError(split_result["error"])
92
+
93
+
94
+ def check_file(file_id):
95
+ url_for_check = URL_API + "check/?"
96
+ query_args = {"id": file_id}
97
+ encoded_args = urlencode(query_args)
98
+
99
+ is_queueup = False
100
+
101
+ while True:
102
+ with urlopen(url_for_check + encoded_args) as response:
103
+ check_result = json.load(response)
104
+
105
+ if check_result["status"] == "error":
106
+ raise RuntimeError(check_result["error"])
107
+
108
+ task_state = check_result["task"]["state"]
109
+
110
+ if task_state == "error":
111
+ raise RuntimeError(check_result["task"]["error"])
112
+
113
+ if task_state == "progress":
114
+ progress = int(check_result["task"]["progress"])
115
+ if progress == 0 and not is_queueup:
116
+ print("Queue up...")
117
+ is_queueup = True
118
+ elif progress > 0:
119
+ update_percent(f"Progress: {progress}%")
120
+
121
+ if task_state == "success":
122
+ update_percent("Progress: 100%\n")
123
+ stem_track_url = check_result["split"]["stem_track"]
124
+ back_track_url = check_result["split"]["back_track"]
125
+ return stem_track_url, back_track_url
126
+
127
+ time.sleep(15)
128
+
129
+
130
+ def get_filename_from_content_disposition(header):
131
+ _, params = cgi.parse_header(header)
132
+ filename = params.get("filename")
133
+ if filename:
134
+ return filename
135
+ filename = params.get("filename*")
136
+ if filename:
137
+ encoding, quoted = filename.split("''")
138
+ unquoted = unquote(quoted, encoding)
139
+ return unquoted
140
+ raise ValueError("Invalid header Content-Disposition")
141
+
142
+
143
+ def download_file(url_for_download, output_path):
144
+ with urlopen(url_for_download) as response:
145
+ filename = get_filename_from_content_disposition(
146
+ response.headers["Content-Disposition"]
147
+ )
148
+ file_path = os.path.join(output_path, filename)
149
+ with open(file_path, "wb") as f:
150
+ while True:
151
+ chunk = response.read(8196)
152
+ if not chunk:
153
+ break
154
+ f.write(chunk)
155
+ return file_path
156
+
157
+
158
+ def batch_process_for_file(input_path, output_path, stem, filter_type, splitter):
159
+ load_dotenv("/home/airflow/utils/deepsync_dub_utils/.env.lalalai")
160
+ license = os.environ.get("LALALAI_LICENCE")
161
+
162
+ try:
163
+ print(f'Uploading the file "{input_path}"...')
164
+ file_id = upload_file(file_path=input_path, license=license)
165
+ print(
166
+ f'The file "{input_path}" has been successfully uploaded (file id: {file_id})'
167
+ )
168
+
169
+ print(f'Processing the file "{input_path}"...')
170
+ split_file(file_id, license, stem, filter_type, splitter)
171
+ stem_track_url, back_track_url = check_file(file_id)
172
+
173
+ print(f'Downloading the stem track file "{stem_track_url}"...')
174
+ downloaded_file = download_file(stem_track_url, output_path)
175
+ print(f'The stem track file has been downloaded to "{downloaded_file}"')
176
+
177
+ print(f'Downloading the back track file "{back_track_url}"...')
178
+ downloaded_file = download_file(back_track_url, output_path)
179
+ print(f'The back track file has been downloaded to "{downloaded_file}"')
180
+
181
+ print(f'The file "{input_path}" has been successfully split')
182
+ except Exception as err:
183
+ print(f'Cannot process the file "{input_path}": {err}')
184
+
185
+
186
+ def batch_process(input_path, output_path, stem, filter_type, splitter):
187
+ if os.path.isfile(input_path):
188
+ batch_process_for_file(input_path, output_path, stem, filter_type, splitter)
189
+ else:
190
+ for path in os.listdir(input_path):
191
+ path = os.path.join(input_path, path)
192
+ if os.path.isfile(path):
193
+ batch_process_for_file(path, output_path, stem, filter_type, splitter)
194
+
195
+
196
+ def main():
197
+ parser = ArgumentParser(description="Lalalai splitter")
198
+ parser.add_argument(
199
+ "--input", type=str, required=True, help="Input directory or a file"
200
+ )
201
+ parser.add_argument(
202
+ "--output", type=str, default=CURRENT_DIR_PATH, help="Output directory"
203
+ )
204
+ parser.add_argument(
205
+ "--stem",
206
+ type=str,
207
+ default="vocals",
208
+ choices=[
209
+ "vocals",
210
+ "drum",
211
+ "bass",
212
+ "piano",
213
+ "electric_guitar",
214
+ "acoustic_guitar",
215
+ "synthesizer",
216
+ "voice",
217
+ "strings",
218
+ "wind",
219
+ ],
220
+ help='Stem option. Stems "voice", "strings", "wind" are not supported by Cassiopeia',
221
+ )
222
+ parser.add_argument(
223
+ "--filter",
224
+ type=int,
225
+ default=1,
226
+ choices=[0, 1, 2],
227
+ help="0 (mild), 1 (normal), 2 (aggressive)",
228
+ )
229
+ parser.add_argument(
230
+ "--splitter",
231
+ type=str,
232
+ default="phoenix",
233
+ choices=["phoenix", "cassiopeia"],
234
+ help="The type of neural network used to split audio",
235
+ )
236
+
237
+ args = parser.parse_args()
238
+
239
+ os.makedirs(args.output, exist_ok=True)
240
+ batch_process(args.input, args.output, args.stem, args.filter, args.splitter)
241
+
242
+
243
+ if __name__ == "__main__":
244
+ try:
245
+ main()
246
+ except Exception as err:
247
+ print(err)