<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Prince Sanchela - Systems & Code]]></title><description><![CDATA[Engineering notes, debugging logs, and architecture deep dives by Prince Sanchela on bare-metal C, embedded robotics, real-time DSP/AI, and web systems.]]></description><link>https://princesanchela.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Fri, 13 Nov 2026 19:48:13 GMT</lastBuildDate><atom:link href="https://princesanchela.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Building a Real-Time 64-Band Audio DSP Lab and Offline Whisper Engine on CPU]]></title><description><![CDATA[How I decoupled live microphone capture, 64-band logarithmic FFT analysis, F0 pitch biometrics, and offline Whisper inference in Python on CPU.]]></description><link>https://princesanchela.hashnode.dev/real-time-audio-dsp-offline-whisper-cpu</link><guid isPermaLink="true">https://princesanchela.hashnode.dev/real-time-audio-dsp-offline-whisper-cpu</guid><category><![CDATA[Python]]></category><category><![CDATA[Signal Processing]]></category><category><![CDATA[Machine Learning]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[software development]]></category><dc:creator><![CDATA[Prince Tech]]></dc:creator><pubDate>Thu, 12 Nov 2026 12:07:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6af5a34dea944eabc0680c07/9bea44d3-d749-4d5e-8ad2-9f1c3257fb4c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>Real-time audio workstations fail when heavy neural network inference runs inside the audio callback loop. When a speech transformer model takes 200 milliseconds to process a chunk on a 20-millisecond hardware audio clock, the result is immediate buffer underruns, audible clicks, and a frozen user interface.</h2>
<p>While building <a href="https://github.com/PrinceSanchela/LangVoi"><strong>LangVoi Voice Studio Pro</strong></a>—an offline desktop workstation combining a 64-band real-time Fast Fourier Transform (FFT) spectral lab, acoustic vocal biometrics, and neural Whisper spoken-language identification across 99+ languages—I designed the entire pipeline to execute deterministically on standard CPUs with zero cloud dependencies.</p>
<p>Here is a complete breakdown of the concurrency model, DSP math, and zero-disk streaming architecture that keeps the workstation responsive under load.</p>
<hr />
<h2>1. System Architecture: Decoupling Deterministic I/O from Burst Inference</h2>
<p>Audio capture hardware operates on a strict periodic deadline. Sampling at <strong>fₛ = 16 kHz</strong> with a frame size of <strong>512 samples</strong> gives the audio callback a hard deadline every <strong>32 ms</strong> (<code>512 / 16000 = 0.032 s</code>).</p>
<p>Meanwhile, the UI requires smooth ~60 FPS updates for decibel VU meters and frequency bands, whereas neural Whisper inference requires multi-second speech context windows and exhibits variable execution latency.</p>
<p>To prevent the neural engine from starving the audio or rendering threads, I partitioned the workstation into <strong>three decoupled stages</strong> connected by thread-safe ring buffers:</p>
<ul>
<li><strong>1. Audio I/O Producer (Hard Periodic — ~32 ms):</strong> Streams raw float32 PCM frames from hardware into a preallocated ring buffer with zero heap allocations.</li>
<li><strong>2. DSP &amp; UI Renderer (~60 FPS — ~16.6 ms):</strong> Computes Dual-Channel Master VU ballistics, 64-band FFT magnitudes, and fundamental pitch (<strong>F₀</strong>) within a sub-millisecond CPU budget.</li>
<li><strong>3. Neural LID Worker (Event-Driven — VAD Gated):</strong> Runs quantized Whisper inference in memory on voiced speech windows asynchronously without blocking the UI.</li>
</ul>
<hr />
<h2>2. Logarithmic 64-Band Spectral Analysis &amp; Hann Windowing</h2>
<p>Human hearing perceives pitch logarithmically rather than linearly—the perceptual distance between <strong>100 Hz and 200 Hz</strong> (one octave) matches the distance between <strong>4000 Hz and 8000 Hz</strong>. Plotting raw linear FFT bins wastes visual resolution on high frequencies while cramping bass and vocal formants.</p>
<p>To match human frequency perception from <strong>20 Hz to 20,000 Hz</strong> while suppressing spectral leakage at frame boundaries, the DSP engine applies a precomputed <strong>Hann window</strong> (<code>w[n] = 0.5 × (1 − cos(2πn / (N − 1)))</code>) before mapping linear FFT bins onto 64 logarithmically spaced bands:</p>
<pre><code class="language-python">import numpy as np

