পাঠ ৪৩ · ৫৭-এর মধ্যে · মডিউল ৯
Home / Courses / Digital Signal Processing / রেট কনভার্সন

স্যাম্পল রেট কনভার্সন — বাস্তব প্রয়োগ

Sample rate conversion — practical applications
১০ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • কেন নন-ইন্টিজার রেট কনভার্সনের জন্য আপস্যাম্পল-তারপর-ডাউনস্যাম্পল পদ্ধতি দরকার
  • একটি একক মিলিত ফিল্টার কীভাবে একইসাথে দুটো কাজ করে — ইন্টারপোলেশন-ইমেজ আর ডেসিমেশন-অ্যালিয়াস দুটোই প্রতিরোধ
  • L40 আর L41-এর বিল্ডিং ব্লক পুনর্ব্যবহার করে একটি সম্পূর্ণ, সংখ্যাগতভাবে যাচাই-করা L/M রেট-কনভার্সন পাইপলাইন বাস্তবায়ন
  • এই কৌশলের একটি বাস্তব-জীবনের প্রেক্ষাপট (অডিও স্যাম্পল-রেট রূপান্তর) — স্পষ্টভাবে ইলাস্ট্রেটিভ হিসেবে চিহ্নিত

১ · কেন নন-ইন্টিজার রেট কনভার্সন দরকার

L40 শুধু পূর্ণসংখ্যা ফ্যাক্টরে ডাউনস্যাম্পল করতে পারে (fs/M), আর L41 শুধু পূর্ণসংখ্যা ফ্যাক্টরে আপস্যাম্পল করতে পারে (fs×L)। কিন্তু বাস্তবে প্রায়ই দুটো স্যাম্পল রেটের মধ্যে অনুপাত একটি ভগ্নাংশ — উদাহরণস্বরূপ, দুটো স্ট্যান্ডার্ড অডিও রেট 44100 Hz আর 48000 Hz-এর মধ্যে অনুপাত 44100/48000 = 147/160 — কোনোটিই অন্যটির পূর্ণসংখ্যা গুণিতক নয়।

সমাধান হলো দুই ধাপ জোড়া লাগানো: প্রথমে L দিয়ে আপস্যাম্পল করে একটি মধ্যবর্তী উচ্চ রেট fs × L-এ পৌঁছানো, তারপর সেই মধ্যবর্তী সিগন্যালকে M দিয়ে ডাউনস্যাম্পল করে চূড়ান্ত রেটে নামানো:

$$fs_{\text{new}} = fs \times \frac{L}{M}$$

গুরুত্বপূর্ণ অপ্টিমাইজেশন: L41-এর ইন্টারপোলেশন ফিল্টার আর L40-এর অ্যান্টি-অ্যালিয়াস ফিল্টার — দুটো আলাদা ফিল্টার না চালিয়ে একটি মিলিত ফিল্টার ব্যবহার করা যায়, যার কাটঅফ দুটো প্রয়োজনের মধ্যে যেটি বেশি কড়াকড়ি (ছোট) সেটিতে বসানো হয়:

$$f_c = \min\left(\frac{fs}{2},\ \frac{fs_{\text{new}}}{2}\right)$$

L/M রেশিও
যেকোনো ভগ্নাংশ রেট-পরিবর্তন অর্জন করা যায় আপস্যাম্পল-তারপর-ডাউনস্যাম্পল জুড়ে দিয়ে।
একটি মিলিত ফিল্টার
দুই আলাদা ফিল্টারের বদলে একটি -- কাটঅফ দুটো প্রয়োজনের মধ্যে ছোটটিতে বসিয়ে।
বাস্তব প্রয়োগ
অডিও স্যাম্পল-রেট রূপান্তর, বিভিন্ন ডিভাইসের ক্লক রেটের মধ্যে সিগন্যাল মেলানো -- সবই এই একই ভিত্তির উপর নির্মিত।

২ · সত্যিকারের ডেমো — ৮ Hz থেকে ১২ Hz (৩/২ রেশিও)

একটি ছোট টেস্ট সিগন্যাল — fs = 8 Hz-এ স্যাম্পল করা একটি ১ Hz সাইন, ৮টি স্যাম্পল (এক পূর্ণ চক্র)। L=3, M=2 নিয়ে এটিকে fs_new = 8 × 3/2 = 12 Hz-এ রূপান্তর করা যাক।

Python
import math

