File size: 11,369 Bytes
b1fdcc2
 
b0a9f8f
 
b1fdcc2
cc1438e
b1fdcc2
5472963
cc1438e
b0a9f8f
 
b1fdcc2
 
b0a9f8f
b1fdcc2
 
9baeeda
72ad181
b1fdcc2
b0a9f8f
 
5472963
b1fdcc2
 
 
 
 
 
 
 
 
 
 
 
b0a9f8f
78dd18a
 
 
 
b0a9f8f
 
 
 
 
 
 
 
 
 
b1fdcc2
b0a9f8f
b1fdcc2
 
 
 
 
b0a9f8f
 
 
 
 
 
 
 
 
 
b1fdcc2
 
 
 
b0a9f8f
 
 
 
 
 
 
 
 
 
 
5e0ccc8
b0a9f8f
 
 
 
 
 
 
78dd18a
4c92361
b0a9f8f
 
b1fdcc2
 
b0a9f8f
 
5472963
b0a9f8f
 
5472963
 
 
b0a9f8f
b1fdcc2
b0a9f8f
b1fdcc2
 
cc7efca
b0a9f8f
b1fdcc2
b0a9f8f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b1fdcc2
 
cc7efca
 
 
 
5472963
 
cc7efca
5472963
cc7efca
 
644834b
3314a1f
644834b
cc7efca
 
 
 
b1fdcc2
 
 
 
 
 
 
8fefa43
 
 
 
 
b1fdcc2
b0a9f8f
b1fdcc2
 
 
 
 
cc1438e
b0a9f8f
 
 
cc1438e
444aecb
 
 
 
cc1438e
 
 
 
444aecb
 
 
 
 
 
644834b
cc1438e
444aecb
 
 
9baeeda
cc1438e
99a6014
b1fdcc2
 
 
78dd18a
b1fdcc2
 
 
 
 
 
 
78dd18a
b1fdcc2
 
 
b0a9f8f
b1fdcc2
 
 
 
 
99a6014
b1fdcc2
 
 
 
 
 
444aecb
b1fdcc2
 
 
 
 
 
444aecb
b1fdcc2
 
 
 
 
2849148
 
 
 
8fefa43
 
 
b1fdcc2
8fefa43
 
 
 
b1fdcc2
b0a9f8f
b1fdcc2
 
 
 
 
 
 
 
 
b0a9f8f
b1fdcc2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8fefa43
 
 
 
b0a9f8f
 
b1fdcc2
 
 
 
 
 
72ad181
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
import os
from pathlib import Path
from typing import List
from loguru import logger as log

import requests
import streamlit as st
from streamlit_option_menu import option_menu

from footer import footer
from header import header
from helpers import (
    load_audio_segment,
    load_list_of_songs,
    plot_audio,
    url_is_valid,
    file_size_is_valid,
    delete_old_files,
)
from service.demucs_runner import separator
from service.vocal_remover.runner import load_model, separate
from style import CSS_TABS


label_sources = {
    "no_vocals.mp3": "๐ŸŽถ Instrumental",
    "vocals.mp3": "๐ŸŽค Vocals",
    "drums.mp3": "๐Ÿฅ Drums",
    "bass.mp3": "๐ŸŽธ Bass",
    "guitar.mp3": "๐ŸŽธ Guitar",
    "piano.mp3": "๐ŸŽน Piano",
    "other.mp3": "๐ŸŽถ Other",
}

separation_mode_to_model = {
    "Vocals & Instrumental (Low Quality, Faster)": (
        "vocal_remover",
        ["vocals.mp3", "no_vocals.mp3"],
    ),
    "Vocals & Instrumental (High Quality, Slower)": ("htdemucs", ["vocals.mp3", "no_vocals.mp3"]),
    "Vocals, Drums, Bass & Other (Slower)": (
        "htdemucs",
        ["vocals.mp3", "drums.mp3", "bass.mp3", "other.mp3"],
    ),
    "Vocal, Drums, Bass, Guitar, Piano & Other (Slowest)": (
        "htdemucs_6s",
        ["vocals.mp3", "drums.mp3", "bass.mp3", "guitar.mp3", "piano.mp3", "other.mp3"],
    ),
}

