Spaces:
Sleeping
Sleeping
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() |