alexnixxflex commited on
Commit
649b359
·
verified ·
1 Parent(s): 8c21cb9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +50 -54
app.py CHANGED
@@ -1,17 +1,11 @@
1
  import os
2
  import torch
3
- import torchaudio
4
- import torchaudio.transforms as T
5
  import gradio as gr
 
6
  from multi_stem_pipeline import MultiStemSeparationModel
7
 
8
- # We import pydub to handle arbitrary audio formats (M4A, FLAC, AIFF, MP4, etc.)
9
- try:
10
- from pydub import AudioSegment
11
- HAS_PYDUB = True
12
- except ImportError:
13
- HAS_PYDUB = False
14
-
15
  # =====================================================================
16
  # 1. ZEROGPU COMPATIBILITY LAYER
17
  # =====================================================================
@@ -29,30 +23,33 @@ STEMS_LIST = ["vocals", "backing_vocals", "drums", "bass", "guitar", "synth"]
29
  cached_model = None
30
 
31
  # =====================================================================
32
- # 2. AUDIO FORMAT CONVERTER
33
  # =====================================================================
34
- def convert_to_wav(input_path):
35
  """
36
- Takes any input file (m4a, flac, aif, mp3, mp4) and converts it to a
37
- standard WAV file that torchaudio can safely load without backend errors.
38
  """
39
- if not HAS_PYDUB:
40
- # Fallback if pydub is missing
41
- return input_path
42
-
43
- ext = os.path.splitext(input_path)[1].lower()
44
- if ext == ".wav":
45
- return input_path
46
-
47
- print(f"Converting {ext} file to temporary WAV format...")
48
- temp_wav_path = "temp_converted_input.wav"
49
 
50
- # Load audio with pydub (handles AAC, ALAC, FLAC, AIFF, etc.)
51
- audio = AudioSegment.from_file(input_path)
52
 
53
- # Export as standard WAV
54
- audio.export(temp_wav_path, format="wav")
55
- return temp_wav_path
 
 
 
 
 
 
 
 
56
 
57
 
58
  # =====================================================================
@@ -67,8 +64,12 @@ def separate_multi_stems(audio_path):
67
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
68
  sample_rate = 16000
69
 
70
- # 1. Pre-process and convert exotic formats to standard WAV
71
- safe_wav_path = convert_to_wav(audio_path)
 
 
 
 
72
 
73
  # 2. Lazy load our model
74
  if cached_model is None:
@@ -83,55 +84,50 @@ def separate_multi_stems(audio_path):
83
  model = cached_model.to(device)
84
  model.eval()
85
 
86
- # 3. Load & Resample the safe WAV file
87
- waveform, sr = torchaudio.load(safe_wav_path)
88
- if sr != sample_rate:
89
- resampler = T.Resample(orig_freq=sr, new_freq=sample_rate)
90
- waveform = resampler(waveform)
91
-
92
- if waveform.shape[0] > 1:
93
- waveform = torch.mean(waveform, dim=0, keepdim=True)
94
 
95
- # 4. Extract Spectrogram Magnitude & Phase
96
  complex_spec = torch.stft(
97
- waveform, n_fft=512, win_length=512, hop_length=128, normalized=True, return_complex=True
98
  )
99
  magnitude = torch.abs(complex_spec)
100
  phase = torch.angle(complex_spec)
101
 
102
- input_tensor = magnitude.unsqueeze(0).to(device)
103
 
104
- # 5. Run Multi-Mask Inference
105
  with torch.no_grad():
106
  pred_stems_spec, masks = model(input_tensor)
107
 
108
  # Move tensors back to CPU for audio reconstruction
109
- pred_stems_spec = pred_stems_spec.squeeze(0).cpu()
110
- phase = phase.cpu()
 
111
 
112
  saved_filepaths = []
113
 
114
- # 6. Reconstruct each stem
115
  for idx, stem_name in enumerate(STEMS_LIST):
116
- stem_spec = pred_stems_spec[idx]
117
- stem_complex = torch.polar(stem_spec, phase)
 
 
118
  stem_wav = torch.istft(
119
- stem_complex, n_fft=512, win_length=512, hop_length=128, normalized=True
120
  )
121
 
 
122
  out_path = f"output_{stem_name}.wav"
123
- torchaudio.save(out_path, stem_wav, sample_rate)
124
  saved_filepaths.append(out_path)
125
 
126
- # Clean up temporary conversion file if it was created
127
- if safe_wav_path == "temp_converted_input.wav" and os.path.exists(safe_wav_path):
128
- os.remove(safe_wav_path)
129
-
130
  return saved_filepaths
131
 
132
 
133
  # =====================================================================