extensions = ["mp3", "wav", "ogg", "flac"]

out_path = Path("/tmp")
in_path = Path("/tmp")


@st.cache_data(show_spinner=False)
def get_sources(path, file_sources):
    sources = {}
    for file in file_sources:
        fullpath = path / file
        if fullpath.exists():
            sources[file] = fullpath
    return sources


def reset_execution():
    st.session_state.executed = False


def show_results(model_name: str, dir_name_output: str, file_sources: List):
    sources = get_sources(out_path / Path(model_name) / dir_name_output, file_sources)
    tab_sources = st.tabs([f"**{label_sources.get(k)}**" for k in sources.keys()])
    for i, (file, pathname) in enumerate(sources.items()):
        with tab_sources[i]:
            cols = st.columns(2)
            with cols[0]:
                auseg = load_audio_segment(pathname, "mp3")
                st.image(
                    plot_audio(
                        auseg,
                        32767,
                        file=file,
                        model_name=model_name,
                        dir_name_output=dir_name_output,
                    ),
                    use_column_width="always",
                )
            with cols[1]:
                st.audio(str(pathname))
    log.info(f"Displaying results for {dir_name_output} - {model_name}")


def body():
    filename = None
    name_song = None
    st.markdown(
        "<h4><center>Extract Vocals & Instrumental from any song</center></h4>",
        unsafe_allow_html=True,
    )
    st.markdown(CSS_TABS, unsafe_allow_html=True)

    cols = st.columns([1, 4, 1, 3, 1])

    with cols[1]:
        with st.columns([1, 8, 1])[1]:
            option = option_menu(
                menu_title=None,
                options=["Examples", "Upload File", "From URL"],
                icons=["cloud-upload-fill", "link-45deg", "music-note-list"],
                orientation="horizontal",
                styles={
                    "container": {
                        "width": "100%",
                        "height": "3.5rem",
                        "margin": "0px",
                        "padding": "0px",
                    },
                    "icon": {"font-size": "1rem"},
                    "nav-link": {
                        "display": "flex",
                        "height": "3rem",
                        "justify-content": "center",
                        "align-items": "center",
                        "text-align": "center",
                        "flex-direction": "column",
                        "font-size": "1rem",
                        "padding-left": "0px",
                        "padding-right": "0px",
                    },
                },
                key="option_separate",
            )
        if option == "Examples":
            samples_song = load_list_of_songs(path="separate_songs.json")
            if samples_song is not None:
                name_song = st.selectbox(
                    label="Select a sample song and listen to sources separated",
                    options=list(samples_song.keys()) + [""],
                    format_func=lambda x: x.replace("_", " "),
                    index=len(samples_song),
                    key="select_example",
                )
                full_path = f"{in_path}/{name_song}"
                if name_song != "" and Path(full_path).exists():
                    st.audio(full_path)
                else:
                    name_song = None

        elif option == "Upload File":
            uploaded_file = st.file_uploader(
                "Choose a file",
                type=extensions,
                key="file",
                help="Supported formats: mp3, wav, ogg, flac.",
            )
            if uploaded_file is not None:
                with st.spinner("Loading audio..."):
                    with open(in_path / uploaded_file.name, "wb") as f:
                        f.write(uploaded_file.getbuffer())
                    filename = uploaded_file.name
                    st.audio(uploaded_file)

        elif option == "From URL":
            url = st.text_input(
                "Paste the URL of the audio file",
                key="url_input",
                help="Supported formats: mp3, wav, ogg, flac.",
            )

            if url != "" and url_is_valid(url):
                with st.spinner("Downloading audio..."):
                    filename = url.split("/")[-1]
                    response = requests.get(url, stream=True)
                    if response.status_code == 200 and file_size_is_valid(
                        response.headers.get("Content-Length")
                    ):
                        file_size = 0
                        with open(in_path / filename, "wb") as audio_file:
                            for chunk in response.iter_content(chunk_size=1024):
                                if chunk:
                                    audio_file.write(chunk)
                                    file_size += len(chunk)
                                    if not file_size_is_valid(file_size):
                                        audio_file.close()
                                        os.remove(in_path / filename)
                                        filename = None
                                        return
                        st.audio(f"{in_path}/{filename}")
                    else:
                        st.error(
                            "Failed to download audio file. Try to download it manually and upload it."
                        )
                        filename = None

    with cols[3]:
        separation_mode = st.selectbox(
            "Choose the separation mode",
            [
                "Vocals & Instrumental (Low Quality, Faster)",
                "Vocals & Instrumental (High Quality, Slower)",
                "Vocals, Drums, Bass & Other (Slower)",
                "Vocal, Drums, Bass, Guitar, Piano & Other (Slowest)",
            ],
            on_change=reset_execution(),
            key="separation_mode",
        )
        if separation_mode == "Vocals & Instrumental (Low Quality, Faster)":
            max_duration = 30
        else:
            max_duration = 15
        model_name, file_sources = separation_mode_to_model[separation_mode]

    if filename is not None:
        song = load_audio_segment(in_path / filename, filename.split(".")[-1])
        n_secs = round(len(song) / 1000)
        if os.environ.get("ENV_LIMITATION", False):
            with cols[3]:
                start_time = st.number_input(
                    "Choose the start time",
                    min_value=0,
                    max_value=n_secs,
                    step=1,
                    value=0,
                    help=f"Maximum duration is {max_duration} seconds for this separation mode.\nDuplicate this space to [remove any limit](https://github.com/fabiogra/moseca#are-there-any-limitations).",
                    format="%d",
                )
                st.session_state.start_time = start_time
                end_time = min(start_time + max_duration, n_secs)
                song = song[start_time * 1000 : end_time * 1000]
                st.info(
                    f"Audio source will be processed from {start_time} to {end_time} seconds.\nDuplicate this space to [remove any limit](https://github.com/fabiogra/moseca#are-there-any-limitations).",
                    icon="โฑ",
                )
        else:
            start_time = 0
            end_time = n_secs
        with st.columns([2, 1, 2])[1]:
            execute = st.button(
                "Separate Music Sources ๐ŸŽถ", type="primary", use_container_width=True
            )
        if execute or st.session_state.executed:
            if execute:
                st.session_state.executed = False

            if not st.session_state.executed:
                log.info(f"{option} - Separating {filename} with {separation_mode}...")
                song.export(in_path / filename, format=filename.split(".")[-1])
                with st.columns([1, 1, 1])[1]:
                    with st.spinner("Separating source audio, it will take a while..."):
                        if model_name == "vocal_remover":
                            model, device = load_model(pretrained_model="baseline.pth")
                            separate(
                                input=in_path / filename,
                                model=model,
                                device=device,
                                output_dir=out_path,
                            )
                        else:
                            stem = None
                            if separation_mode == "Vocals & Instrumental (High Quality, Slower)":
                                stem = "vocals"

                            separator(
                                tracks=[in_path / filename],
                                out=out_path,
                                model=model_name,
                                shifts=1,
                                overlap=0.5,
                                stem=stem,
                                int24=False,
                                float32=False,
                                clip_mode="rescale",
                                mp3=True,
                                mp3_bitrate=320,
                                verbose=True,
                                start_time=start_time,
                                end_time=end_time,
                            )
            dir_name_output = ".".join(filename.split(".")[:-1])
            filename = None
            st.session_state.executed = True
            show_results(model_name, dir_name_output, file_sources)
    elif name_song is not None and option == "Examples":
        show_results(model_name, name_song, file_sources)


if __name__ == "__main__":
    header()
    body()
    footer()
    delete_old_files("/tmp", 60 * 30)