def sinc_lowpass(numtaps, fc):
    """windowed-sinc লো-পাস ফিল্টার (M7/L31-এর পদ্ধতি)"""
    M = (numtaps - 1) / 2.0
    h = []
    for n in range(numtaps):
        d = n - M
        if abs(d) < 1e-9:
            hn = 2 * fc
        else:
            hn = math.sin(2 * math.pi * fc * d) / (math.pi * d)
        w = 0.54 - 0.46 * math.cos(2 * math.pi * n / (numtaps - 1))
        h.append(hn * w)
    return h

def fir_apply(x, h):
    """ডিসক্রিট কনভোলিউশন (M3/L10)"""
    nx, nh = len(x), len(h)
    y = [0.0] * (nx + nh - 1)
    for n in range(nx):
        for k in range(nh):
            y[n + k] += x[n] * h[k]
    return y

fs_orig = 8.0
f0 = 1.0
N = 8
x = [math.sin(2*math.pi*f0*n/fs_orig) for n in range(N)]
print("মূল সিগন্যাল (৮ Hz):", [round(v, 4) for v in x])

L, Mdec = 3, 2
fs_inter = fs_orig * L      # মধ্যবর্তী রেট: ২৪ Hz
fs_new = fs_orig * L / Mdec # চূড়ান্ত রেট: ১২ Hz
print(f"মধ্যবর্তী রেট: {fs_inter} Hz  |  চূড়ান্ত রেট: {fs_new} Hz  (রেশিও L/M = {L}/{Mdec})")

# ধাপ ১ -- L দিয়ে আপস্যাম্পল (L41-এর জিরো-ইনসার্শন)
x_zeros = []
for v in x:
    x_zeros.append(v)
    x_zeros.extend([0.0] * (L - 1))

# ধাপ ২ -- একটি মিলিত ফিল্টার (ইন্টারপোলেশন-ইমেজ + ডেসিমেশন-অ্যালিয়াস দুটোই ঠেকায়)
numtaps = 31
fc_hz = min(fs_orig / 2, fs_new / 2)     # যে সীমা বেশি কড়াকড়ি, সেটিই বেছে নেওয়া হয়
fc = fc_hz / fs_inter                    # fs_inter-এর ভগ্নাংশ হিসেবে কাটঅফ
h = sinc_lowpass(numtaps, fc)
h_scaled = [L * v for v in h]            # L41-এর মতো গেইন সংশোধন

y_full = fir_apply(x_zeros, h_scaled)
delay = (numtaps - 1) // 2
y_aligned = y_full[delay: delay + len(x_zeros)]

# ধাপ ৩ -- M দিয়ে ডেসিমেট (L40-এর সরল স্লাইসিং)
y_final = y_aligned[::Mdec]

print(f"কাটঅফ: {fc_hz} Hz (fs_inter={fs_inter} Hz-এর {fc:.4f} ভগ্নাংশ)")
print(f"জিরো-স্টাফড দৈর্ঘ্য: {len(x_zeros)}  |  চূড়ান্ত (ডেসিমেটেড) দৈর্ঘ্য: {len(y_final)}")

# তুলনার জন্য -- একই সাইন সরাসরি ১২ Hz-এ স্যাম্পল করা "প্রকৃত" মান
x_true = [math.sin(2*math.pi*f0*n/fs_new) for n in range(len(y_final))]

print(f"\n{'n':>3} | {'রূপান্তরিত (১২Hz)':>18} | {'প্রকৃত (১২Hz)':>14} | {'পার্থক্য':>10}")
max_diff = 0.0
for n in range(len(y_final)):
    diff = abs(y_final[n] - x_true[n])
    max_diff = max(max_diff, diff)
    print(f"{n:>3} | {y_final[n]:>18.4f} | {x_true[n]:>14.4f} | {diff:>10.2e}")

print(f"\nসর্বোচ্চ পার্থক্য: {max_diff:.4f}")

    
চূড়ান্ত রূপান্তরিত সিগন্যালে ১২টি স্যাম্পল আছে (মূল ৮টি স্যাম্পল থেকে 8×3/2=12 — ঠিক প্রত্যাশিত রেশিও অনুযায়ী), আর প্রতিটি মান সরাসরি ১২ Hz-এ স্যাম্পল করা প্রকৃত সাইন-ওয়েভের মানের সাথে খুব কাছাকাছি — সর্বোচ্চ পার্থক্য মাত্র ০.০৫৩ (প্রান্তে, যেখানে ফিল্টারের সীমিত "ইতিহাস" সবচেয়ে বেশি প্রভাব ফেলে, ঠিক L41-এ দেখা প্রান্ত-ট্রানজিয়েন্টের মতোই)। এটি প্রমাণ করে L/M রেট কনভার্সন পাইপলাইন সত্যিই কাজ করে — সিগন্যালের প্রকৃত ফ্রিকোয়েন্সি বিষয়বস্তু (এখানে ১ Hz) নতুন রেটে সঠিকভাবে সংরক্ষিত থাকে।

