File size: 4,624 Bytes
a648319
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7a5cbc1
a648319
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7a5cbc1
a648319
7a5cbc1
a648319
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7a5cbc1
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
#!/usr/bin/env python3
"""
GN‑v7 – Fast Raw Mode Extractor
================================
Extract raw Koopman (PCA) modes from any audio file.
No ICA, no plots – just the raw modes as WAV files.
"""

import os
import shutil
import tempfile
import zipfile
from typing import List

import gradio as gr
import numpy as np
from numpy.lib.stride_tricks import sliding_window_view
import scipy.signal as signal
from sklearn.utils.extmath import randomized_svd
import soundfile as sf
import spaces

# ----------------------------------------------------------------------
# Fast embedding
# ----------------------------------------------------------------------
def embed(x: np.ndarray, dim: int, tau: int) -> np.ndarray:
    L = (dim - 1) * tau + 1
    win = sliding_window_view(x, L)[:, ::tau]
    return win[:, ::-1].copy()

# ----------------------------------------------------------------------
# Fast raw mode extraction using randomized SVD
# ----------------------------------------------------------------------
def extract_raw_modes(x: np.ndarray, dim: int = 32, tau: int = 3, k: int = 8) -> List[np.ndarray]:
    """
    Returns list of raw PCA mode signals (time domain) of length same as input.
    Each mode is a reconstructed time series from one singular vector.
    """
    X = embed(x, dim, tau)
    a = np.linalg.norm(X, axis=1) + 1e-9
    Xv = X / a[:, None]

    U, S, Vt = randomized_svd(Xv, n_components=k, n_iter=5, random_state=0)
    W = Vt  # shape (k, dim)

    Y = Xv @ W.T  # activations (T x k)

    raw_modes = []
    for i in range(k):
        mode_embedded = Y[:, i:i+1] @ W[i:i+1, :]  # (T, dim)
        mode_signal = np.real(mode_embedded[:, 0]) * a
        raw_modes.append(mode_signal)

    return raw_modes

# ----------------------------------------------------------------------
# Gradio interface (ZeroGPU compatible)
# ----------------------------------------------------------------------
@spaces.GPU
def process_to_raw_modes(file, dim: int, tau: int, k: int):
    if file is None:
        raise gr.Error("Please upload an audio file.")

    try:
        sig, fs = sf.read(file)
    except Exception as e:
        raise gr.Error(f"Failed to read: {e}")

    if sig.ndim > 1:
        sig = np.mean(sig, axis=1)

    target_sr = 16000
    if fs != target_sr:
        sig = signal.resample_poly(sig, target_sr, fs)
        fs = target_sr

    sig = sig / (np.max(np.abs(sig)) + 1e-9)

    duration = len(sig) / fs
    gr.Info(f"Processing {duration:.1f} sec song with dim={dim}, tau={tau}, k={k} ...")

    try:
        raw_modes = extract_raw_modes(sig, dim=dim, tau=tau, k=k)
    except Exception as e:
        raise gr.Error(f"Extraction failed: {e}")

    temp_dir = tempfile.mkdtemp()
    try:
        for i, mode in enumerate(raw_modes):
            sf.write(os.path.join(temp_dir, f"raw_mode_{i:02d}.wav"), mode, fs)

        zip_path = os.path.join(tempfile.gettempdir(), "raw_modes.zip")
        with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:
            for fname in os.listdir(temp_dir):
                zf.write(os.path.join(temp_dir, fname), arcname=fname)
    finally:
        shutil.rmtree(temp_dir, ignore_errors=True)

    preview_audio = (fs, raw_modes[0])
    return preview_audio, zip_path

# ----------------------------------------------------------------------
# UI
# ----------------------------------------------------------------------
with gr.Blocks(title="Fast Raw Mode Extractor", theme=gr.themes.Soft()) as demo:
    gr.Markdown("""
    # 🔧 Fast Raw Mode Extractor (GN‑v7, PCA only)
    Upload your song → get raw Koopman/PCA modes as WAV files.
    **No ICA, no plots – just the raw modes.**  
    These are the `raw_mode_XX.wav` files from the original GN‑v7.
    """)
    with gr.Row():
        with gr.Column():
            audio_in = gr.Audio(type="filepath", label="Your song (any length)")
            dim_slider = gr.Slider(16, 64, value=32, step=2, label="Embedding dim (lower = faster)")
            tau_slider = gr.Slider(1, 8, value=3, step=1, label="Delay tau")
            k_slider = gr.Slider(2, 16, value=8, step=1, label="Number of raw modes (k)")
            run_btn = gr.Button("Extract Raw Modes", variant="primary")
        with gr.Column():
            preview = gr.Audio(label="Preview of first raw mode", type="numpy")
            zip_output = gr.File(label="📦 Download all raw modes (ZIP)")

    run_btn.click(
        fn=process_to_raw_modes,
        inputs=[audio_in, dim_slider, tau_slider, k_slider],
        outputs=[preview, zip_output]
    )

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