class LogarithmicSpectralLab:
    def __init__(self, sample_rate: int = 44100, fft_size: int = 2048, num_bands: int = 64):
        self.sample_rate = sample_rate
        self.fft_size = fft_size
        self.num_bands = num_bands

        # Precompute window and frequency axis once at startup to avoid per-frame allocations
        self.window = np.hanning(fft_size).astype(np.float32)
        self.freqs = np.fft.rfftfreq(fft_size, d=1.0 / sample_rate)

        # Logarithmic bin edges spanning 20 Hz to 20 kHz
        self.band_edges = np.geomspace(20.0, min(20000.0, sample_rate / 2.0), num_bands + 1)

    def compute_bands_dbfs(self, frame: np.ndarray) -&gt; np.ndarray:
        if len(frame) != self.fft_size:
            raise ValueError(f"Expected {self.fft_size} samples, got {len(frame)}")

        # 1. Apply Hann window to minimize side-lobe spectral leakage
        windowed = frame * self.window

        # 2. Compute one-sided real FFT and normalize magnitude
        spectrum = np.abs(np.fft.rfft(windowed)) / (self.fft_size / 2.0)

        # 3. Map linear bins to 64 logarithmic bands in decibels full scale (dBFS)
        bands = np.zeros(self.num_bands, dtype=np.float32)
        for i in range(self.num_bands):
            mask = (self.freqs &gt;= self.band_edges[i]) &amp; (self.freqs &lt; self.band_edges[i + 1])
            band_energy = np.mean(spectrum[mask]) if np.any(mask) else 1e-7
            bands[i] = np.clip(20.0 * np.log10(max(band_energy, 1e-7)), -60.0, 6.0)

        return bands
</code></pre>
<p>Precomputing <code>self.window</code>, <code>self.freqs</code>, and <code>self.band_edges</code> during initialization eliminates heap allocations inside the real-time loop, keeping spectral analysis under <strong>0.5 ms per frame</strong> on a standard laptop CPU.</p>
<hr />
<h2>3. Acoustic Telemetry: F₀ Pitch Tracking &amp; VAD Gating</h2>
<p>Running Whisper continuously on room silence wastes CPU cycles and triggers thermal throttling. Before waking up the neural worker thread, every incoming chunk passes through a lightweight time-domain telemetry stage:</p>
<ul>
<li><strong>Voice Activity Detection (VAD) &amp; Zero-Crossing Rate (ZCR):</strong> Filters out background noise and unvoiced transients so the neural worker stays idle during silence.</li>
<li><strong>Autocorrelation Fundamental Frequency (F₀) Tracking:</strong> Estimates vocal pitch and pitch stability (<strong>ΔF₀</strong>), classifying vocal registers (<em>Bass, Baritone, Tenor, Alto, Soprano</em>) alongside Signal-to-Noise Ratio (SNR) and dynamic range.</li>
</ul>
<pre><code class="language-python">def compute_signal_telemetry(frame: np.ndarray, rms_gate: float = 0.015) -&gt; dict:
    rms = float(np.sqrt(np.mean(np.square(frame))))
    dbfs = 20.0 * np.log10(max(rms, 1e-7))
    zcr = float(np.mean(np.abs(np.diff(np.signbit(frame)))))
    is_voiced = (rms &gt; rms_gate) and (zcr &lt; 0.35)

    return {
        "rms": round(rms, 5),
        "dbfs": round(dbfs, 2),
        "zcr": round(zcr, 4),
        "is_voiced": is_voiced,
    }
</code></pre>
<hr />
<h2>4. Key Engineering Takeaways</h2>
<blockquote>
<p><strong>Core Rule for Real-Time Systems:</strong> Never mix deterministic millisecond deadlines with best-effort compute workloads on the same thread.</p>
</blockquote>
<ol>
<li><strong>Zero-Disk In-Memory Streaming:</strong> Passing float32 NumPy buffers directly to the inference engine eliminates temporary <code>.wav</code> file I/O latency and keeps audio processing 100% private on the local machine.</li>
<li><strong>Automated</strong> <code>unittest</code> <strong>Coverage for DSP Edge Cases:</strong> Writing automated unit tests for pure silence, clipped sine waves, stereo-to-mono downmixing, and language database resolution caught subtle off-by-one scaling bugs before packaging the standalone executable.</li>
<li><strong>Profile Before Optimizing:</strong> Benchmarking revealed that UI canvas redraws and Python object allocations inside tight loops cost more CPU time than the FFT math itself.</li>
</ol>
<hr />
<h2>Source Code &amp; Next Steps</h2>
<p>The complete codebase, automated <code>unittest</code> suite, and standalone release builds are available on GitHub:</p>
<ul>
<li><strong>Project Repository:</strong> <a href="https://github.com/PrinceSanchela/LangVoi">github.com/PrinceSanchela/LangVoi</a></li>
<li><strong>GitHub Profile:</strong> <a href="https://github.com/PrinceSanchela">github.com/PrinceSanchela</a></li>
</ul>
<p>I am actively contributing to open-source projects across <strong>systems programming, audio/DSP, computer vision, and developer tooling</strong>. Feel free to inspect the repo, open an issue, or share feedback in the comments below!</p>
]]></content:encoded></item></channel></rss>