পাইথন ব্যবহার করে অডিও সিন্থেসিস ও ডিজিটাল সিগন্যাল প্রসেসিং (ডিএসপি)-এর জগত অন্বেষণ করুন। ওয়েভফর্ম তৈরি করা, ফিল্টার প্রয়োগ করা এবং শুরু থেকে শব্দ তৈরি করা শিখুন।
শব্দ উন্মোচন: অডিও সিন্থেসিস এবং ডিজিটাল সিগন্যাল প্রসেসিংয়ের জন্য পাইথনে একটি গভীর অনুসন্ধান
আপনার হেডফোনে শোনা সঙ্গীত থেকে শুরু করে ভিডিও গেমসের নিমগ্ন সাউন্ডস্কেপ এবং আমাদের ডিভাইসের ভয়েস অ্যাসিস্ট্যান্ট পর্যন্ত, ডিজিটাল অডিও আধুনিক জীবনের একটি অবিচ্ছেদ্য অংশ। কিন্তু আপনি কি কখনো ভেবে দেখেছেন এই শব্দগুলি কীভাবে তৈরি হয়? এটি কোনো জাদু নয়; এটি গণিত, পদার্থবিদ্যা এবং কম্পিউটার বিজ্ঞানের এক আকর্ষণীয় মিশ্রণ যা ডিজিটাল সিগন্যাল প্রসেসিং (ডিএসপি) নামে পরিচিত। আজ, আমরা পর্দা সরিয়ে দেখাবো কীভাবে পাইথনের শক্তিকে কাজে লাগিয়ে শুরু থেকে শব্দ তৈরি, পরিবর্তন এবং সিন্থেসাইজ করা যায়।
এই গাইডটি ডেভেলপার, ডেটা সায়েন্টিস্ট, সুরকার, শিল্পী এবং কোড ও সৃজনশীলতার সংযোগস্থলে আগ্রহী যে কারো জন্য। আপনাকে ডিএসপি বিশেষজ্ঞ বা একজন অভিজ্ঞ অডিও ইঞ্জিনিয়ার হওয়ার দরকার নেই। পাইথন সম্পর্কে একটি মৌলিক ধারণা থাকলে, আপনি শীঘ্রই আপনার নিজস্ব অনন্য সাউন্ডস্কেপ তৈরি করতে পারবেন। আমরা ডিজিটাল অডিওর মৌলিক বিল্ডিং ব্লকগুলি অন্বেষণ করবো, ক্লাসিক ওয়েভফর্ম তৈরি করবো, সেগুলিকে এনভেলপ এবং ফিল্টার দিয়ে আকার দেবো এবং এমনকি একটি মিনি-সিন্থেসাইজারও তৈরি করবো। চলুন, কম্পিউটেশনাল অডিওর প্রাণবন্ত জগতে আমাদের যাত্রা শুরু করি।
ডিজিটাল অডিওর বিল্ডিং ব্লকগুলি বোঝা
এক লাইন কোড লেখার আগে, আমাদের বুঝতে হবে কম্পিউটারে শব্দ কীভাবে উপস্থাপিত হয়। বাস্তব জগতে, শব্দ চাপের একটি অবিচ্ছিন্ন অ্যানালগ তরঙ্গ। কম্পিউটার, ডিজিটাল হওয়ার কারণে, একটি অবিচ্ছিন্ন তরঙ্গ সংরক্ষণ করতে পারে না। পরিবর্তে, তারা প্রতি সেকেন্ডে তরঙ্গের হাজার হাজার স্ন্যাপশট, বা স্যাম্পল নেয়। এই প্রক্রিয়াটিকে স্যাম্পলিং বলা হয়।
স্যাম্পল রেট
স্যাম্পল রেট প্রতি সেকেন্ডে কতগুলি স্যাম্পল নেওয়া হয় তা নির্ধারণ করে। এটি হার্জ (Hz)-এ পরিমাপ করা হয়। একটি উচ্চতর স্যাম্পল রেট মূল শব্দ তরঙ্গের আরও নির্ভুল উপস্থাপনা দেয়, যার ফলে উচ্চতর বিশ্বস্ততার অডিও পাওয়া যায়। সাধারণ স্যাম্পল রেটগুলির মধ্যে রয়েছে:
- 44100 Hz (44.1 kHz): অডিও সিডির জন্য আদর্শ। এটি নাইকুইস্ট-শ্যানন স্যাম্পলিং থিওরেমের উপর ভিত্তি করে নির্বাচিত হয়েছে, যা বলে যে স্যাম্পল রেট আপনার ক্যাপচার করতে চাওয়া সর্বোচ্চ ফ্রিকোয়েন্সির অন্তত দ্বিগুণ হতে হবে। যেহেতু মানুষের শ্রুতিসীমা প্রায় 20,000 Hz পর্যন্ত, তাই 44.1 kHz একটি পর্যাপ্ত বাফার প্রদান করে।
- 48000 Hz (48 kHz): পেশাদার ভিডিও এবং ডিজিটাল অডিও ওয়ার্কস্টেশন (DAWs)-এর জন্য আদর্শ।
- 96000 Hz (96 kHz): আরও বেশি নির্ভুলতার জন্য উচ্চ-রেজোলিউশন অডিও প্রোডাকশনে ব্যবহৃত হয়।
আমাদের উদ্দেশ্য পূরণের জন্য, আমরা প্রাথমিকভাবে 44100 Hz ব্যবহার করবো, কারণ এটি গুণমান এবং কম্পিউটেশনাল দক্ষতার মধ্যে একটি চমৎকার ভারসাম্য প্রদান করে।
বিট ডেপথ
যদি স্যাম্পল রেট সময়ের রেজোলিউশন নির্ধারণ করে, তবে বিট ডেপথ অ্যাম্পলিটিউড (শব্দ উচ্চতা)-এর রেজোলিউশন নির্ধারণ করে। প্রতিটি স্যাম্পল একটি সংখ্যা যা সেই নির্দিষ্ট মুহূর্তে তরঙ্গের অ্যাম্পলিটিউডকে উপস্থাপন করে। বিট ডেপথ হল সেই সংখ্যাটি সংরক্ষণ করতে ব্যবহৃত বিটগুলির সংখ্যা। একটি উচ্চতর বিট ডেপথ আরও বেশি সম্ভাব্য অ্যাম্পলিটিউড মানের অনুমতি দেয়, যার ফলে একটি বৃহত্তর ডাইনামিক রেঞ্জ (সবচেয়ে শান্ত এবং সবচেয়ে উচ্চ শব্দগুলির মধ্যে পার্থক্য) এবং একটি কম নয়েজ ফ্লোর তৈরি হয়।
- 16-bit: সিডির জন্য আদর্শ, 65,536 সম্ভাব্য অ্যাম্পলিটিউড স্তর প্রদান করে।
- 24-bit: পেশাদার অডিও প্রোডাকশনের জন্য আদর্শ, 16.7 মিলিয়নেরও বেশি স্তর প্রদান করে।
যখন আমরা নামপাই (NumPy)-এর মতো লাইব্রেরি ব্যবহার করে পাইথনে অডিও তৈরি করি, তখন আমরা সাধারণত সর্বোচ্চ নির্ভুলতার জন্য ফ্লোটিং-পয়েন্ট সংখ্যা (যেমন -1.0 থেকে 1.0 এর মধ্যে) নিয়ে কাজ করি। ফাইল সেভ করার সময় বা হার্ডওয়্যারের মাধ্যমে প্লেব্যাক করার সময় এগুলি একটি নির্দিষ্ট বিট ডেপথে (যেমন 16-বিট পূর্ণসংখ্যা) রূপান্তরিত হয়।
চ্যানেল
এটি কেবল অডিও স্ট্রিমের সংখ্যাকে বোঝায়। মনো অডিওতে একটি চ্যানেল থাকে, যখন স্টেরিও অডিওতে দুটি (বাম এবং ডান) থাকে, যা স্থান এবং দিকনির্দেশনার অনুভূতি তৈরি করে।
আপনার পাইথন এনভায়রনমেন্ট সেট আপ করা
শুরু করার জন্য, আমাদের কয়েকটি অপরিহার্য পাইথন লাইব্রেরি প্রয়োজন। এগুলি সংখ্যাসূচক গণনা, সিগন্যাল প্রসেসিং, ভিজ্যুয়ালাইজেশন এবং অডিও প্লেব্যাকের জন্য আমাদের টুলকিট তৈরি করে।
আপনি pip ব্যবহার করে এগুলি ইনস্টল করতে পারেন:
pip install numpy scipy matplotlib sounddevice
আসুন তাদের ভূমিকা সংক্ষেপে পর্যালোচনা করি:
- NumPy: পাইথনে বৈজ্ঞানিক কম্পিউটিংয়ের মূল ভিত্তি। আমরা এটি ব্যবহার করে সংখ্যার অ্যারে তৈরি ও পরিবর্তন করবো, যা আমাদের অডিও সিগন্যালগুলিকে উপস্থাপন করবে।
- SciPy: NumPy-এর উপর নির্মিত, এটি ওয়েভফর্ম জেনারেশন এবং ফিল্টারিং সহ সিগন্যাল প্রসেসিংয়ের জন্য অ্যালগরিদমগুলির একটি বিশাল সংগ্রহ সরবরাহ করে।
- Matplotlib: পাইথনের প্রাথমিক প্লটিং লাইব্রেরি। এটি আমাদের ওয়েভফর্মগুলিকে ভিজ্যুয়ালাইজ করতে এবং আমাদের প্রসেসিংয়ের প্রভাবগুলি বুঝতে অমূল্য।
- SoundDevice: আপনার কম্পিউটারের স্পিকারের মাধ্যমে আমাদের NumPy অ্যারেগুলিকে অডিও হিসাবে প্লেব্যাক করার জন্য একটি সুবিধাজনক লাইব্রেরি। এটি একটি সহজ এবং ক্রস-প্ল্যাটফর্ম ইন্টারফেস সরবরাহ করে।
ওয়েভফর্ম জেনারেশন: সিন্থেসিসের প্রাণকেন্দ্র
সমস্ত শব্দ, যত জটিলই হোক না কেন, সরল, মৌলিক ওয়েভফর্মগুলির সংমিশ্রণে ভাঙা যেতে পারে। এগুলি আমাদের সোনিক প্যালেটের প্রাথমিক রঙ। চলুন জেনে নিই কীভাবে এগুলি তৈরি করা যায়।
সাইন ওয়েভ: বিশুদ্ধতম টোন
সাইন ওয়েভ হল সমস্ত শব্দের পরম বিল্ডিং ব্লক। এটি কোনো ওভারটোন বা হারমোনিকস ছাড়াই একটি একক ফ্রিকোয়েন্সি উপস্থাপন করে। এটি খুব মসৃণ, পরিষ্কার শোনায় এবং প্রায়শই 'বাঁশির মতো' হিসাবে বর্ণনা করা হয়। গাণিতিক সূত্রটি হল:
y(t) = Amplitude * sin(2 * π * frequency * t)
যেখানে 't' হল সময়। আসুন এটিকে পাইথন কোডে অনুবাদ করি।
import numpy as np
import sounddevice as sd
import matplotlib.pyplot as plt
# --- Global Parameters ---
SAMPLE_RATE = 44100 # samples per second
DURATION = 3.0 # seconds
# --- Waveform Generation ---
def generate_sine_wave(frequency, duration, sample_rate, amplitude=0.5):
"""Generate a sine wave.
Args:
frequency (float): The frequency of the sine wave in Hz.
duration (float): The duration of the wave in seconds.
sample_rate (int): The sample rate in Hz.
amplitude (float): The amplitude of the wave (0.0 to 1.0).
Returns:
np.ndarray: The generated sine wave as a NumPy array.
"""
# Create an array of time points
t = np.linspace(0, duration, int(sample_rate * duration), False)
# Generate the sine wave
# 2 * pi * frequency is the angular frequency
wave = amplitude * np.sin(2 * np.pi * frequency * t)
return wave
# --- Example Usage ---
if __name__ == "__main__":
# Generate a 440 Hz (A4 note) sine wave
frequency_a4 = 440.0
sine_wave = generate_sine_wave(frequency_a4, DURATION, SAMPLE_RATE)
print("Playing 440 Hz sine wave...")
# Play the sound
sd.play(sine_wave, SAMPLE_RATE)
sd.wait() # Wait for the sound to finish playing
print("Playback finished.")
# --- Visualization ---
# Plot a small portion of the wave to see its shape
plt.figure(figsize=(12, 4))
plt.plot(sine_wave[:500])
plt.title("Sine Wave (440 Hz)")
plt.xlabel("Sample")
plt.ylabel("Amplitude")
plt.grid(True)
plt.show()
এই কোডে, np.linspace সময় অক্ষকে প্রতিনিধিত্বকারী একটি অ্যারে তৈরি করে। আমরা তখন এই টাইম অ্যারেতে সাইন ফাংশন প্রয়োগ করি, যা কাঙ্ক্ষিত ফ্রিকোয়েন্সি দ্বারা স্কেল করা হয়। ফলাফল হল একটি NumPy অ্যারে যেখানে প্রতিটি উপাদান আমাদের শব্দ তরঙ্গের একটি স্যাম্পল। আমরা তখন sounddevice দিয়ে এটি প্লে করতে পারি এবং matplotlib দিয়ে ভিজ্যুয়ালাইজ করতে পারি।
অন্যান্য মৌলিক ওয়েভফর্ম অন্বেষণ
যদিও সাইন ওয়েভ বিশুদ্ধ, এটি সবসময় সবচেয়ে আকর্ষণীয় হয় না। অন্যান্য মৌলিক ওয়েভফর্মগুলি হারমোনিক্সে সমৃদ্ধ, যা তাদের আরও জটিল এবং উজ্জ্বল চরিত্র (টিম্বার) দেয়। scipy.signal মডিউল এগুলি তৈরি করার জন্য সুবিধাজনক ফাংশন সরবরাহ করে।
স্কয়ার ওয়েভ
একটি স্কয়ার ওয়েভ তার সর্বোচ্চ এবং সর্বনিম্ন অ্যাম্পলিটিউডের মধ্যে তাৎক্ষণিকভাবে ঝাঁপিয়ে পড়ে। এতে শুধুমাত্র বিজোড় সংখ্যক হারমোনিক্স থাকে। এটির একটি উজ্জ্বল, রিডি এবং কিছুটা 'ফাঁপা' বা 'ডিজিটাল' শব্দ রয়েছে, যা প্রায়শই প্রাথমিক ভিডিও গেমের সঙ্গীতের সাথে যুক্ত থাকে।
from scipy import signal
# Generate a square wave
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()
সাওটুথ ওয়েভ
একটি সাউটুথ ওয়েভ রৈখিকভাবে উপরে ওঠে এবং তারপরে তাৎক্ষণিকভাবে তার সর্বনিম্ন মানে নেমে আসে (বা এর বিপরীত)। এটি অবিশ্বাস্যভাবে সমৃদ্ধ, এতে সমস্ত পূর্ণসংখ্যা হারমোনিক্স (জোড় এবং বিজোড় উভয়ই) থাকে। এটি খুব উজ্জ্বল, গুঞ্জনযুক্ত শোনায় এবং সাবট্র্যাক্টিভ সিন্থেসিসের জন্য একটি দুর্দান্ত শুরু করার পয়েন্ট, যা আমরা পরে কভার করব।
# Generate a sawtooth wave
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()
ট্রায়াঙ্গেল ওয়েভ
একটি ট্রায়াঙ্গেল ওয়েভ রৈখিকভাবে উপরে ওঠে এবং নীচে নামে। একটি স্কয়ার ওয়েভের মতো, এতে শুধুমাত্র বিজোড় হারমোনিক্স থাকে, তবে তাদের অ্যাম্পলিটিউড অনেক দ্রুত হ্রাস পায়। এটি এটিকে একটি স্কয়ার ওয়েভের চেয়ে নরম এবং আরও মৃদু শব্দ দেয়, যা একটি সাইন ওয়েভের কাছাকাছি কিন্তু সামান্য 'বডি' সহ।
# Generate a triangle wave (a sawtooth with 0.5 width)
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()
হোয়াইট নয়েজ: এলোমেলোতার শব্দ
হোয়াইট নয়েজ হল একটি সিগন্যাল যা প্রতিটি ফ্রিকোয়েন্সিতে সমান শক্তি ধারণ করে। এটি স্ট্যাটিক বা একটি জলপ্রপাতের 'শশশ' শব্দের মতো শোনায়। এটি সাউন্ড ডিজাইনে পারকাশন শব্দ (যেমন হাই-হ্যাটস এবং স্নায়ার্স) এবং বায়ুমণ্ডলীয় প্রভাব তৈরির জন্য অবিশ্বাস্যভাবে কার্যকর। এটি তৈরি করা অত্যন্ত সহজ।
# Generate white noise
num_samples = int(SAMPLE_RATE * DURATION)
white_noise = np.random.uniform(-1, 1, num_samples)
# sd.play(white_noise, SAMPLE_RATE)
# sd.wait()
অ্যাডিটিভ সিন্থেসিস: জটিলতা তৈরি করা
যেখানে অ্যাডিটিভ সিন্থেসিস সাইন ওয়েভ যোগ করে শব্দ তৈরি করে, সেখানে সাবট্র্যাক্টিভ সিন্থেসিস বিপরীত উপায়ে কাজ করে। আমরা একটি হারমোনিকভাবে সমৃদ্ধ সিগন্যাল (যেমন সাউটুথ ওয়েভ বা হোয়াইট নয়েজ) দিয়ে শুরু করি এবং তারপরে ফিল্টার ব্যবহার করে নির্দিষ্ট ফ্রিকোয়েন্সিগুলিকে সরিয়ে দিই বা কমিয়ে দিই। এটি একজন ভাস্করের মার্বেলের ব্লক দিয়ে শুরু করে একটি আকৃতি প্রকাশ করার জন্য খোদাই করার মতো।
চলুন, একটি মৌলিক ফ্রিকোয়েন্সির প্রথম কয়েকটি হারমোনিক্স যোগ করে আরও জটিল টোন তৈরি করি।
def generate_complex_tone(fundamental_freq, duration, sample_rate):
t = np.linspace(0, duration, int(sample_rate * duration), False)
# Start with the fundamental frequency
tone = 0.5 * np.sin(2 * np.pi * fundamental_freq * t)
# Add harmonics (overtones)
# 2nd harmonic (octave higher), lower amplitude
tone += 0.25 * np.sin(2 * np.pi * (2 * fundamental_freq) * t)
# 3rd harmonic, even lower amplitude
tone += 0.12 * np.sin(2 * np.pi * (3 * fundamental_freq) * t)
# 5th harmonic
tone += 0.08 * np.sin(2 * np.pi * (5 * fundamental_freq) * t)
# Normalize the waveform to be between -1 and 1
tone = tone / np.max(np.abs(tone))
return tone
# --- Example Usage ---
complex_tone = generate_complex_tone(220, DURATION, SAMPLE_RATE)
sd.play(complex_tone, SAMPLE_RATE)
sd.wait()
কোন হারমোনিক্স যোগ করবেন এবং কোন অ্যাম্পলিটিউডে যোগ করবেন তা সাবধানে নির্বাচন করে, আপনি বাস্তব বিশ্বের বাদ্যযন্ত্রের শব্দ অনুকরণ করতে শুরু করতে পারেন। এই সাধারণ উদাহরণটি একটি সরল সাইন ওয়েভের চেয়ে অনেক সমৃদ্ধ এবং আরও আকর্ষণীয় শোনাচ্ছে।
এনভেলপ (ADSR) দিয়ে শব্দকে আকার দেওয়া
এখন পর্যন্ত, আমাদের শব্দগুলি হঠাৎ করে শুরু হয় এবং থামে। তাদের পুরো সময় জুড়ে একটি ধ্রুবক ভলিউম থাকে, যা খুব অস্বাভাবিক এবং রোবোটিক শোনায়। বাস্তব জগতে, শব্দ সময়ের সাথে সাথে বিকশিত হয়। একটি পিয়ানো নোটে একটি তীক্ষ্ণ, উচ্চস্বরের শুরু থাকে যা দ্রুত ম্লান হয়ে যায়, যখন একটি বেহালায় বাজানো নোট ধীরে ধীরে ভলিউমে ফুলে উঠতে পারে। আমরা একটি অ্যাম্পলিটিউড এনভেলপ ব্যবহার করে এই গতিশীল বিবর্তন নিয়ন্ত্রণ করি।
এডিএসআর মডেল
সবচেয়ে সাধারণ ধরণের এনভেলপ হল এডিএসআর এনভেলপ, যার চারটি পর্যায় রয়েছে:
- অ্যাটাক (Attack): শব্দ নীরব অবস্থা থেকে তার সর্বোচ্চ অ্যাম্পলিটিউডে পৌঁছাতে যে সময় নেয়। একটি দ্রুত অ্যাটাক একটি পারকাশনমূলক, তীক্ষ্ণ শব্দ তৈরি করে (যেমন ড্রাম হিট)। একটি ধীর অ্যাটাক একটি মৃদু, ফুলে ওঠা শব্দ তৈরি করে (যেমন স্ট্রিং প্যাড)।
- ডিকে (Decay): শব্দ সর্বোচ্চ অ্যাটাক লেভেল থেকে সাসটেইন লেভেলে নামতে যে সময় নেয়।
- সাসটেইন (Sustain): যে অ্যাম্পলিটিউড লেভেল শব্দ ধরে রাখে যতক্ষণ নোট বাজানো হয়। এটি একটি স্তর, সময় নয়।
- রিলিজ (Release): নোট ছেড়ে দেওয়ার পর শব্দ সাসটেইন লেভেল থেকে নীরবতায় ম্লান হয়ে যেতে যে সময় নেয়। একটি দীর্ঘ রিলিজ শব্দকে দীর্ঘায়িত করে, যেমন সাসটেইন প্যাডেল ধরে রাখা পিয়ানো নোট।
পাইথনে একটি এডিএসআর এনভেলপ বাস্তবায়ন করা
আমরা একটি NumPy অ্যারে হিসাবে একটি ADSR এনভেলপ তৈরি করার জন্য একটি ফাংশন বাস্তবায়ন করতে পারি। তারপর আমরা সাধারণ এলিমেন্ট-ওয়াইজ গুণন এর মাধ্যমে আমাদের ওয়েভফর্মে এটি প্রয়োগ করি।
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:
# If times are too long, adjust them proportionally
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
# Generate each part of the envelope
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])
# --- Example Usage: Plucky vs. Pad Sound ---
# Pluck sound (fast attack, quick decay, no sustain)
pluck_envelope = adsr_envelope(DURATION, SAMPLE_RATE, 0.01, 0.2, 0.0, 0.5)
# Pad sound (slow attack, long release)
pad_envelope = adsr_envelope(DURATION, SAMPLE_RATE, 0.5, 0.2, 0.7, 1.0)
# Generate a harmonically rich sawtooth wave to apply envelopes to
saw_wave_for_env = generate_complex_tone(220, DURATION, SAMPLE_RATE)
# Apply envelopes
plucky_sound = saw_wave_for_env * pluck_envelope
pad_sound = saw_wave_for_env * pad_envelope
print("Playing plucky sound...")
sd.play(plucky_sound, SAMPLE_RATE)
sd.wait()
print("Playing pad sound...")
sd.play(pad_sound, SAMPLE_RATE)
sd.wait()
# Visualize the envelopes
plt.figure(figsize=(12, 6))
plt.subplot(2, 1, 1)
plt.plot(pluck_envelope)
plt.title("Pluck ADSR Envelope")
plt.subplot(2, 1, 2)
plt.plot(pad_envelope)
plt.title("Pad ADSR Envelope")
plt.tight_layout()
plt.show()
লক্ষ্য করুন কীভাবে একই অন্তর্নিহিত ওয়েভফর্ম কেবল একটি ভিন্ন এনভেলপ প্রয়োগ করার মাধ্যমেই তার চরিত্রকে নাটকীয়ভাবে পরিবর্তন করে। এটি সাউন্ড ডিজাইনের একটি মৌলিক কৌশল।
ডিজিটাল ফিল্টারিংয়ের ভূমিকা (সাবট্র্যাক্টিভ সিন্থেসিস)
যেখানে অ্যাডিটিভ সিন্থেসিস সাইন ওয়েভ যোগ করে শব্দ তৈরি করে, সেখানে সাবট্র্যাক্টিভ সিন্থেসিস বিপরীত উপায়ে কাজ করে। আমরা একটি হারমোনিকভাবে সমৃদ্ধ সিগন্যাল (যেমন সাউটুথ ওয়েভ বা হোয়াইট নয়েজ) দিয়ে শুরু করি এবং তারপরে ফিল্টার ব্যবহার করে নির্দিষ্ট ফ্রিকোয়েন্সিগুলিকে সরিয়ে দিই বা কমিয়ে দিই। এটি একজন ভাস্করের মার্বেলের ব্লক দিয়ে শুরু করে একটি আকৃতি প্রকাশ করার জন্য খোদাই করার মতো।
প্রধান ফিল্টার প্রকারভেদ
- লো-পাস ফিল্টার: সিন্থেসিসে এটি সবচেয়ে সাধারণ ফিল্টার। এটি একটি নির্দিষ্ট 'কাটঅফ' পয়েন্টের নীচের ফ্রিকোয়েন্সিগুলিকে অতিক্রম করতে দেয় যখন এর উপরের ফ্রিকোয়েন্সিগুলিকে কমিয়ে দেয়। এটি একটি শব্দকে আরও গাঢ়, উষ্ণ বা বেশি চাপা শব্দে পরিণত করে।
- হাই-পাস ফিল্টার: লো-পাস ফিল্টারের বিপরীত। এটি কাটঅফের উপরের ফ্রিকোয়েন্সিগুলিকে অতিক্রম করতে দেয়, খাদ এবং নিম্ন-প্রান্তের ফ্রিকোয়েন্সিগুলিকে সরিয়ে দেয়। এটি একটি শব্দকে পাতলা বা টিনি করে তোলে।
- ব্যান্ড-পাস ফিল্টার: শুধুমাত্র একটি নির্দিষ্ট ফ্রিকোয়েন্সি ব্যান্ডকে অতিক্রম করতে দেয়, উচ্চ এবং নিম্ন উভয় ফ্রিকোয়েন্সিই বাদ দেয়। এটি একটি 'টেলিফোন' বা 'রেডিও' প্রভাব তৈরি করতে পারে।
- ব্যান্ড-স্টপ (নচ) ফিল্টার: ব্যান্ড-পাসের বিপরীত। এটি ফ্রিকোয়েন্সিগুলির একটি নির্দিষ্ট ব্যান্ডকে সরিয়ে দেয়।
SciPy দিয়ে ফিল্টার বাস্তবায়ন
scipy.signal লাইব্রেরি ডিজিটাল ফিল্টার ডিজাইন এবং প্রয়োগের জন্য শক্তিশালী সরঞ্জাম সরবরাহ করে। আমরা বাটারওয়ার্থ ফিল্টার নামক একটি সাধারণ প্রকার ব্যবহার করব, যা এর পাসব্যান্ডে সমতল প্রতিক্রিয়ার জন্য পরিচিত।
এই প্রক্রিয়ায় দুটি ধাপ রয়েছে: প্রথমত, এর সহগগুলি পেতে ফিল্টার ডিজাইন করা এবং দ্বিতীয়ত, আমাদের অডিও সিগন্যালে সেই সহগগুলি প্রয়োগ করা।
from scipy.signal import butter, lfilter, freqz
def butter_lowpass_filter(data, cutoff, fs, order=5):
"""Apply a low-pass Butterworth filter to a signal."""
nyquist = 0.5 * fs
normal_cutoff = cutoff / nyquist
# Get the filter coefficients
b, a = butter(order, normal_cutoff, btype='low', analog=False)
y = lfilter(b, a, data)
return y
# --- Example Usage ---
# Start with a rich signal: sawtooth wave
saw_wave_rich = 0.5 * signal.sawtooth(2 * np.pi * 220 * np.linspace(0, DURATION, int(SAMPLE_RATE * DURATION), False))
print("Playing original sawtooth wave...")
sd.play(saw_wave_rich, SAMPLE_RATE)
sd.wait()
# Apply a low-pass filter with a cutoff of 800 Hz
filtered_saw = butter_lowpass_filter(saw_wave_rich, cutoff=800, fs=SAMPLE_RATE, order=6)
print("Playing filtered sawtooth wave...")
sd.play(filtered_saw, SAMPLE_RATE)
sd.wait()
# --- Visualization of the filter's frequency response ---
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("Low-pass Filter Frequency Response")
plt.xlabel('Frequency [Hz]')
plt.grid()
plt.show()
মূল এবং ফিল্টার করা তরঙ্গগুলির মধ্যে পার্থক্য শুনুন। মূল তরঙ্গটি উজ্জ্বল এবং গুঞ্জনময়; ফিল্টার করা সংস্করণটি অনেক নরম এবং গাঢ়, কারণ উচ্চ-ফ্রিকোয়েন্সি হারমোনিক্সগুলি সরানো হয়েছে। একটি লো-পাস ফিল্টারের কাটঅফ ফ্রিকোয়েন্সি সুইপ করা ইলেকট্রনিক সঙ্গীতে সবচেয়ে অভিব্যক্তিপূর্ণ এবং সাধারণ কৌশলগুলির মধ্যে একটি।
মডুলেশন: গতি এবং জীবন যোগ করা
স্থির শব্দ বিরক্তিকর। মডুলেশন হল গতিশীল, বিবর্তিত এবং আকর্ষণীয় শব্দ তৈরির চাবিকাঠি। নীতিটি সহজ: একটি সংকেত (মডুলেটর) ব্যবহার করে অন্য একটি সংকেতের (ক্যারিয়ার) একটি প্যারামিটার নিয়ন্ত্রণ করা। একটি সাধারণ মডুলেটর হল লো-ফ্রিকোয়েন্সি ওসিলেটর (LFO), যা মানুষের শ্রুতিসীমার নিচে ফ্রিকোয়েন্সি সহ একটি ওসিলেটর (যেমন 0.1 Hz থেকে 20 Hz)।
অ্যাম্পলিটিউড মডুলেশন (AM) এবং ট্রেমোলো
এটি তখন যখন আমরা আমাদের শব্দের অ্যাম্পলিটিউড নিয়ন্ত্রণ করতে একটি LFO ব্যবহার করি। ফলাফল হল ভলিউমে একটি ছন্দময় স্পন্দন, যা ট্রেমোলো নামে পরিচিত।
# Carrier wave (the sound we hear)
carrier_freq = 300
carrier = generate_sine_wave(carrier_freq, DURATION, SAMPLE_RATE)
# Modulator LFO (controls the volume)
lfo_freq = 5 # 5 Hz LFO
modulator = generate_sine_wave(lfo_freq, DURATION, SAMPLE_RATE, amplitude=1.0)
# Create tremolo effect
# We scale the modulator to be from 0 to 1
tremolo_modulator = (modulator + 1) / 2
tremolo_sound = carrier * tremolo_modulator
print("Playing tremolo effect...")
sd.play(tremolo_sound, SAMPLE_RATE)
sd.wait()
ফ্রিকোয়েন্সি মডুলেশন (FM) এবং ভাইব্রাটো
এটি তখন যখন আমরা আমাদের শব্দের ফ্রিকোয়েন্সি নিয়ন্ত্রণ করতে একটি LFO ব্যবহার করি। ফ্রিকোয়েন্সির একটি ধীর, সূক্ষ্ম মডুলেশন ভাইব্রাটো তৈরি করে, যা সুরকার এবং বেহালাবাদকরা অভিব্যক্তি যোগ করার জন্য পিচের মৃদু কম্পন ব্যবহার করে।
# Create vibrato effect
t = np.linspace(0, DURATION, int(SAMPLE_RATE * DURATION), False)
carrier_freq = 300
lfo_freq = 7
modulation_depth = 10 # How much the frequency will vary
# The LFO will be added to the carrier frequency
modulator_vibrato = modulation_depth * np.sin(2 * np.pi * lfo_freq * t)
# The instantaneous frequency changes over time
instantaneous_freq = carrier_freq + modulator_vibrato
# We need to integrate the frequency to get the phase
phase = np.cumsum(2 * np.pi * instantaneous_freq / SAMPLE_RATE)
vibrato_sound = 0.5 * np.sin(phase)
print("Playing vibrato effect...")
sd.play(vibrato_sound, SAMPLE_RATE)
sd.wait()
এটি এফএম সিন্থেসিসের একটি সরলীকৃত সংস্করণ। যখন LFO ফ্রিকোয়েন্সি শ্রবণযোগ্য পরিসরে বৃদ্ধি করা হয়, তখন এটি জটিল সাইডব্যান্ড ফ্রিকোয়েন্সি তৈরি করে, যার ফলে সমৃদ্ধ, ঘণ্টার মতো এবং ধাতব সুর তৈরি হয়। এটি ইয়ামাহা ডিএক্স৭ (Yamaha DX7)-এর মতো সিন্থেসাইজারের আইকনিক শব্দের ভিত্তি।
সব একসাথে: একটি মিনি সিন্থেসাইজার প্রকল্প
আমরা যা কিছু শিখেছি তা একটি সহজ, কার্যকরী সিন্থেসাইজার ক্লাসে একত্রিত করি। এটি আমাদের ওসিলেটর, এনভেলপ এবং ফিল্টারকে একটি একক, পুনরায় ব্যবহারযোগ্য বস্তুতে আবদ্ধ করবে।
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):
"""Generate a single synthesized note."""
num_samples = int(duration * self.sample_rate)
t = np.linspace(0, duration, num_samples, False)
# 1. Oscillator
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("Unsupported waveform")
# 2. Envelope
attack, decay, sustain, release = adsr_params
envelope = adsr_envelope(duration, self.sample_rate, attack, decay, sustain, release)
# Ensure envelope and wave are the same length
min_len = min(len(wave), len(envelope))
wave = wave[:min_len] * envelope[:min_len]
# 3. Filter (optional)
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)
# ... could add high-pass etc. here
# Normalize to 0.5 amplitude
return wave * 0.5
# --- Example Usage of the Synth ---
synth = MiniSynth()
# A bright, plucky bass sound
bass_note = synth.generate_note(
frequency=110, # A2 note
duration=1.5,
waveform='sawtooth',
adsr_params=(0.01, 0.3, 0.0, 0.2),
filter_params={'cutoff': 600, 'order': 6}
)
print("Playing synth bass note...")
sd.play(bass_note, SAMPLE_RATE)
sd.wait()
# A soft, atmospheric pad sound
pad_note = synth.generate_note(
frequency=440, # A4 note
duration=5.0,
waveform='triangle',
adsr_params=(1.0, 0.5, 0.7, 1.5)
)
print("Playing synth pad note...")
sd.play(pad_note, SAMPLE_RATE)
sd.wait()
# A simple melody
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("Playing a short melody...")
sd.play(full_melody_wave, SAMPLE_RATE)
sd.wait()
এই সাধারণ ক্লাসটি আমরা যে নীতিগুলি কভার করেছি তার একটি শক্তিশালী প্রদর্শন। আমি আপনাকে এটি নিয়ে পরীক্ষা করার জন্য উৎসাহিত করি। বিভিন্ন ওয়েভফর্ম ব্যবহার করে দেখুন, ADSR প্যারামিটারগুলি পরিবর্তন করুন এবং ফিল্টার কাটঅফ পরিবর্তন করে দেখুন আপনি কতটা নাটকীয়ভাবে শব্দ পরিবর্তন করতে পারেন।
মৌলিক বিষয়গুলির বাইরে: পরবর্তী ধাপগুলি কী?
অডিও সিন্থেসিস এবং ডিএসপির গভীর এবং ফলপ্রসূ ক্ষেত্রের আমরা কেবল উপরিভাগ ছুঁয়েছি। যদি এটি আপনার আগ্রহ জাগিয়ে তোলে, তবে এখানে কিছু উন্নত বিষয় রয়েছে যা অন্বেষণ করা যেতে পারে:
- ওয়েভটেবল সিন্থেসিস (Wavetable Synthesis): গাণিতিকভাবে নিখুঁত আকার ব্যবহার করার পরিবর্তে, এই কৌশলটি ওসিলেটর উত্স হিসাবে পূর্ব-রেকর্ড করা, একক-চক্রের ওয়েভফর্ম ব্যবহার করে, যা অবিশ্বাস্যভাবে জটিল এবং বিবর্তনশীল টিম্বার তৈরি করতে দেয়।
- গ্র্যানুলার সিন্থেসিস (Granular Synthesis): একটি বিদ্যমান অডিও স্যাম্পলকে ক্ষুদ্র অংশে (গ্রেন) ভেঙে এবং তারপর সেগুলিকে পুনরায় সাজানো, প্রসারিত করা এবং পিচ করার মাধ্যমে নতুন শব্দ তৈরি করে। এটি বায়ুমণ্ডলীয় টেক্সচার এবং প্যাড তৈরির জন্য দুর্দান্ত।
- ফিজিক্যাল মডেলিং সিন্থেসিস (Physical Modeling Synthesis): একটি আকর্ষণীয় পদ্ধতি যা একটি বাদ্যযন্ত্রের ভৌত বৈশিষ্ট্যগুলি - একটি গিটারের স্ট্রিং, একটি ক্লারিনেটের টিউব, একটি ড্রামের মেমব্রেন - গাণিতিকভাবে মডেল করে শব্দ তৈরি করার চেষ্টা করে।
- রিয়েল-টাইম অডিও প্রসেসিং (Real-time Audio Processing): PyAudio এবং SoundCard এর মতো লাইব্রেরিগুলি আপনাকে মাইক্রোফোন বা অন্যান্য ইনপুট থেকে রিয়েল টাইমে অডিও স্ট্রিমগুলির সাথে কাজ করার অনুমতি দেয়, যা লাইভ ইফেক্ট, ইন্টারেক্টিভ ইনস্টলেশন এবং আরও অনেক কিছুর পথ খুলে দেয়।
- অডিওতে মেশিন লার্নিং (Machine Learning in Audio): এআই এবং ডিপ লার্নিং অডিওতে বিপ্লব ঘটাচ্ছে। মডেলগুলি অভিনব সঙ্গীত তৈরি করতে পারে, বাস্তবসম্মত মানুষের বক্তৃতা সিন্থেসাইজ করতে পারে, অথবা এমনকি একটি মিশ্র গান থেকে স্বতন্ত্র যন্ত্রগুলিকেও আলাদা করতে পারে।
উপসংহার
আমরা ডিজিটাল শব্দের মৌলিক প্রকৃতি থেকে একটি কার্যকরী সিন্থেসাইজার তৈরি করা পর্যন্ত যাত্রা করেছি। আমরা পাইথন, NumPy এবং SciPy ব্যবহার করে কীভাবে বিশুদ্ধ এবং জটিল ওয়েভফর্ম তৈরি করতে হয় তা শিখেছি। আমরা ADSR এনভেলপ ব্যবহার করে কীভাবে আমাদের শব্দগুলিকে জীবন ও আকৃতি দিতে হয়, ডিজিটাল ফিল্টার দিয়ে তাদের চরিত্র তৈরি করতে হয় এবং মডুলেশন দিয়ে গতিশীল চলন যোগ করতে হয় তা আবিষ্কার করেছি। আমরা যে কোড লিখেছি তা কেবল একটি প্রযুক্তিগত অনুশীলন নয়; এটি একটি সৃজনশীল সরঞ্জাম।
পাইথনের শক্তিশালী বৈজ্ঞানিক স্ট্যাক এটিকে অডিও জগতে শেখার, পরীক্ষা করার এবং তৈরির জন্য একটি অসামান্য প্ল্যাটফর্ম করে তোলে। আপনার লক্ষ্য একটি প্রকল্পের জন্য একটি কাস্টম সাউন্ড ইফেক্ট তৈরি করা হোক, একটি বাদ্যযন্ত্র তৈরি করা হোক, বা কেবল প্রতিদিন আপনি যে শব্দগুলি শোনেন তার পিছনের প্রযুক্তিটি বোঝা হোক, এখানে আপনি যে নীতিগুলি শিখেছেন তা আপনার শুরুর বিন্দু। এখন, পরীক্ষা করার পালা আপনার। এই কৌশলগুলি একত্রিত করা শুরু করুন, নতুন প্যারামিটার চেষ্টা করুন এবং ফলাফলগুলি মনোযোগ সহকারে শুনুন। শব্দের বিশাল মহাবিশ্ব এখন আপনার হাতের মুঠোয়—আপনি কী তৈরি করবেন?