File size: 4,942 Bytes
af8e0fa
 
 
b12171d
ff5958c
70b3168
af8e0fa
 
 
d14d2b9
 
af8e0fa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62ea0b2
aab2da5
 
 
d09f276
 
 
 
70b3168
 
 
aab2da5
d09f276
 
70b3168
 
 
 
 
256bf62
 
aab2da5
 
 
 
 
 
 
 
 
 
d09f276
aab2da5
b12171d
d09f276
aab2da5
62ea0b2
 
 
d09f276
00b2148
d09f276
b12171d
 
70b3168
af8e0fa
 
 
 
b4e60ac
af8e0fa
 
 
 
 
 
 
3ea06c2
9a36366
00b2148
a07d0b1
b12171d
af8e0fa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b12171d
 
 
a07d0b1
af8e0fa
 
b12171d
 
 
 
af8e0fa
 
b12171d
af8e0fa
 
 
 
 
 
b12171d
af8e0fa
 
 
 
b12171d
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
import gradio as gr
import os
import allin1
import zipfile
from subprocess import Popen
from concurrent.futures import ThreadPoolExecutor

from pathlib import Path

from dissector import generate_dissector_data

HEADER = """
<header style="text-align: center;">
  <h1>
    All-In-One Music Structure Analyzer 🔮
  </h1>
  <p>
    <a href="https://github.com/mir-aidj/all-in-one">[Python Package]</a>
    <a href="https://arxiv.org/abs/2307.16425">[Paper]</a>
    <a href="https://taejun.kim/music-dissector/">[Visual Demo]</a>
  </p>
</header>
<main
  style="display: flex; justify-content: center;"
>
  <div
    style="display: inline-block;"
  >
    <p>
      This Space demonstrates the music structure analyzer predicts:
      <ul
        style="padding-left: 1rem;"
      >
        <li>BPM</li>
        <li>Beats</li>
        <li>Downbeats</li>
        <li>Functional segment boundaries</li>
        <li>Functional segment labels (e.g. intro, verse, chorus, bridge, outro)</li>
      </ul>
    </p>
    <p>
      It takes about 4:30 to analyze a 3-minute song.
      If you are in a hurry, you can <strong><u>try the examples at the bottom</u></strong>.
    </p>
    <p>
      For more information, please visit the links above ✨🧸
    </p>
  </div>
</main>
"""

CACHE_EXAMPLES = os.getenv('CACHE_EXAMPLES', '1') == '1'

BASIC_PITCH_DIR = "basic-pitch"


def compress_files(demucs_output_path, dissector_file, original_audio):
    """Compresses specific files in the specified folder into a .zip file"""
    # List of the files to be compressed
    wav_files = ['bass.wav', 'drums.wav', 'other.wav', 'vocals.wav']
    mp3_files = [file.replace('.wav', '.mp3') for file in wav_files]

    def convert_to_mp3(wav, mp3):
        """Utility function to run the ffmpeg command"""
        command = ["ffmpeg", "-i", os.path.join(demucs_output_path, wav), os.path.join(demucs_output_path, mp3)]
        process = Popen(command)
        process.communicate()

    # Use ThreadPoolExecutor to convert .wav files to .mp3 in parallel
    with ThreadPoolExecutor(max_workers=8) as executor:
        list(executor.map(convert_to_mp3, wav_files, mp3_files))

    if not os.path.exists(BASIC_PITCH_DIR):
        os.makedirs(BASIC_PITCH_DIR)
    command = [
        "basic-pitch",
        BASIC_PITCH_DIR,
        os.path.join(demucs_output_path, 'bass.wav'),
        os.path.join(demucs_output_path, 'vocals.wav'),
        os.path.join(demucs_output_path, 'other.wav')
    ]
    process = Popen(command)
    process.communicate()

    # Compress the files
    zip_path = demucs_output_path + ".zip"
    with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
        for mp3 in mp3_files:
            zipf.write(os.path.join(demucs_output_path, mp3), arcname=mp3)
        zipf.write(f'{BASIC_PITCH_DIR}/bass_basic_pitch.mid', arcname='bass_basic_pitch.mid')
        zipf.write(f'{BASIC_PITCH_DIR}/vocals_basic_pitch.mid', arcname='vocals_basic_pitch.mid')
        zipf.write(f'{BASIC_PITCH_DIR}/other_basic_pitch.mid', arcname='other_basic_pitch.mid')
        zipf.write(dissector_file, arcname='dissector.json')  # Fixed name
        zipf.write(original_audio, arcname='mixdown.mp3')  # Added original audio
    
    return zip_path


def analyze(path):
  path = Path(path)
  result = allin1.analyze(
    path,
    keep_byproducts=True,  # TODO(taejun): remove this
  )

  fig = allin1.visualize(result)
  fig.set_dpi(300)

  allin1.sonify(result, out_dir='./sonif')
  sonif_path = Path(f'./sonif/{path.stem}.sonif{path.suffix}').resolve().as_posix()
      
  dissector_file = generate_dissector_data(path.stem, result)  
  compressed_file = compress_files(f"demix/htdemucs/{path.stem}", dissector_file, path.as_posix())

  return result.bpm, fig, sonif_path, compressed_file


with gr.Blocks() as demo:
  gr.HTML(HEADER)

  input_audio_path = gr.Audio(
    label='Input',
    source='upload',
    type='filepath',
    format='mp3',
    show_download_button=False,
  )
  button = gr.Button('Analyze', variant='primary')
  output_viz = gr.Plot(label='Visualization')
  with gr.Row():
    output_bpm = gr.Textbox(label='BPM', scale=1)
    output_sonif = gr.Audio(
      label='Sonification',
      type='filepath',
      format='mp3',
      show_download_button=False,
      scale=9,
    )
    output_compressed = gr.File(
        label="Compressed Files",
        type="file",
    )
  gr.Examples(
    examples=[
        './assets/kult.mp3',
        # './assets/krematorii.mp3',
      # './assets/NewJeans - Super Shy.mp3',
      # './assets/Bruno Mars - 24k Magic.mp3'
    ],
    inputs=input_audio_path,
    outputs=[output_bpm, output_viz, output_sonif, output_compressed],
    fn=analyze,
    cache_examples=CACHE_EXAMPLES,
  )
  button.click(
    fn=analyze,
    inputs=input_audio_path,
    outputs=[output_bpm, output_viz, output_sonif, output_compressed],
    api_name='analyze',
  )

if __name__ == '__main__':
  demo.launch()