৩ · বাস্তব-জীবনের প্রেক্ষাপট — অডিও স্যাম্পল রেট রূপান্তর

এই কৌশলের সবচেয়ে পরিচিত বাস্তব প্রয়োগ অডিও প্রসেসিং-এ। বিভিন্ন অডিও স্ট্যান্ডার্ড ভিন্ন ভিন্ন স্যাম্পল রেট ব্যবহার করে (উদাহরণস্বরূপ, একটি সাধারণ মিউজিক ফাইল প্রায়ই 44100 Hz-এ থাকে, আবার কিছু ভিডিও বা ব্রডকাস্ট সিস্টেম 48000 Hz ব্যবহার করে) — দুটো ভিন্ন-রেট সিস্টেমের মধ্যে অডিও পাঠাতে হলে একটি রেট কনভার্সন প্রয়োজন হয়। উপরের ডেমোর মতোই একটি নীতিতে (আপস্যাম্পল, মিলিত ফিল্টার, ডাউনস্যাম্পল) এই রূপান্তর সম্পন্ন হয় — যদিও প্রকৃত অডিও রিস্যাম্পলার সাধারণত আরও দীর্ঘ, আরও সুচারুভাবে ডিজাইন করা ফিল্টার এবং L42-এর পলিফেজ কাঠামো ব্যবহার করে কার্যকারিতা বাড়ায়। এখানকার সংখ্যা (৮ Hz, ১২ Hz) সম্পূর্ণ ইলাস্ট্রেটিভ — কোনো নির্দিষ্ট প্রোডাক্ট বা লাইব্রেরির প্রকৃত ইমপ্লিমেন্টেশনের দাবি নয়, শুধু নীতিটি বোঝানোর জন্য।

মূল কথা · Key takeaway

L40-এর ডেসিমেশন আর L41-এর ইন্টারপোলেশন আলাদা আলাদাভাবে শুধু পূর্ণসংখ্যা রেট-পরিবর্তন করতে পারে — কিন্তু একসাথে জুড়ে দিলে (আপস্যাম্পল-তারপর-ডাউনস্যাম্পল, একটি মিলিত ফিল্টার সহ) যেকোনো ভগ্নাংশ L/M রেট কনভার্সন অর্জন করা যায়। এটাই এই মডিউলের (M9) মূল শিক্ষা — মাল্টিরেট প্রসেসিং মানে শুধু "রেট বদলানো" নয়, বরং প্রতিটি ধাপে সঠিক ফিল্টারিং দিয়ে নিশ্চিত করা যে সিগন্যালের প্রকৃত তথ্য (এর ফ্রিকোয়েন্সি বিষয়বস্তু) নতুন রেটেও অক্ষুণ্ণ থাকে। M10-এ আমরা এগিয়ে যাব স্পেকট্রাল এস্টিমেশন ও অ্যাডভান্সড টপিকে — একটি সিগন্যালের ফ্রিকোয়েন্সি বিষয়বস্তু কীভাবে আরও গভীরভাবে বিশ্লেষণ করা যায় তা নিয়ে।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ উপরের কোডে কাটঅফ ফ্রিকোয়েন্সি বেছে নেওয়া হয়েছিল min(fs_orig/2, fs_new/2) — এখানে fs_orig/2 = 4 Hz আর fs_new/2 = 6 Hz। কেন ছোটটি (৪ Hz) বেছে নেওয়া হলো, বড়টি (৬ Hz) নয়?

ফিল্টারকে দুটো কাজ একসাথে করতে হয় — L41-এর মতো ইন্টারপোলেশন-ইমেজ সরানো (যার জন্য কাটঅফ মূল সিগন্যালের নাইকুইস্ট সীমা fs_orig/2-তে বসাতে হয়) এবং L40-এর মতো ডেসিমেশন-অ্যালিয়াস ঠেকানো (যার জন্য কাটঅফ নতুন সিগন্যালের নাইকুইস্ট সীমা fs_new/2-তে বসাতে হয়)। এই দুই প্রয়োজনের মধ্যে যে কাটঅফ ছোট (এখানে ৪ Hz), সেটি বেছে নিলে দুটো শর্তই একসাথে পূরণ হয় — বড় কাটঅফ (৬ Hz) বেছে নিলে ইন্টারপোলেশন-ইমেজ পুরোপুরি সরানো যেত না।

