Avastage helisünteesi ja digitaalse signaalitöötluse (DSP) maailma Pythoni abil. Õppige looma lainekujusid, rakendama filtreid ja genereerima heli nullist.
Heli valla päästmine: Sügav sukeldumine Pythonisse helisünteesi ja digitaalse signaalitöötluse jaoks
Alates muusikast, mis mängib teie kõrvaklappides, kuni kaasahaaravate helimaastikeni videomängudes ja hääleassistentideni meie seadmetes, on digitaalne heli kaasaegse elu lahutamatu osa. Kuid kas olete kunagi mõelnud, kuidas neid helisid luuakse? See ei ole maagia; see on matemaatika, füüsika ja arvutiteaduse põnev segu, mida tuntakse digitaalse signaalitöötlusena (DSP). Täna kergitame saladusloori ja näitame teile, kuidas kasutada Pythoni võimsust heli genereerimiseks, manipuleerimiseks ja sünteesimiseks nullist.
See juhend on mõeldud arendajatele, andmeteadlastele, muusikutele, kunstnikele ja kõigile, kes on uudishimulikud koodi ja loovuse ristumiskoha vastu. Te ei pea olema DSP ekspert ega kogenud helitehnik. Pythoni põhiliste teadmistega saate peagi luua oma unikaalseid helimaastikke. Uurime digitaalse heli põhilisi ehitusplokke, genereerime klassikalisi lainekujusid, vormime neid ümbrikute ja filtritega ning ehitame isegi miniseisüntesaatori. Alustame oma teekonda arvutusheli elavasse maailma.
Digitaalse heli ehitusplokkide mõistmine
Enne kui saame kirjutada ühegi koodirea, peame mõistma, kuidas heli arvutis esitatakse. Füüsilises maailmas on heli pidev analoogsurve laine. Arvutid, olles digitaalsed, ei saa pidevat lainet salvestada. Selle asemel võtavad nad igast sekundist tuhandeid hetktõmmiseid ehk sämpleid lainest. Seda protsessi nimetatakse sämplimiseks.
Sämplimissagedus
Sämplimissagedus määrab, mitu sämplit sekundis võetakse. Seda mõõdetakse hertsides (Hz). Kõrgem sämplimissagedus annab originaalse helilaine täpsema esituse, mis viib kõrgema täpsusega helini. Levinud sämplimissagedused on järgmised:
- 44100 Hz (44.1 kHz): Helikassettide standard. See on valitud Nyquist-Shannoni sämplimisteoreemi alusel, mis ütleb, et sämplimissagedus peab olema vähemalt kaks korda suurem kui kõrgeim sagedus, mida soovite jäädvustada. Kuna inimkõrva kuuldeulatus ulatub umbes 20 000 Hz-ni, pakub 44,1 kHz piisavat puhvrit.
- 48000 Hz (48 kHz): Standard professionaalse video ja digitaalsete helitöötlusjaamade (DAW) jaoks.
- 96000 Hz (96 kHz): Kasutatakse kõrglahutusega helitootmises veelgi suurema täpsuse saavutamiseks.
Meie eesmärkidel kasutame peamiselt 44100 Hz, kuna see pakub suurepärast tasakaalu kvaliteedi ja arvutustõhususe vahel.
Bitisügavus
Kui sämplimissagedus määrab ajas resolutsiooni, siis bitisügavus määrab resolutsiooni amplituudis (valjuses). Iga sämpel on number, mis esindab laine amplituudi sel konkreetsel hetkel. Bitisügavus on selle numbri salvestamiseks kasutatavate bittide arv. Suurem bitisügavus võimaldab rohkem võimalikke amplituudiväärtusi, mille tulemuseks on suurem dünaamiline ulatus (erinevus kõige vaiksemate ja valjemate võimalike helide vahel) ja madalam mürapõrand.
- 16-bit: CD-de standard, pakkudes 65 536 võimalikku amplituuditasandit.
- 24-bit: Professionaalse helitootmise standard, pakkudes üle 16,7 miljoni tasandi.
Kui genereerime Pythonis heli, kasutades teeke nagu NumPy, töötame tavaliselt ujukomaarvudega (nt vahemikus -1.0 kuni 1.0) maksimaalse täpsuse saavutamiseks. Need teisendatakse seejärel konkreetse bitisügavusega (nt 16-bitisteks täisarvudeks) faili salvestamisel või riistvara kaudu taasesitamisel.
Kanalid
See viitab lihtsalt helivoogude arvule. Mono helil on üks kanal, samas kui Stereo helil on kaks (vasak ja parem), luues ruumilisuse ja suunalisuse tunde.
Pythoni keskkonna seadistamine
Alustamiseks vajame mõnda olulist Pythoni teeki. Need moodustavad meie tööriistakomplekti numbriliseks arvutuseks, signaalitöötluseks, visualiseerimiseks ja heli taasesituseks.
Saate neid paigaldada pip-i abil:
pip install numpy scipy matplotlib sounddevice
Vaatame lühidalt nende rolle:
- NumPy: Teadusliku arvutuse nurgakivi Pythonis. Kasutame seda arvude massiivide loomiseks ja manipuleerimiseks, mis esindavad meie helisignaale.
- SciPy: Ehitatud NumPy peale, pakub see ulatuslikku algoritmide kogu signaalitöötluseks, sealhulgas lainekujude genereerimiseks ja filtreerimiseks.
- Matplotlib: Peamine graafikute joonistamise teek Pythonis. See on hindamatu väärtusega meie lainekujude visualiseerimiseks ja meie töötluse mõjude mõistmiseks.
- SoundDevice: Mugav teek meie NumPy massiivide esitamiseks heli kujul läbi teie arvuti kõlarite. See pakub lihtsat ja platvormiülest liidest.
Lainekuju genereerimine: Sünteesi süda
Kõik helid, olenemata sellest, kui keerulised, saab jagada lihtsate, põhiliste lainekujude kombinatsioonideks. Need on meie helipaleti põhivärvid. Õppime neid genereerima.
Siinuslaine: Puhtaim toon
Siinuslaine on kogu heli absoluutne ehitusplokk. See esindab üht sagedust ilma ületoonide või harmoonilisteta. See kõlab väga sujuvalt, puhtalt ja seda kirjeldatakse sageli kui "flöödisarnast". Matemaatiline valem on:
y(t) = Amplituud * sin(2 * π * sagedus * t)
Kus 't' on aeg. Tõlgime selle Pythoni koodiks.
import numpy as np
import sounddevice as sd
import matplotlib.pyplot as plt
# --- Üldparameetrid ---
SAMPLE_RATE = 44100 # sämplit sekundis
DURATION = 3.0 # sekundit
# --- Lainekuju genereerimine ---
def generate_sine_wave(frequency, duration, sample_rate, amplitude=0.5):
"""Genereerib siinuslaine.
Args:
frequency (float): Siinuslaine sagedus Hz-des.
duration (float): Laine kestus sekundites.
sample_rate (int): Sämplimissagedus Hz-des.
amplitude (float): Laine amplituud (0.0 kuni 1.0).
Returns:
np.ndarray: Genereeritud siinuslaine NumPy massiivina.
"""
# Loo ajapunktide massiiv
t = np.linspace(0, duration, int(sample_rate * duration), False)
# Genereeri siinuslaine
# 2 * pi * frequency on nurksagedus
wave = amplitude * np.sin(2 * np.pi * frequency * t)
return wave
# --- Näide kasutusest ---
if __name__ == "__main__":
# Genereeri 440 Hz (A4 noot) siinuslaine
frequency_a4 = 440.0
sine_wave = generate_sine_wave(frequency_a4, DURATION, SAMPLE_RATE)
print("Mängitakse 440 Hz siinuslainet...")
# Mängi heli
sd.play(sine_wave, SAMPLE_RATE)
sd.wait() # Oota, kuni heli mängimine lõpeb
print("Taasesitus lõpetatud.")
# --- Visualiseerimine ---
# Joonista väike osa lainest, et näha selle kuju
plt.figure(figsize=(12, 4))
plt.plot(sine_wave[:500])
plt.title("Siinuslaine (440 Hz)")
plt.xlabel("Sämpel")
plt.ylabel("Amplituud")
plt.grid(True)
plt.show()
Selles koodis loob np.linspace massiivi, mis esindab ajatelge. Seejärel rakendame sellele ajamassiivile siinusfunktsiooni, mis on skaleeritud soovitud sagedusega. Tulemuseks on NumPy massiiv, kus iga element on meie helilaine sämpel. Seejärel saame seda esitada sounddevice abil ja visualiseerida matplotlib abil.
Teiste põhiliste lainekujude uurimine
Kuigi siinuslaine on puhas, ei ole see alati kõige huvitavam. Teised põhilised lainekujud on harmooniliste poolest rikkad, andes neile keerukama ja heledama iseloomu (tämber). Moodul scipy.signal pakub mugavaid funktsioone nende genereerimiseks.
Kandiline laine
Kandiline laine hüppab hetkega oma maksimaalse ja minimaalse amplituudi vahel. See sisaldab ainult paarituid harmoonilisi. Sellel on hele, rohutooniline ja mõnevõrra 'õõnes' või 'digitaalne' heli, mida sageli seostatakse varajase videomängude muusikaga.
from scipy import signal
# Genereeri kandiline laine
square_wave = 0.5 * signal.square(2 * np.pi * 440 * np.linspace(0, DURATION, int(SAMPLE_RATE * DURATION), False))
# sd.play(square_wave, SAMPLE_RATE)
# sd.wait()
Saekujuline laine
Saekujuline laine tõuseb lineaarselt ja langeb seejärel koheselt oma miinimumväärtusele (või vastupidi). See on uskumatult rikas, sisaldades kõiki täisarvharmoonilisi (nii paaris- kui ka paarituid). See muudab selle kõla väga heledaks, sumisevaks ja on fantastiline lähtepunkt subtraktiivsele sünteesile, mida käsitleme hiljem.
# Genereeri saekujuline laine
sawtooth_wave = 0.5 * signal.sawtooth(2 * np.pi * 440 * np.linspace(0, DURATION, int(SAMPLE_RATE * DURATION), False))
# sd.play(sawtooth_wave, SAMPLE_RATE)
# sd.wait()
Kolmnurklaine
Kolmnurklaine tõuseb ja langeb lineaarselt. Nagu kandiline laine, sisaldab see ainult paarituid harmoonilisi, kuid nende amplituud väheneb palju kiiremini. See annab sellele heli, mis on kandilisest lainest pehmem ja mahedam, siinuslainele lähedasem, kuid pisut rohkem 'kehaga'.
# Genereeri kolmnurklaine (saekujuline laine laiusega 0.5)
triangle_wave = 0.5 * signal.sawtooth(2 * np.pi * 440 * np.linspace(0, DURATION, int(SAMPLE_RATE * DURATION), False), width=0.5)
# sd.play(triangle_wave, SAMPLE_RATE)
# sd.wait()
Valge müra: Juhuslikkuse heli
Valge müra on signaal, mis sisaldab võrdset energiat igal sagedusel. See kõlab nagu staatika või joa "šššš". See on helidisainis uskumatult kasulik perkussiivsete helide (nagu hi-hatid ja trummid) ja atmosfääriliste efektide loomiseks. Selle genereerimine on märkimisväärselt lihtne.
# Genereeri valge müra
num_samples = int(SAMPLE_RATE * DURATION)
white_noise = np.random.uniform(-1, 1, num_samples)
# sd.play(white_noise, SAMPLE_RATE)
# sd.wait()
Aditiivne süntees: Keerukuse loomine
Prantsuse matemaatik Joseph Fourier avastas, et iga keeruline, perioodiline lainekuju saab dekonstrueerida lihtsate siinuslainete summaks. See on aditiivse sünteesi alus. Erinevate sageduste (harmooniliste) ja amplituudidega siinuslainete liitmisega saame konstrueerida uusi, rikkamaid tämbereid.
Loome keerukama tooni, lisades põhisageduse esimesed harmoonilised.
def generate_complex_tone(fundamental_freq, duration, sample_rate):
t = np.linspace(0, duration, int(sample_rate * duration), False)
# Alusta põhisagedusega
tone = 0.5 * np.sin(2 * np.pi * fundamental_freq * t)
# Lisa harmoonilisi (ületoone)
# 2. harmooniline (oktav kõrgem), madalama amplituudiga
tone += 0.25 * np.sin(2 * np.pi * (2 * fundamental_freq) * t)
# 3. harmooniline, veelgi madalama amplituudiga
tone += 0.12 * np.sin(2 * np.pi * (3 * fundamental_freq) * t)
# 5. harmooniline
tone += 0.08 * np.sin(2 * np.pi * (5 * fundamental_freq) * t)
# Normaliseeri lainekuju vahemikku -1 ja 1
tone = tone / np.max(np.abs(tone))
return tone
# --- Näide kasutusest ---
complex_tone = generate_complex_tone(220, DURATION, SAMPLE_RATE)
sd.play(complex_tone, SAMPLE_RATE)
sd.wait()
Hoolikalt valides, milliseid harmoonilisi ja milliste amplituudidega lisada, saate hakata jäljendama reaalmaailma instrumentide helisid. See lihtne näide kõlab juba palju rikkalikumalt ja huvitavamalt kui tavaline siinuslaine.
Heli vormimine ümbrikute abil (ADSR)
Siiani algavad ja lõpevad meie helid järsult. Nende helitugevus on kogu kestuse vältel konstantne, mis kõlab väga ebaloomulikult ja robotlikult. Reaalmaailmas arenevad helid aja jooksul. Klaveri noot algab teravalt ja valjult, mis kiiresti hajub, samas kui viiulil mängitud noot võib järk-järgult helitugevust suurendada. Seda dünaamilist arengut kontrollime amplituudi ümbriku abil.
ADSR mudel
Kõige levinum ümbriku tüüp on ADSR ümbrik, millel on neli etappi:
- Attack (rünnak): Aeg, mis kulub heli vaiksest olekust maksimaalse amplituudini jõudmiseks. Kiire rünnak loob perkussiivse, terava heli (nagu trummilöök). Aeglane rünnak loob õrna, paisuva heli (nagu keelpillipadi).
- Decay (lagunemine): Aeg, mis kulub heli vähenemiseks maksimaalsest rünnaku tasemest püsiva tasemeni.
- Sustain (püsimine): Amplituuditasand, mida heli säilitab seni, kuni nooti hoitakse. See on tase, mitte aeg.
- Release (vabanemine): Aeg, mis kulub heli hääbumiseks püsivast tasemest vaikuseni pärast noodi vabastamist. Pikk vabanemine paneb heli kauem kestma, nagu klaveri noot, millel on sustain-pedaal all.
ADSR ümbriku implementeerimine Pythonis
Saame implementeerida funktsiooni ADSR ümbriku genereerimiseks NumPy massiivina. Seejärel rakendame selle oma lainekujule lihtsa elemendiviisilise korrutamise teel.
def adsr_envelope(duration, sample_rate, attack_time, decay_time, sustain_level, release_time):
num_samples = int(duration * sample_rate)
attack_samples = int(attack_time * sample_rate)
decay_samples = int(decay_time * sample_rate)
release_samples = int(release_time * sample_rate)
sustain_samples = num_samples - attack_samples - decay_samples - release_samples
if sustain_samples < 0:
# Kui ajad on liiga pikad, kohanda neid proportsionaalselt
total_time = attack_time + decay_time + release_time
attack_time, decay_time, release_time = \
attack_time/total_time*duration, decay_time/total_time*duration, release_time/total_time*duration
attack_samples = int(attack_time * sample_rate)
decay_samples = int(decay_time * sample_rate)
release_samples = int(release_time * sample_rate)
sustain_samples = num_samples - attack_samples - decay_samples - release_samples
# Genereeri ümbriku iga osa
attack = np.linspace(0, 1, attack_samples)
decay = np.linspace(1, sustain_level, decay_samples)
sustain = np.full(sustain_samples, sustain_level)
release = np.linspace(sustain_level, 0, release_samples)
return np.concatenate([attack, decay, sustain, release])
# --- Näide kasutusest: Pluck vs. Pad heli ---
# Pluck heli (kiire rünnak, kiire hääbumine, sustain puudub)
pluck_envelope = adsr_envelope(DURATION, SAMPLE_RATE, 0.01, 0.2, 0.0, 0.5)
# Pad heli (aeglane rünnak, pikk vabastamine)
pad_envelope = adsr_envelope(DURATION, SAMPLE_RATE, 0.5, 0.2, 0.7, 1.0)
# Genereeri harmooniliselt rikas saekujuline laine, millele ümbrikud rakendada
saw_wave_for_env = generate_complex_tone(220, DURATION, SAMPLE_RATE)
# Rakenda ümbrikud
plucky_sound = saw_wave_for_env * pluck_envelope
pad_sound = saw_wave_for_env * pad_envelope
print("Mängitakse pluck-heli...")
sd.play(plucky_sound, SAMPLE_RATE)
sd.wait()
print("Mängitakse pad-heli...")
sd.play(pad_sound, SAMPLE_RATE)
sd.wait()
# Visualiseeri ümbrikud
plt.figure(figsize=(12, 6))
plt.subplot(2, 1, 1)
plt.plot(pluck_envelope)
plt.title("Pluck ADSR ümbrik")
plt.subplot(2, 1, 2)
plt.plot(pad_envelope)
plt.title("Pad ADSR ümbrik")
plt.tight_layout()
plt.show()
Pange tähele, kui dramaatiliselt sama aluslainekuju muudab oma iseloomu lihtsalt erineva ümbriku rakendamisega. See on helidisaini fundamentaalne tehnika.
Sissejuhatus digitaalsesse filtreerimisse (Subtraktiivne süntees)
Kui aditiivne süntees loob heli siinuslainete lisamisega, siis subtraktiivne süntees töötab vastupidi. Me alustame harmooniliselt rikka signaaliga (nagu saekujuline laine või valge müra) ja seejärel eemaldame või summutame teatud sagedusi filtrite abil. See on analoogiline skulptoriga, kes alustab marmorplokist ja raiub sellest vormi välja.
Peamised filtritüübid
- Madalpääsfilter: See on sünteesis kõige levinum filter. See laseb läbi sagedused, mis on teatud "lõikepunktist" allpool, summutades samal ajal sagedused, mis on sellest ülespool. See muudab heli tumedamaks, soojemaks või summutatumaks.
- Kõrgpääsfilter: Madalpääsfiltri vastand. See laseb läbi sagedused, mis on lõikepunktist ülespool, eemaldades bassi- ja madala sagedusega helid. See muudab heli õhemaks või plekkisevamaks.
- Ribapääsfilter: Laseb läbi ainult kindla sagedusriba, lõigates nii kõrged kui ka madalad sagedused. See võib luua "telefoni" või "raadio" efekti.
- Riba-tõkestusfilter (Notch): Ribapääsfiltri vastand. See eemaldab kindla sagedusriba.
Filtrite implementeerimine SciPyga
Teek scipy.signal pakub võimsaid tööriistu digitaalsete filtrite kavandamiseks ja rakendamiseks. Kasutame levinud tüüpi, mida nimetatakse Butterworthi filtriks, mis on tuntud oma tasase reaktsiooni poolest läbipääsuribas.
Protsess hõlmab kahte sammu: esiteks filtri projekteerimine selle koefitsientide saamiseks ja teiseks nende koefitsientide rakendamine meie helisignaalile.
from scipy.signal import butter, lfilter, freqz
def butter_lowpass_filter(data, cutoff, fs, order=5):
"""Rakendab signaalile madalpääs-Butterworthi filtri."""
nyquist = 0.5 * fs
normal_cutoff = cutoff / nyquist
# Hangi filtri koefitsiendid
b, a = butter(order, normal_cutoff, btype='low', analog=False)
y = lfilter(b, a, data)
return y
# --- Näide kasutusest ---
# Alusta rikkalikust signaalist: saekujuline laine
saw_wave_rich = 0.5 * signal.sawtooth(2 * np.pi * 220 * np.linspace(0, DURATION, int(SAMPLE_RATE * DURATION), False))
print("Mängitakse originaalset saekujulist lainet...")
sd.play(saw_wave_rich, SAMPLE_RATE)
sd.wait()
# Rakenda madalpääsfilter lõikesagedusega 800 Hz
filtered_saw = butter_lowpass_filter(saw_wave_rich, cutoff=800, fs=SAMPLE_RATE, order=6)
print("Mängitakse filtreeritud saekujulist lainet...")
sd.play(filtered_saw, SAMPLE_RATE)
sd.wait()
# --- Filtri sageduskarakteristiku visualiseerimine ---
cutoff_freq = 800
order = 6
b, a = butter(order, cutoff_freq / (0.5 * SAMPLE_RATE), btype='low')
w, h = freqz(b, a, worN=8000)
plt.figure(figsize=(10, 5))
plt.plot(0.5 * SAMPLE_RATE * w / np.pi, np.abs(h), 'b')
plt.plot(cutoff_freq, 0.5 * np.sqrt(2), 'ko')
plt.axvline(cutoff_freq, color='k', linestyle='--')
plt.xlim(0, 5000)
plt.title("Madalpääsfiltri sageduskarakteristik")
plt.xlabel('Sagedus [Hz]')
plt.grid()
plt.show()
Kuulake erinevust originaal- ja filtreeritud lainete vahel. Originaal on hele ja sumisev; filtreeritud versioon on palju pehmem ja tumedam, sest kõrgsageduslikud harmoonilised on eemaldatud. Madalpääsfiltri lõikesageduse pühkimine on elektroonilises muusikas üks kõige väljendusrikkamaid ja levinumaid tehnikaid.
Modulatsioon: Liikumise ja elu lisamine
Staatilised helid on igavad. Modulatsioon on võti dünaamiliste, arenevate ja huvitavate helide loomiseks. Põhimõte on lihtne: kasutada ühte signaali (modulaatorit) teise signaali (kandja) parameetri juhtimiseks. Levinud modulaator on madalsagedusostsillaator (LFO), mis on lihtsalt ostsillaator sagedusega, mis jääb alla inimkõrva kuuldeulatuse (nt 0,1 Hz kuni 20 Hz).
Amplituudmodulatsioon (AM) ja tremolo
See on siis, kui kasutame LFO-d oma heli amplituudi juhtimiseks. Tulemuseks on rütmiline helitugevuse pulseerimine, mida tuntakse tremolona.
# Kandja laine (heli, mida kuuleme)
carrier_freq = 300
carrier = generate_sine_wave(carrier_freq, DURATION, SAMPLE_RATE)
# Modulaator LFO (kontrollib helitugevust)
lfo_freq = 5 # 5 Hz LFO
modulator = generate_sine_wave(lfo_freq, DURATION, SAMPLE_RATE, amplitude=1.0)
# Loo tremolo efekt
# Skaleerime modulaatori vahemikku 0 kuni 1
tremolo_modulator = (modulator + 1) / 2
tremolo_sound = carrier * tremolo_modulator
print("Mängitakse tremolo efekti...")
sd.play(tremolo_sound, SAMPLE_RATE)
sd.wait()
Sagedusmodulatsioon (FM) ja vibrato
See on siis, kui kasutame LFO-d oma heli sageduse juhtimiseks. Aeglane, peen sageduse modulatsioon loob vibrato, pehme helikõrguse võnke, mida lauljad ja viiuldajad kasutavad väljenduse lisamiseks.
# Loo vibrato efekt
t = np.linspace(0, DURATION, int(SAMPLE_RATE * DURATION), False)
carrier_freq = 300
lfo_freq = 7
modulation_depth = 10 # Kui palju sagedus varieerub
# LFO lisatakse kandjasagedusele
modulator_vibrato = modulation_depth * np.sin(2 * np.pi * lfo_freq * t)
# Hetkeline sagedus muutub aja jooksul
instantaneous_freq = carrier_freq + modulator_vibrato
# Faasi saamiseks peame sagedust integreerima
phase = np.cumsum(2 * np.pi * instantaneous_freq / SAMPLE_RATE)
vibrato_sound = 0.5 * np.sin(phase)
print("Mängitakse vibrato efekti...")
sd.play(vibrato_sound, SAMPLE_RATE)
sd.wait()
See on FM-sünteesi lihtsustatud versioon. Kui LFO sagedust suurendatakse kuuldavasse vahemikku, loob see keerukaid külgribasagedusi, mille tulemuseks on rikkalikud, kellalikud ja metalsed toonid. See on selliste süntesaatorite nagu Yamaha DX7 ikoonilise heli alus.
Kõike kokku pannes: Mini-süntesaatori projekt
Ühendame kõik, mida oleme õppinud, lihtsasse, funktsionaalsesse süntesaatori klassi. See kapseldab meie ostsillaatori, ümbriku ja filtri ühte, korduvkasutatavasse objekti.
class MiniSynth:
def __init__(self, sample_rate=44100):
self.sample_rate = sample_rate
def generate_note(self, frequency, duration, waveform='sine',
adsr_params=(0.05, 0.2, 0.5, 0.3),
filter_params=None):
"""Genereerib ühe sünteesitud noodi."""
num_samples = int(duration * self.sample_rate)
t = np.linspace(0, duration, num_samples, False)
# 1. Ostsillaator
if waveform == 'sine':
wave = np.sin(2 * np.pi * frequency * t)
elif waveform == 'square':
wave = signal.square(2 * np.pi * frequency * t)
elif waveform == 'sawtooth':
wave = signal.sawtooth(2 * np.pi * frequency * t)
elif waveform == 'triangle':
wave = signal.sawtooth(2 * np.pi * frequency * t, width=0.5)
else:
raise ValueError("Toetuseta lainekuju")
# 2. Ümbrik
attack, decay, sustain, release = adsr_params
envelope = adsr_envelope(duration, self.sample_rate, attack, decay, sustain, release)
# Veendu, et ümbrik ja laine on sama pikkusega
min_len = min(len(wave), len(envelope))
wave = wave[:min_len] * envelope[:min_len]
# 3. Filter (valikuline)
if filter_params:
cutoff = filter_params.get('cutoff', 1000)
order = filter_params.get('order', 5)
filter_type = filter_params.get('type', 'low')
if filter_type == 'low':
wave = butter_lowpass_filter(wave, cutoff, self.sample_rate, order)
# ... siia saab lisada kõrgpääsfiltri jne.
# Normaliseeri 0.5 amplituudile
return wave * 0.5
# --- Süntesaatori näide kasutusest ---
synth = MiniSynth()
# Ere, terav bassheli
bass_note = synth.generate_note(
frequency=110, # A2 noot
duration=1.5,
waveform='sawtooth',
adsr_params=(0.01, 0.3, 0.0, 0.2),
filter_params={'cutoff': 600, 'order': 6}
)
print("Mängitakse süntesaatori bassinooti...")
sd.play(bass_note, SAMPLE_RATE)
sd.wait()
# Pehme, atmosfääriline pad-heli
pad_note = synth.generate_note(
frequency=440, # A4 noot
duration=5.0,
waveform='triangle',
adsr_params=(1.0, 0.5, 0.7, 1.5)
)
print("Mängitakse süntesaatori pad-nooti...")
sd.play(pad_note, SAMPLE_RATE)
sd.wait()
# Lihtne meloodia
melody = [
('C4', 261.63, 0.4),
('D4', 293.66, 0.4),
('E4', 329.63, 0.4),
('C4', 261.63, 0.8)
]
final_melody = []
for note, freq, dur in melody:
sound = synth.generate_note(freq, dur, 'square', adsr_params=(0.01, 0.1, 0.2, 0.1), filter_params={'cutoff': 1500})
final_melody.append(sound)
full_melody_wave = np.concatenate(final_melody)
print("Mängitakse lühikest meloodiat...")
sd.play(full_melody_wave, SAMPLE_RATE)
sd.wait()
See lihtne klass demonstreerib võimsalt põhimõtteid, mida oleme käsitlenud. Soovitan teil sellega eksperimenteerida. Proovige erinevaid lainekujusid, kohandage ADSR-parameetreid ja muutke filtri lõikesagedust, et näha, kui radikaalselt saate heli muuta.
Lisaks põhitõdedele: Kuhu edasi minna?
Oleme puudutanud vaid helisünteesi ja DSP sügava ja rahuldustpakkuva valdkonna pinda. Kui see on teie huvi äratanud, siis siin on mõned edasijõudnute teemad, mida uurida:
- Lainetabeli süntees: Matemaatiliselt täiuslike kujundite asemel kasutab see tehnika eelnevalt salvestatud, ühe tsükli lainekujusid ostsillaatori allikana, võimaldades uskumatult keerukaid ja arenevaid tämbereid.
- Granulaarsüntees: Loob uusi helisid, dekonstrueerides olemasoleva helinäite pisikesteks fragmentideks (graanuliteks) ja seejärel neid ümber korraldades, venitades ja helikõrgust muutes. See on fantastiline atmosfääritekstuuride ja padjade loomiseks.
- Füüsikaline modelleerimissüntees: Põnev lähenemine, mis püüab luua heli, modelleerides matemaatiliselt instrumendi füüsikalisi omadusi – kitarri keelt, klarneti toru, trummi membraani.
- Reaalajas helitöötlus: Teegid nagu PyAudio ja SoundCard võimaldavad teil töötada helivoogudega mikrofonidest või muudest sisenditest reaalajas, avades ukse live-efektidele, interaktiivsetele installatsioonidele ja muule.
- Masinõpe helis: AI ja süvaõpe revolutsioneerivad heli. Mudelid saavad genereerida uut muusikat, sünteesida realistlikku inimkõnet või isegi eraldada üksikuid instrumente miksitud laulust.
Järeldus
Oleme rännanud digitaalse heli põhiiseloomust kuni funktsionaalse süntesaatori ehitamiseni. Õppisime genereerima puhtaid ja keerukaid lainekujusid Pythoni, NumPy ja SciPy abil. Avastasime, kuidas anda oma helidele elu ja kuju ADSR-ümbrikute abil, vormida nende iseloomu digitaalsete filtritega ja lisada dünaamilist liikumist modulatsiooniga. Kirjutatud kood ei ole lihtsalt tehniline harjutus; see on loominguline tööriist.
Pythoni võimas teaduslik virn muudab selle silmapaistvaks platvormiks helimaailmas õppimiseks, eksperimenteerimiseks ja loomiseks. Olenemata sellest, kas teie eesmärk on luua projektile kohandatud heliefekt, ehitada muusikariist või lihtsalt mõista iga päev kuuldavate helide taga olevat tehnoloogiat, on siin õpitud põhimõtted teie lähtepunktiks. Nüüd on teie kord eksperimenteerida. Alustage nende tehnikate kombineerimist, proovige uusi parameetreid ja kuulake tähelepanelikult tulemusi. Heli tohutu universum on nüüd teie käeulatuses – mida te loote?