Mel Spectrogram#
- MelExtractor trt_edgellm::rt::audio::makeExtractorByName(
- std::string const &feType,
Floor strategy used right before the log.
HF FE call sites:
Whisper:
np.maximum(mel_floor=1e-10, mel)-> kMax ! - Parakeet:torch.log(mel + 2**-24)-> kAdd enum class LogFloorMode { kMax, //!<log(max(mel, logFloor))kAdd, //!<log(mel + logFloor)};
! Mel scale convention. HF FE picks one per model. enum class MelScale { kHtk, //!<
2595 log10(1 + f/700)— librosa(htk=True). kSlaney, //!< Auditory toolbox / librosa default (htk=False). Used by HF !< WhisperFeatureExtractor and ParakeetFeatureExtractor. kKaldi, //!<1127 ln(1 + f/700)— Kaldi-style; pair with !<triangulariseInMelSpace=true. };! Mel filter normalization. Slaney area-norm matches HF defaults for ! Whisper / Parakeet. enum class MelNorm { kNone, kSlaney, };
! Window function applied to each frame. enum class WindowType { kHannPeriodic, //!<
torch.hann_window(N, periodic=True); HF Whisper default. kHannSymmetric, //!<torch.hann_window(N, periodic=False); HF Parakeet. kHammingPeriodic, //!<torch.hamming_window(N, periodic=True). kHammingSymmetric, //!<torch.hamming_window(N, periodic=False). };! Output layout for the mel tensor. enum class MelLayout { kMelTime, //!<
[n_mel, T]— Whisper convention. kTimeMel, //!<[T, n_mel]— Parakeet convention. };! Optional post-processing applied after log. ! ! Whisper: !
log_spec = max(log_spec, log_spec.max() - 8.0)!log_spec = (log_spec + 4.0) / 4.0! ! Parakeet: subtract per-mel mean, divide by per-mel std (over valid T). enum class PostNormalize { kNone, kWhisperClamp, kPerFeatureMeanStd, };! How successive frames slice the waveform. ! ! HF transformers’
audio_utils.spectrogram(center=True, pad_mode="reflect")! reflectsn_fft / 2samples on each side before framing, which centres ! frametat samplet * hop. Our left-aligned legacy frames frame !tat samples[t*hop, t*hop + n_fft). enum class FramePadding { kLeftAlignedZero, //!< Legacy: frame[t] = samples[t*hop .. t*hop+nFFT). Zero-pad tail. kCenterReflect, //!< HF Whisper: reflect-pad nFFT/2 each side, then frame !< (audio_utils.spectrogram(center=True, pad_mode=”reflect”)). kCenterZero, //!< HF Parakeet: zero-pad nFFT/2 each side, then frame !< (torch.stft(pad_mode=”constant”), default for HF Parakeet). kSemicausalZero, //!< HF Gemma4 (sl.STFT time_padding=’semicausal’): zero-pad !< winLength/2 on the left only; frames unfold with size !< winLength+1 (the trailing sample is dropped before the !< window when preemphasis is off). };! Time-axis padding policy. ! ! Whisper extracts mel over the full audio without padding to a fixed T ! (the model handles up to
chunk_size=30s -> T=3000). Engines that ! bake static seq constants into the ONNX trace can usekStaticPad! to pad mel tostaticTimeLengthwith zero rows at the tail. enum class TimePadding { kNone, kStaticPad, //!< Pad / truncate tostaticTimeLengthrows. };! Configuration for one mel-spectrogram extractor instance. struct MelExtractorConfig { ! Display name used in log messages. std::string name;
int32_t sampleRate{16000}; int32_t nFFT{400}; int32_t hopLength{160}; int32_t winLength{400}; //!< Window length (typically == nFFT for Whisper, 400 in a 512 FFT for Parakeet). int32_t nMel{128};
! Min/max frequency for the mel filter bank. Default 0..sr/2 matches HF ! Whisper. float minFrequencyHz{0.0f}; float maxFrequencyHz{-1.0f}; //!< Negative -> sample_rate / 2.
! Pre-emphasis filter
y[t] = x[t] - coeff * x[t-1]. Disabled when !preemphCoeff == 0. WhenpreemphPostScale != 0the filtered ! frame is also multiplied by it. float preemphCoeff{0.0f}; float preemphPostScale{0.0f};WindowType windowType{WindowType::kHannPeriodic}; ! Where the window sits inside the nFFT-sized FFT input buffer when !
winLength < nFFT. HF Whisper / Parakeet (torch.stft-style) centre ! the window: source[start+pad, start+pad+winLen)-> buffer ![pad, pad+winLen),pad = (nFFT-winLen)/2. Left-aligned mode ! (unfold + rfft(n=nFFT)) maps source[start, start+winLen)-> ! buffer[0, winLen)with trailing zeros. Ignored when winLen == nFFT. bool windowCentredInFft{true}; ! When true, feed the mel filter bank the magnitude spectrum|stft|! (HF Gemma4) instead of the power spectrum|stft|^2. bool magnitudeSpectrum{false};MelScale melScale{MelScale::kHtk}; MelNorm melNorm{MelNorm::kSlaney}; ! When true, build triangle filters with their slopes linear in mel ! space rather than Hz. Used together with
MelScale::kKaldi. bool triangulariseInMelSpace{false};LogType logType{LogType::kLog10}; LogFloorMode logFloorMode{LogFloorMode::kMax}; float logFloor{1e-10f}; //!< Per-FE: Whisper 1e-10, Parakeet 2^-24.
MelLayout layout{MelLayout::kMelTime}; PostNormalize postNormalize{PostNormalize::kWhisperClamp}; TimePadding timePadding{TimePadding::kNone}; int32_t staticTimeLength{0};
FramePadding framePadding{FramePadding::kLeftAlignedZero};
! When true, drop the last STFT frame before mel filter / log / post-norm, ! matching HF Whisper / Parakeet’s
stft[..., :-1]and the original ! Whisper reference. Without this the post-normalize statistics ! (whisper max-clamp, parakeet per-feature mean/std) drift from HF by ! O(1e-1) at frame boundaries even though the underlying mel-power ! values are byte-identical. bool dropLastStftFrame{false};! Pointer to a precomputed mel filter bank of shape
[nMel × (nFFT/2 + 1)]! in row-major order. Generated offline by !scripts/gen_mel_filter_bank.pyand embedded as a static array. ! Lifetime must outlive the extractor (typically pointer to a !static constexprtable). float const* melFilter{nullptr}; };! CPU mel-spectrogram extractor. ! ! Takes mono float32 PCM and produces a host-resident
Tensorof ! shape determined by the config’s layout + padding. Pipeline mirrors ! HF feature extractors numerically (windowing, hand-rolled radix-2 ! FFT with direct-DFT fallback for odd sizes, power spectrum, mel filter ! mat-mul, log, optional post-normalize). class MelExtractor { public: ! Default-constructed extractor is empty; runners move-assign one from !makeWhisperExtractor/makeParakeetExtractorin !validateAndFillConfigbefore anyextractcall. MelExtractor() noexcept; explicit MelExtractor(MelExtractorConfig cfg); ~MelExtractor();MelExtractor(MelExtractor const&) = delete; MelExtractor& operator=(MelExtractor const&) = delete; MelExtractor(MelExtractor&&) noexcept; MelExtractor& operator=(MelExtractor&&) noexcept;
! Extract mel-spectrogram from
pcmintoout. ! !pcm.sampleRatemust equalconfig.sampleRate(caller ! responsibility — pass the righttargetSampleRateto !loadAudioBytes). ! !
MelExtractorConfig const& config() const noexcept { return mConfig; }
! Internally built mel filter bank: row-major
[nMel x (nFFT/2 + 1)]! weights, valid for the extractor’s lifetime. Empty for a ! default-constructed extractor or when a precomputedmelFilterwas ! supplied in the config. std::vector<float> const& melFilterBank() const noexcept;! Analysis window used by
extract():winLengthfloat taps, valid ! for the extractor’s lifetime. Empty for a default-constructed (empty) ! extractor. std::vector<float> const& window() const noexcept;private: struct Impl; std::unique_ptr<Impl> mImpl; MelExtractorConfig mConfig; };
! Build a Whisper-compatible extractor (16 kHz, n_fft=400, hop=160, ! n_mel=128, slaney mel scale + slaney norm, log10, Whisper-clamp ! normalize,
[n_mel, T]layout, HF centre-reflect framing). Used by ! Qwen3-Omni and Qwen3-ASR. MelExtractor makeWhisperExtractor();! Gemma4 / USM-style extractor matching HF Gemma4FeatureExtractor defaults. MelExtractor makeGemma4AudioExtractor();
! Build a Parakeet-compatible extractor (16 kHz, n_fft=512, hop=160, ! n_mel=128, slaney scale + slaney norm, Hann non-periodic window, ! pre-emphasis 0.97,
log(x + 2^-24), per-feature mean/std normalize, ![T, n_mel]layout, HF centre-reflect framing). Used by ! Nemotron-Omni. MelExtractor makeParakeetExtractor();! Dispatch helper: build extractor from a string tag. ! ! Accepts
"whisper","parakeet". Throws !std::invalid_argumentfor unknown tags.- Parameters:
pcm – Mono float32 PCM. !
out – Populated with mel data on host memory (caller may ! copy to device). !
- Returns:
true on success, false on bad input / config mismatch. bool extract(AudioPCM const& pcm, Tensor& out);