File size: 2,591 Bytes
cf9d382
 
 
 
532b865
 
 
 
049565f
cf9d382
532b865
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
049565f
 
532b865
cf9d382
532b865
 
 
cf9d382
532b865
 
 
 
 
 
 
cf9d382
532b865
 
 
 
cf9d382
532b865
 
049565f
cf9d382
 
049565f
532b865
 
 
 
 
 
 
cf9d382
 
 
 
532b865
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
import gradio as gr
import numpy as np
from scipy import signal

def generate_cylinder_noise(duration_sec: float, rpm: float, sr=44100):
    # Calculate rotational parameters
    rotation_period = 60 / rpm  # Time for one full rotation
    samples_per_rotation = int(sr * rotation_period)
    num_rotations = int(duration_sec / rotation_period)
    
    # Create fundamental mechanical noise pattern
    t = np.linspace(0, rotation_period, samples_per_rotation)
    
    # Base waveform with up-down pattern
    fundamental = 0.5 * signal.sawtooth(2 * np.pi * 1/rotation_period * t, width=0.5)
    
    # Add gravelly texture using modulated noise
    brown_noise = np.cumsum(np.random.normal(0, 1, samples_per_rotation))
    brown_noise /= np.max(np.abs(brown_noise))  # Normalize
    
    # Combine elements with mechanical harmonics
    pattern = (
        fundamental * 0.7 + 
        brown_noise * 0.3 +
        0.2 * np.sin(2 * np.pi * 3/rotation_period * t)  # 3rd harmonic
    )
    
    # Add periodic needle drag (repeating every rotation)
    drag_frequency = rpm/60 * 8  # 8 drags per rotation
    drag = 0.3 * signal.square(2 * np.pi * drag_frequency * t, duty=0.1)
    
    # Create full pattern
    full_waveform = np.tile(pattern + drag, num_rotations)
    remaining_samples = int((duration_sec % rotation_period) * sr)
    if remaining_samples > 0:
        full_waveform = np.concatenate([full_waveform, pattern[:remaining_samples]])
    
    # Apply mechanical filtering
    b, a = signal.butter(2, [80, 2500], btype='bandpass', fs=sr)
    filtered = signal.lfilter(b, a, full_waveform)
    
    # Add rotational speed fluctuations
    warp = 0.05 * np.sin(2 * np.pi * 0.2 * np.arange(len(filtered)) / sr)
    warped = np.interp(
        np.arange(len(filtered)) * (1 + warp),
        np.arange(len(filtered)),
        filtered
    )
    
    # Normalize and add final grit
    warped = warped / np.max(np.abs(warped))
    warped = np.tanh(warped * 3)  # Soft clipping
    return warped, sr

def create_audio(duration: float, rpm: float):
    audio, sr = generate_cylinder_noise(duration, rpm)
    return (sr, audio)

demo = gr.Interface(
    fn=create_audio,
    inputs=[
        gr.Slider(1, 30, value=5, label="Duration (seconds)"),
        gr.Slider(90, 160, value=120, label="Cylinder RPM")
    ],
    outputs=gr.Audio(label="Mechanical Cylinder Noise"),
    title="Rotational Wax Cylinder Noise Generator",
    description="Generate authentic rotational mechanical noise with adjustable RPM",
    allow_flagging="never"
)

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