প্র ০২ যদি L=2, M=3 নেওয়া হতো (রেশিও উল্টো — রেট কমানো), তাহলে ৮ Hz সিগন্যালটি কোন রেটে গিয়ে থামত?

fs_new = fs_orig × L/M = 8 × 2/3 ≈ 5.33 Hz। লক্ষ্য করুন কোডের একই পাইপলাইন (আপস্যাম্পল, মিলিত ফিল্টার, ডাউনস্যাম্পল) এখানেও প্রযোজ্য — শুধু L আর M-এর মান বদলে যায়, গঠন একই থাকে। এটাই দেখায় কেন এই দুই-ধাপের পদ্ধতি এত সাধারণ (general) — একই কোড যেকোনো L/M রেশিওর জন্য কাজ করে।

প্র ০৩ এই লেসনের ডেমোতে সরাসরি L41-এর মতো একটি ফিল্টার আর L40-এর মতো আরেকটি আলাদা ফিল্টার — মোট দুটো আলাদা কনভোলিউশন ব্যবহার না করে একটি মিলিত ফিল্টার ব্যবহার করার সুবিধা কী?

গণনাগত সাশ্রয় — L42-এ শেখা নীতির সাথেই সামঞ্জস্যপূর্ণ। দুটো আলাদা ফিল্টার (একটি ইন্টারপোলেশনের জন্য, একটি ডেসিমেশনের জন্য) ক্রমানুসারে চালালে দুইবার পূর্ণ কনভোলিউশনের খরচ লাগত, যদিও দুটো ফিল্টারের প্রায় একই কাজ (নির্দিষ্ট একটি ফ্রিকোয়েন্সির উপরে সবকিছু কাটা)। একটি মিলিত ফিল্টার ব্যবহার করে একটিমাত্র কনভোলিউশনেই দুটো শর্ত পূরণ করা যায় — বাস্তব রিস্যাম্পলার ইমপ্লিমেন্টেশনে এই ধরনের অপ্টিমাইজেশন গুরুত্বপূর্ণ (L42-এর পলিফেজ কাঠামোর সাথে মিলিয়ে আরও দক্ষ করা যায়)।

অনুশীলন

  1. চিন্তা করুন: উপরের কোডে L = 3, M = 2-কে L = 2, M = 1-এ বদলালে (fs_orig=8 Hz অপরিবর্তিত রেখে) নতুন fs_new কত হবে, আর এটি আসলে L41-এর কোন লেসনের সাথে সমতুল্য হয়ে যাবে?

    fs_new = 8 × 2/1 = 16 Hz। M=1 মানে কোনো ডাউনস্যাম্পলিং হচ্ছে না (প্রতি ১ম স্যাম্পলই রাখা হচ্ছে, অর্থাৎ সবগুলো) — তাই এটি আসলে বিশুদ্ধ L41-এর ইন্টারপোলেশনের সাথে সমতুল্য হয়ে যাবে, L=2 দিয়ে সরাসরি আপস্যাম্পলিং।

  2. পরীক্ষা করুন: কোডে L = 3, M = 2-কে L = 2, M = 1-এ পরিবর্তন করে Run চেপে দেখুন চূড়ান্ত আউটপুট দৈর্ঘ্য ও সর্বোচ্চ পার্থক্য কেমন আসে, এবং তা আপনার আগের হিসেবের সাথে মেলে কি না।

    রান করলে দেখা যাবে চূড়ান্ত দৈর্ঘ্য 8 × 2 = 16 স্যাম্পল (যা N × L / M = 8×2/1 থেকে প্রত্যাশিত), এবং কাটঅফ এবার শুধু fs_orig/2 = 4 Hz-এ বসবে (কারণ fs_new/2 = 8 Hz এখন বড়, তাই min() ছোটটিই বেছে নেবে) — অর্থাৎ ফলাফল খাঁটি L41-এর ইন্টারপোলেশন ফিল্টারের সাথে ধারণাগতভাবে অভিন্ন আচরণ দেখাবে।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

আগের পাঠ
পলিফেজ ফিল্টার ও এফিসিয়েন্ট মাল্টিরেট স্ট্রাকচার