134
- # 4. DYNAMIC GRADIO INTERFACE GENERATION
135
  # =====================================================================
136
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
137
  gr.Markdown(
 
1
  import os
2
  import torch
3
+ import numpy as np
4
+ import soundfile as sf
5
  import gradio as gr
6
+ from pydub import AudioSegment
7
  from multi_stem_pipeline import MultiStemSeparationModel
8
 
 
 
 
 
 
 
 
9
  # =====================================================================
10
  # 1. ZEROGPU COMPATIBILITY LAYER
11
  # =====================================================================
 
23
  cached_model = None
24
 
25
  # =====================================================================
26
+ # 2. AUDIO LOADER & RESAMPLER (No torchaudio)
27
  # =====================================================================
28
+ def load_audio_as_tensor(audio_path, target_sr=16000):
29
  """
30
+ Loads any audio format directly to a normalized mono PyTorch tensor
31
+ using pydub in memory (no external CLI conversion needed!).
32
  """
33
+ # Load audio (works on mp3, wav, flac, m4a, aiff, mp4 video, etc.)
34
+ audio = AudioSegment.from_file(audio_path)
35
+
36
+ # Resample to 16kHz and convert to Mono
37
+ audio = audio.set_frame_rate(target_sr).set_channels(1)
 
 
 
 
 
38
 
39
+ # Get raw samples as numpy array
40
+ samples = np.array(audio.get_array_of_samples(), dtype=np.float32)
41
 
42
+ # Normalize based on source bit depth
43
+ if audio.sample_width == 1:
44
+ samples = (samples - 128.0) / 128.0
45
+ elif audio.sample_width == 2:
46
+ samples = samples / 32768.0
47
+ elif audio.sample_width == 4:
48
+ samples = samples / 2147483648.0
49
+
50
+ # Convert directly to PyTorch tensor [1, Samples]
51
+ waveform = torch.from_numpy(samples).unsqueeze(0)
52
+ return waveform
53
 
54
 
55
  # =====================================================================
 
64
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
65
  sample_rate = 16000
66
 
67
+ # 1. Load waveform in-memory using our pydub function
68
+ try:
69
+ waveform = load_audio_as_tensor(audio_path, sample_rate)
70
+ except Exception as e:
71
+ print(f"Error decoding audio file: {e}")
72
+ return [None] * len(STEMS_LIST)
73
 
74
  # 2. Lazy load our model
75
  if cached_model is None:
 
84
  model = cached_model.to(device)
85
  model.eval()
86
 
87
+ # Move audio tensors to computation device
88
+ waveform = waveform.to(device)
89
+ window = torch.hann_window(512).to(device)
 
 
 
 
 
90
 
91
+ # 3. Extract Spectrogram Magnitude & Phase using PyTorch native stft
92
  complex_spec = torch.stft(
93
+ waveform, n_fft=512, win_length=512, hop_length=128, window=window, normalized=True, return_complex=True
94
  )
95
  magnitude = torch.abs(complex_spec)
96
  phase = torch.angle(complex_spec)
97
 
98
+ input_tensor = magnitude.unsqueeze(0) # Shape [1, 1, Freq, Time]
99
 
100
+ # 4. Run Multi-Mask Inference
101
  with torch.no_grad():
102
  pred_stems_spec, masks = model(input_tensor)
103
 
104
  # Move tensors back to CPU for audio reconstruction
105
+ pred_stems_spec = pred_stems_spec.squeeze(0) # [num_stems, Freqs, Time]
106
+ phase = phase.squeeze(0) # [Freqs, Time]
107
+ window_cpu = torch.hann_window(512).cpu()
108
 
109
  saved_filepaths = []
110
 
111
+ # 5. Reconstruct and write each isolated stem (No torchaudio)
112
  for idx, stem_name in enumerate(STEMS_LIST):
113
+ stem_spec = pred_stems_spec[idx].cpu()
114
+ stem_complex = torch.polar(stem_spec, phase.cpu())
115
+
116
+ # Native PyTorch inverse Fourier transform to reconstruct waveform
117
  stem_wav = torch.istft(
118
+ stem_complex, n_fft=512, win_length=512, hop_length=128, window=window_cpu, normalized=True
119
  )
120
 
121
+ # Output clean WAV stem using soundfile
122
  out_path = f"output_{stem_name}.wav"
123
+ sf.write(out_path, stem_wav.numpy(), sample_rate)
124
  saved_filepaths.append(out_path)
125
 
 
 
 
 
126
  return saved_filepaths
127
 
128
 
129
  # =====================================================================
130
+ # 4. GRADIO INTERFACE LAYOUT
131
  # =====================================================================
132
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
133
  gr.Markdown(