music.py
import librosa
import soundfile as sf
import numpy as np
import pyloudnorm as pyln
from scipy import signal
import matplotlib.pyplot as plt
import pyrubberband as pyrb
from pydub import AudioSegment
from pydub.effects import compress_dynamic_range, normalize
import noisereduce as nr
from sklearn.cluster import KMeans
import warnings
warnings.filterwarnings("ignore")
class UltraProMixMaster:
def __init__(self):
self.sr = 44100
self.n_fft = 2048
self.hop_length = 512
def load_and_analyze(self, file_path):
print(f"Yükleniyor ve detaylı analiz ediliyor: {file_path}")
y, sr = librosa.load(file_path, sr=self.sr)
# Temel özellikler
duration = librosa.get_duration(y=y, sr=sr)
tempo, beats = librosa.beat.beat_track(y=y, sr=sr)
# Spektral özellikler
spectral_centroid = librosa.feature.spectral_centroid(y=y, sr=sr)[0]
spectral_bandwidth = librosa.feature.spectral_bandwidth(y=y, sr=sr)[0]
spectral_contrast = librosa.feature.spectral_contrast(y=y, sr=sr)
# Harmonik özellikler
harmonic, percussive = librosa.effects.hpss(y)
pitches, magnitudes = librosa.piptrack(y=y, sr=sr)
# MFCC
mfcc = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=13)
# Chroma özelliği
chroma = librosa.feature.chroma_stft(y=y, sr=sr)
# Flow analizi
onset_env = librosa.onset.onset_strength(y=y, sr=sr)
pulse = librosa.beat.plp(onset_envelope=onset_env, sr=sr)
# Ses patlamaları tespiti
rms = librosa.feature.rms(y=y)[0]
peaks = librosa.util.peak_pick(rms, pre_max=3, post_max=3, pre_avg=3, post_avg=5, delta=0.5, wait=10)
print(f"Süre: {duration:.2f} saniye, Tempo: {tempo:.2f} BPM")
print(f"Ortalama spektral merkez: {np.mean(spectral_centroid):.2f} Hz")
return y, {
'duration': duration,
'tempo': tempo,
'beats': beats,
'spectral_centroid': spectral_centroid,
'spectral_bandwidth': spectral_bandwidth,
'spectral_contrast': spectral_contrast,
'harmonic': harmonic,
'percussive': percussive,
'pitches': pitches,
'magnitudes': magnitudes,
'mfcc': mfcc,
'chroma': chroma,
'pulse': pulse,
'peaks': peaks
}
def adaptive_eq(self, y, analysis):
print("Gelişmiş adaptif EQ uygulanıyor...")
avg_centroid = np.mean(analysis['spectral_centroid'])
avg_bandwidth = np.mean(analysis['spectral_bandwidth'])
eq_bands = [
(20, 60), (60, 200), (200, 600), (600, 2000),
(2000, 6000), (6000, 20000)
]
for low, high in eq_bands:
band_energy = np.mean(analysis['spectral_contrast'][(low < analysis['spectral_centroid']) & (analysis['spectral_centroid'] < high)])
if band_energy < -10:
y = self.boost_frequencies(y, low, high, 2)
elif band_energy > 10:
y = self.cut_frequencies(y, low, high, -1)
return y
def boost_frequencies(self, y, low_freq, high_freq, gain):
sos = signal.butter(10, [low_freq, high_freq], btype='bandpass', fs=self.sr, output='sos')
filtered = signal.sosfilt(sos, y)
return y + gain * filtered
def cut_frequencies(self, y, low_freq, high_freq, gain):
sos = signal.butter(10, [low_freq, high_freq], btype='bandstop', fs=self.sr, output='sos')
filtered = signal.sosfilt(sos, y)
return y + gain * filtered
def advanced_compression(self, y, analysis):
print("Gelişmiş çok bantlı kompresyon uygulanıyor...")
rms = librosa.feature.rms(y=y)[0]
dynamic_range = np.max(rms) / np.mean(rms)
bands = [
(20, 150), (150, 600), (600, 2000), (2000, 6000), (6000, 20000)
]
compressed_bands = []
for i, (low, high) in enumerate(bands):
band = self.filter_band(y, low, high)
band_rms = librosa.feature.rms(y=band)[0]
band_dynamic_range = np.max(band_rms) / np.mean(band_rms)
threshold = -30 + i * 3 # Lower threshold for lower frequencies
ratio = 4 - i * 0.5 # Higher ratio for lower frequencies
if band_dynamic_range > dynamic_range:
ratio += 1 # Increase ratio for bands with high dynamic range
compressed = self.compress(band, threshold, ratio)
compressed_bands.append(compressed)
return sum(compressed_bands)
def filter_band(self, y, low_freq, high_freq):
sos = signal.butter(10, [low_freq, high_freq], btype='bandpass', fs=self.sr, output='sos')
return signal.sosfilt(sos, y)
def compress(self, y, threshold, ratio):
compressed = np.copy(y)
mask = y > threshold
compressed[mask] = threshold + (y[mask] - threshold) / ratio
return compressed
def apply_reverb(self, y, analysis, mix=0.3):
print("Akıllı reverb uygulanıyor...")
tempo = analysis['tempo']
room_size = 1.0 - (tempo / 180) # Tempo arttıkça room size azalır
room_size = max(0.4, min(room_size, 0.9)) # 0.4 ile 0.9 arasında sınırla
impulse_response = np.exp(-np.linspace(0, 5, int(self.sr * room_size)))
reverb = signal.convolve(y, impulse_response, mode='full')[:len(y)]
return (1 - mix) * y + mix * reverb
def apply_delay(self, y, analysis, feedback=0.4):
print("Akıllı delay uygulanıyor...")
tempo = analysis['tempo']
delay_time = 60 / tempo / 4 # Temponun çeyreği kadar delay
delay_samples = int(delay_time * self.sr)
delayed = np.zeros_like(y)
delayed[delay_samples:] = y[:-delay_samples]
output = y + feedback * delayed
return output / np.max(np.abs(output))
def pitch_correction(self, y, analysis):
print("Gelişmiş pitch düzeltme uygulanıyor...")
pitches = analysis['pitches']
magnitudes = analysis['magnitudes']
# En belirgin pitch'leri bul
pitch_means = []
for i in range(0, len(pitches), self.hop_length):
segment = pitches[i:i+self.hop_length]
segment_magnitudes = magnitudes[i:i+self.hop_length]
if len(segment) > 0:
pitch_mean = np.sum(segment * segment_magnitudes) / np.sum(segment_magnitudes)
pitch_means.append(pitch_mean)
# K-means ile pitch gruplarını bul
kmeans = KMeans(n_clusters=3, random_state=0).fit(np.array(pitch_means).reshape(-1, 1))
dominant_pitches = kmeans.cluster_centers_.flatten()
# Her grup için en yakın nota frekansını bul ve düzelt
corrected_y = np.zeros_like(y)
for i, pitch in enumerate(dominant_pitches):
note_freqs = librosa.midi_to_hz(np.arange(128))
target_freq = note_freqs[np.argmin(np.abs(note_freqs - pitch))]
# Pitch shift uygula
n_steps = 12 * np.log2(target_freq / pitch)
corrected_segment = librosa.effects.pitch_shift(y, sr=self.sr, n_steps=n_steps)
# Düzeltilmiş segmenti ana sinyale ekle
mask = (kmeans.labels_ == i)
corrected_y[mask] += corrected_segment[mask]
return corrected_y
def stereo_enhancement(self, y):
print("Gelişmiş stereo genişletme uygulanıyor...")
if y.ndim == 1:
y = np.column_stack((y, y)) # Mono to stereo
mid = (y[:, 0] + y[:, 1]) / 2
side = (y[:, 0] - y[:, 1]) / 2
# Side sinyali frekans bantlarına ayır ve genişlet
side_bands = [
(0, 200), (200, 800), (800, 3000), (3000, 10000), (10000, self.sr//2)
]
enhanced_side = np.zeros_like(side)
for low, high in side_bands:
band = self.filter_band(side, low, high)
enhanced_side += band * (1 + (high - low) / 10000) # Yüksek frekansları daha çok genişlet
left = mid + enhanced_side
right = mid - enhanced_side
return np.column_stack((left, right))
def dynamic_eq(self, y, analysis):
print("Gelişmiş dinamik EQ uygulanıyor...")
spectral_contrast = analysis['spectral_contrast']
for i, band in enumerate(spectral_contrast):
center_freq = librosa.band_to_hz(i, sr=self.sr)
if np.mean(band) < -20:
y = self.boost_frequencies(y, center_freq * 0.8, center_freq * 1.2, 2)
elif np.mean(band) > 10:
y = self.cut_frequencies(y, center_freq * 0.8, center_freq * 1.2, -1)
return y
def match_loudness(self, y, target_loudness=-14.0):
print("Hassas ses yüksekliği eşitleme uygulanıyor...")
meter = pyln.Meter(self.sr)
loudness = meter.integrated_loudness(y)
return pyln.normalize.loudness(y, loudness, target_loudness)
def mix_tracks(self, vocal, beat, vocal_analysis, beat_analysis):
print("Gelişmiş akıllı mix uygulanıyor...")
# Tempo eşleştirme
if abs(vocal_analysis['tempo'] - beat_analysis['tempo']) > 5:
beat = pyrb.time_stretch(beat, self.sr, vocal_analysis['tempo'] / beat_analysis['tempo'])
# Uzunlukları eşitle
max_len = max(len(vocal), len(beat))
vocal = librosa.util.fix_length(vocal, max_len)
beat = librosa.util.fix_length(beat, max_len)
# Dinamik mix oranları
vocal_rms = librosa.feature.rms(y=vocal)[0]
beat_rms = librosa.feature.rms(y=beat)[0]
# Flow'a göre mix oranlarını ayarla
vocal_pulse = vocal_analysis['pulse']
beat_pulse = beat_analysis['pulse']
mix_ratio = np.zeros(len(vocal))
for i in range(len(mix_ratio)):
if vocal_pulse[i] > beat_pulse[i]:
mix_ratio[i] = 0.7 # Vokal öne çıksın
else:
mix_ratio[i] = 0.5 # Beat öne çıksın
mixed = mix_ratio * vocal + (1 - mix_ratio) * beat
return mixed
def de_ess(self, y, threshold=-20, reduction_factor=0.5):
print("De-essing uygulanıyor...")
# 5000-8000 Hz bandını izole et
sos = signal.butter(10, [5000, 8000], btype='bandpass', fs=self.sr, output='sos')
high_freq = signal.sosfilt(sos, y)
# Yüksek frekanslardaki aşırı sibilansı tespit et ve azalt
envelope = np.abs(hilbert(high_freq))
mask = envelope > 10**(threshold/20)
high_freq[mask] *= reduction_factor
# Düzeltilmiş yüksek frekansları orijinal sinyale ekle
return y - signal.sosfilt(sos, y) + high_freq
def remove_plosives(self, y, threshold=0.95):
print("Plosive sesleri kaldırılıyor...")
# Düşük frekans bandını izole et
sos = signal.butter(10, 180, btype='lowpass', fs=self.sr, output='sos')
low_freq = signal.sosfilt(sos, y)
# Ani yükselişleri tespit et ve yumuşat
envelope = np.abs(hilbert(low_freq))
smooth_env = signal.savgol_filter(envelope, 101, 2)
mask = envelope > threshold * smooth_env
y[mask] = smooth_env[mask]
return y
def final_processing(self, y):
print("Son işlemler uygulanıyor...")
# Multiband limiter
bands = [
(0, 150), (150, 600), (600, 2000), (2000, 6000), (6000, self.sr//2)
]
limited_bands = []
for low, high in bands:
band = self.filter_band(y, low, high)
limited_band = np.clip(band, -0.98, 0.98)
limited_bands.append(limited_band)
y = sum(limited_bands)
# Soft clipper
y = np.tanh(y)
# Dither uygula
y += np.random.normal(0, 1/32768, y.shape)
# Final normalization
y = librosa.util.normalize(y, norm=np.inf, threshold=0.99)
return y
def visualize_spectrogram(self, y, title):
plt.figure(figsize=(12, 8))
D = librosa.stft(y)
S_db = librosa.amplitude_to_db(np.abs(D), ref=np.max)
librosa.display.specshow(S_db, sr=self.sr, x_axis='time', y_axis='hz')
plt.colorbar(format='%+2.0f dB')
plt.title(title)
plt.tight_layout()
plt.show()
def process(self, vocal_path, beat_path, output_path):
vocal, vocal_analysis = self.load_and_analyze(vocal_path)
beat, beat_analysis = self.load_and_analyze(beat_path)
# Vokal işleme
vocal = self.remove_plosives(vocal)
vocal = self.de_ess(vocal)
vocal = self.adaptive_eq(vocal, vocal_analysis)
vocal = self.advanced_compression(vocal, vocal_analysis)
vocal = self.pitch_correction(vocal, vocal_analysis)
vocal = self.apply_reverb(vocal, vocal_analysis, mix=0.2)
vocal = self.apply_delay(vocal, vocal_analysis, feedback=0.2)
# Beat işleme
beat = self.adaptive_eq(beat, beat_analysis)
beat = self.advanced_compression(beat, beat_analysis)
beat = self.stereo_enhancement(beat)
# Mixleme
mixed = self.mix_tracks(vocal, beat, vocal_analysis, beat_analysis)
# Son işlemler
mixed = self.dynamic_eq(mixed, vocal_analysis) # Vokal analizini kullanıyoruz
mixed = self.stereo_enhancement(mixed)
mixed = self.final_processing(mixed)
mixed = self.match_loudness(mixed)
# Sonucu kaydet
sf.write(output_path, mixed, self.sr)
print(f"Mix ve mastering tamamlandı. Sonuç {output_path} konumuna kaydedildi.")
# Spektrogramları görselleştir
self.visualize_spectrogram(vocal, "Vokal Spektrogramı")
self.visualize_spectrogram(beat, "Beat Spektrogramı")
self.visualize_spectrogram(mixed, "Final Mix Spektrogramı")
def analyze_flow(self, y, sr):
print("Flow analizi yapılıyor...")
onset_env = librosa.onset.onset_strength(y=y, sr=sr)
tempo, beats = librosa.beat.beat_track(onset_envelope=onset_env, sr=sr)
# Flow değişimlerini tespit et
beat_strengths = onset_env[beats]
flow_changes = np.diff(beat_strengths)
significant_changes = np.where(np.abs(flow_changes) > np.std(flow_changes))[0]
flow_segments = []
last_change = 0
for change in significant_changes:
flow_segments.append((last_change, change))
last_change = change
flow_segments.append((last_change, len(beats)-1))
return flow_segments, beats
def flow_based_processing(self, y, sr):
print("Flow tabanlı işleme uygulanıyor...")
flow_segments, beats = self.analyze_flow(y, sr)
processed_y = np.zeros_like(y)
for start, end in flow_segments:
segment = y[beats[start]:beats[end]]
# Her flow segmenti için özel işlemler
segment = self.adaptive_eq(segment, {'spectral_centroid': librosa.feature.spectral_centroid(y=segment, sr=sr)[0]})
segment = self.advanced_compression(segment, {'rms': librosa.feature.rms(y=segment)[0]})
# Flow değişimine göre efekt miktarını ayarla
flow_intensity = np.mean(librosa.feature.rms(y=segment)[0])
reverb_mix = 0.1 + 0.2 * flow_intensity
segment = self.apply_reverb(segment, {'tempo': librosa.beat.tempo(y=segment, sr=sr)[0]}, mix=reverb_mix)
processed_y[beats[start]:beats[end]] = segment
return processed_y
# Kullanım örneği
mixer = UltraProMixMaster()
vocal_file = input("Vokal dosyasının yolunu girin: ")
beat_file = input("Beat dosyasının yolunu girin: ")
output_file = input("Çıktı dosyasının yolunu ve adını girin: ")
mixer.process(vocal_file, beat_file, output_file)