Add fft_analysis/src/fft_analysis/core.py
This commit is contained in:
parent
9bf005109a
commit
0b0901ec04
1 changed files with 58 additions and 0 deletions
58
fft_analysis/src/fft_analysis/core.py
Normal file
58
fft_analysis/src/fft_analysis/core.py
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
"""Core module for performing FFT (Fast Fourier Transform) analysis on audio data.
|
||||
|
||||
This module provides numerical processing of time-domain PCM audio signals using FFT,
|
||||
returning their magnitude spectrum for further analysis and visualization.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
from scipy import fft as sp_fft
|
||||
from typing import Any
|
||||
|
||||
|
||||
class InvalidAudioDataError(ValueError):
|
||||
"""Raised when provided audio_data is invalid for FFT computation."""
|
||||
|
||||
|
||||
def perform_fft(audio_data: np.ndarray) -> np.ndarray:
|
||||
"""Compute the frequency magnitude spectrum of the given audio data.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
audio_data : numpy.ndarray
|
||||
1D array with PCM audio data (time-domain).
|
||||
|
||||
Returns
|
||||
-------
|
||||
numpy.ndarray
|
||||
Magnitude spectrum of the audio signal.
|
||||
|
||||
Raises
|
||||
------
|
||||
InvalidAudioDataError
|
||||
If the input array is invalid or empty.
|
||||
"""
|
||||
if not isinstance(audio_data, np.ndarray):
|
||||
raise InvalidAudioDataError("audio_data must be a numpy.ndarray")
|
||||
|
||||
if audio_data.ndim != 1:
|
||||
raise InvalidAudioDataError("audio_data must be a 1D array")
|
||||
|
||||
if audio_data.size == 0:
|
||||
raise InvalidAudioDataError("audio_data is empty")
|
||||
|
||||
# Normalize to range [-1, 1] to avoid scaling effects
|
||||
max_val = np.max(np.abs(audio_data))
|
||||
if max_val == 0:
|
||||
raise InvalidAudioDataError("audio_data contains only zeros")
|
||||
|
||||
normalized = audio_data / max_val
|
||||
|
||||
# Compute FFT using scipy for better numerical accuracy
|
||||
spectrum_complex = sp_fft.fft(normalized)
|
||||
magnitude = np.abs(spectrum_complex)
|
||||
|
||||
# Return only the positive frequency components (real signal symmetry)
|
||||
half_length = magnitude.shape[0] // 2
|
||||
return magnitude[:half_length]
|
||||
Loading…
Reference in a new issue