KU_SW_Academy / audio_processing /effect_chain.py
heybaeheef's picture
Upload effect_chain.py
a6c03bb verified
raw
history blame
6.91 kB
"""
Effect Chain - Pedalboard ๊ธฐ๋ฐ˜ ์˜ค๋””์˜ค ์ดํŽ™ํŠธ ์ฒ˜๋ฆฌ
=================================================
V2: ํ•™์Šต ๋ฐ์ดํ„ฐ ํŒŒ๋ผ๋ฏธํ„ฐ ํ‚ค์™€ ํ˜ธํ™˜์„ฑ ๊ฐœ์„ 
"""
import numpy as np
import soundfile as sf
from typing import Dict, List, Optional
from pedalboard import (
Pedalboard,
Compressor,
Gain,
HighShelfFilter,
LowShelfFilter,
PeakFilter,
Delay,
Reverb,
Distortion,
Limiter
)
class EffectChain:
"""Pedalboard ๊ธฐ๋ฐ˜ ์ดํŽ™ํŠธ ์ฒด์ธ"""
def __init__(self, sample_rate: int = 44100):
self.sample_rate = sample_rate
self.available_effects = [
"eq_peak1", "eq_peak2",
"eq_lowshelf", "eq_highshelf",
"distortion", "delay", "compressor",
"reverb", "limiter"
]
def get_available_effects(self) -> List[str]:
"""์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ดํŽ™ํŠธ ๋ชฉ๋ก"""
return self.available_effects
def _get_param(self, params: Dict[str, float], *keys, default=0.0) -> float:
"""
์—ฌ๋Ÿฌ ํ‚ค ์ค‘ ํ•˜๋‚˜๋ผ๋„ ์žˆ์œผ๋ฉด ๋ฐ˜ํ™˜ (ํ˜ธํ™˜์„ฑ ์ฒ˜๋ฆฌ)
์˜ˆ: 'eq_peak1.params.Q' ๋˜๋Š” 'eq_peak1.params.q' ๋‘˜ ๋‹ค ์ง€์›
"""
for key in keys:
if key in params:
return params[key]
return default
def _build_pedalboard(self, params: Dict[str, float]) -> Pedalboard:
"""ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ Pedalboard ๊ตฌ์„ฑ"""
effects = []
# Compressor (ํ•ญ์ƒ ์ ์šฉ)
effects.append(Compressor(
threshold_db=-18.0,
ratio=2.0,
attack_ms=10.0,
release_ms=100.0
))
# EQ Peak 1 (Q/q ๋‘˜ ๋‹ค ์ง€์›)
freq1 = self._get_param(params, "eq_peak1.params.freq", default=1000.0)
gain1 = self._get_param(params, "eq_peak1.params.gain", default=0.0)
q1 = self._get_param(params, "eq_peak1.params.Q", "eq_peak1.params.q", default=1.0)
if abs(gain1) > 0.1:
effects.append(PeakFilter(
cutoff_frequency_hz=max(20, min(20000, freq1)),
gain_db=max(-12, min(12, gain1)),
q=max(0.1, min(10, q1))
))
# EQ Peak 2 (Q/q ๋‘˜ ๋‹ค ์ง€์›)
freq2 = self._get_param(params, "eq_peak2.params.freq", default=4000.0)
gain2 = self._get_param(params, "eq_peak2.params.gain", default=0.0)
q2 = self._get_param(params, "eq_peak2.params.Q", "eq_peak2.params.q", default=1.0)
if abs(gain2) > 0.1:
effects.append(PeakFilter(
cutoff_frequency_hz=max(20, min(20000, freq2)),
gain_db=max(-12, min(12, gain2)),
q=max(0.1, min(10, q2))
))
# Low Shelf
freq_low = self._get_param(params, "eq_lowshelf.params.freq", default=200.0)
gain_low = self._get_param(params, "eq_lowshelf.params.gain", default=0.0)
if abs(gain_low) > 0.1:
effects.append(LowShelfFilter(
cutoff_frequency_hz=max(20, min(2000, freq_low)),
gain_db=max(-12, min(12, gain_low)),
q=0.707
))
# High Shelf
freq_high = self._get_param(params, "eq_highshelf.params.freq", default=8000.0)
gain_high = self._get_param(params, "eq_highshelf.params.gain", default=0.0)
if abs(gain_high) > 0.1:
effects.append(HighShelfFilter(
cutoff_frequency_hz=max(1000, min(20000, freq_high)),
gain_db=max(-12, min(12, gain_high)),
q=0.707
))
# Distortion
dist_amount = self._get_param(params, "distortion_amount", default=0.0)
if dist_amount > 0.01:
# ํ•™์Šต ๋ฐ์ดํ„ฐ๋Š” sigmoid ํ›„ 0.1 ๊ณฑํ•˜๋ฏ€๋กœ ์ตœ๋Œ€ 0.1
# ์—ฌ๊ธฐ์„œ๋Š” drive_db๋กœ ๋ณ€ํ™˜ (0~20dB ๋ฒ”์œ„)
effects.append(Distortion(
drive_db=max(0, min(20, dist_amount * 100))
))
# Delay
delay_time = self._get_param(params, "delay.delay_time", default=0.02)
delay_feedback = self._get_param(params, "delay.feedback", default=0.3)
delay_mix = self._get_param(params, "delay.mix", default=0.2)
if delay_mix > 0.01:
effects.append(Delay(
delay_seconds=max(0.01, min(1.0, delay_time)),
feedback=max(0.0, min(0.9, delay_feedback)),
mix=max(0.0, min(1.0, delay_mix))
))
# Limiter (ํ•ญ์ƒ ๋งˆ์ง€๋ง‰์—)
effects.append(Limiter(threshold_db=-1.0))
return Pedalboard(effects)
def process(
self,
input_path: str,
output_path: str,
parameters: Dict[str, float]
) -> bool:
"""์˜ค๋””์˜ค ํŒŒ์ผ ์ฒ˜๋ฆฌ"""
try:
# ์˜ค๋””์˜ค ๋กœ๋“œ
audio, sr = sf.read(input_path)
# ๋ชจ๋…ธ/์Šคํ…Œ๋ ˆ์˜ค ์ฒ˜๋ฆฌ
if len(audio.shape) == 1:
audio = audio.reshape(-1, 1)
# float32๋กœ ๋ณ€ํ™˜
audio = audio.astype(np.float32)
# Pedalboard ๊ตฌ์„ฑ
board = self._build_pedalboard(parameters)
# ์ฒ˜๋ฆฌ
processed = board(audio, sr)
# Wet/Dry ๋ฏน์Šค (final_wet_mix ํŒŒ๋ผ๋ฏธํ„ฐ ์‚ฌ์šฉ)
wet_mix = self._get_param(parameters, "final_wet_mix", default=0.5)
wet_mix = max(0.0, min(1.0, wet_mix))
# ๊ธธ์ด ๋งž์ถ”๊ธฐ
min_len = min(len(audio), len(processed))
output = audio[:min_len] * (1 - wet_mix) + processed[:min_len] * wet_mix
# ํด๋ฆฌํ•‘ ๋ฐฉ์ง€
output = np.clip(output, -1.0, 1.0)
# ์ €์žฅ
sf.write(output_path, output, sr)
print(f"[EffectChain] โœ… ์ฒ˜๋ฆฌ ์™„๋ฃŒ: {output_path}")
print(f"[EffectChain] ์ ์šฉ๋œ ํŒŒ๋ผ๋ฏธํ„ฐ:")
print(f" - EQ Peak1 Gain: {self._get_param(parameters, 'eq_peak1.params.gain'):.2f} dB")
print(f" - EQ Peak2 Gain: {self._get_param(parameters, 'eq_peak2.params.gain'):.2f} dB")
print(f" - Low Shelf Gain: {self._get_param(parameters, 'eq_lowshelf.params.gain'):.2f} dB")
print(f" - High Shelf Gain: {self._get_param(parameters, 'eq_highshelf.params.gain'):.2f} dB")
print(f" - Distortion: {self._get_param(parameters, 'distortion_amount'):.3f}")
print(f" - Delay Mix: {self._get_param(parameters, 'delay.mix'):.2f}")
print(f" - Wet Mix: {wet_mix:.2f}")
return True
except Exception as e:
print(f"[EffectChain] โŒ ์ฒ˜๋ฆฌ ์‹คํŒจ: {e}")
import traceback
traceback.print_exc()
raise e