Repository: TheAlgorithms/Python
Pull request: Add MFCC Feature Extraction Algorithm
### Describe your change:

Added MFCC Audio Signal Feature Extraction Algorithm.

Fixes https://github.com/TheAlgorithms/Python/issues/9026

* [x] Add an algorithm?
* [ ] Fix a bug or typo in an existing algorithm?
* [ ] Documentation change?

### Checklist:
* [x] I have read [CONTRIBUTING.md](https://github.com/TheAlgorithms/Python/blob/master/CONTRIBUTING.md).
* [x] This pull request is all my own work -- I have not plagiarized.
* [x] I know that pull requests will not be merged if they fail the automated tests.
* [x] This PR only changes one algorithm file.  To ease review, please open separate PRs for separate algorithms.
* [x] All new Python files are placed inside an existing directory.
* [x] All filenames are in all lowercase characters with no spaces or dashes.
* [x] All functions and variable names follow Python naming conventions.
* [x] All function parameters and return values are annotated with Python [type hints](https://docs.python.org/3/library/typing.html).
* [x] All functions have [doctests](https://docs.python.org/3/library/doctest.html) that pass the automated testing.
* [x] All new algorithms include at least one URL that points to Wikipedia or another similar explanation.
* [x] If this pull request resolves one or more open issues then the description above includes the issue number(s) with a [closing keyword](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue): "Fixes #ISSUE-NUMBER".


Problem statement:
Adding MFCC Audio Signal Feature Extraction Algorithm
### Feature description

**Issue Title:** 
Adding MFCC Audio Signal Feature Extraction Algorithm

**Issue Description:**
I am planning to contribute to this repository by implementing the MFCC (Mel Frequency Cepstral Coefficients) audio signal feature extraction algorithm. MFCC is widely used in various applications, including speech recognition and audio analysis.

**Proposed Location:**
I would like to propose adding a new category or subcategory for signal processing or feature extraction. Specifically, I suggest:
- Creating a "Signal Processing" category if one does not exist, and placing the MFCC algorithm under it.
OR
- Creating a "Feature Extraction" subcategory under the "Machine Learning" section and placing MFCC under it.

**Contributor Request:**
I am opening this issue to let the community know about my intention to work on this algorithm. I also welcome suggestions and feedback from other contributors regarding the best location for this feature extraction algorithm and any considerations related to its implementation.

Looking forward to collaborating with the community to enhance this repository with the addition of the MFCC feature extraction algorithm.

Hints from issue discussion:
check [audio_filters](https://github.com/TheAlgorithms/Python/tree/master/audio_filters) folder of this repo.
Thanks for your response. @rohan472000  

I also checked the audio filter folder, but I didn't find an MFCC implementation there, so I will open a PR to add the MFCC Algorithm. MFCC is commonly categorized as a feature extraction algorithm in machine learning, rather than an audio filter. I'm open to community feedback, and if most agree, I'll add it to the audio filter category.
Yep.. it would be better to put this into machine_learning folder.
The automatic build keeps failing for other algorithm tests. Hey Rohan, What do you suggest I do? @rohan472000 

Here is the error message:

=========================== short test summary info ============================
FAILED machine_learning/xgboost_regressor.py::machine_learning.xgboost_regressor.main
FAILED machine_learning/xgboost_regressor.py::machine_learning.xgboost_regressor.xgboost
============ 2 failed, 1473 passed, 16 warnings in 66.14s (0:01:06) ============
Error: Process completed with exit code 1.
I'm working to fix that issue...will raise PR for it...after getting merged your PR's build will be successful.

Commit message: Update review issues
* Remove types from docstring
* Rename dct
* Add mfcc docstring
* Add typing to several functions

Patch to review:
diff --git a/machine_learning/mfcc.py b/machine_learning/mfcc.py
new file mode 100644
index 000000000000..c3d85b526112
--- /dev/null
+++ b/machine_learning/mfcc.py
@@ -0,0 +1,484 @@
+"""
+Mel Frequency Cepstral Coefficients (MFCC) Calculation
+
+MFCC is a feature widely used in audio and speech processing to represent the
+short-term power spectrum of a sound signal in a more compact and
+discriminative way. It is particularly popular in speech and audio processing
+tasks such as speech recognition and speaker identification.
+
+How MFCC is Calculated:
+1. Preprocessing:
+   - Load an audio signal and normalize it to ensure that the values fall
+     within a specific range (e.g., between -1 and 1).
+   - Frame the audio signal into overlapping, fixed-length segments, typically
+     using a technique like windowing to reduce spectral leakage.
+
+2. Fourier Transform:
+   - Apply a Fast Fourier Transform (FFT) to each audio frame to convert it
+     from the time domain to the frequency domain. This results in a
+     representation of the audio frame as a sequence of frequency components.
+
+3. Power Spectrum:
+   - Calculate the power spectrum by taking the squared magnitude of each
+     frequency component obtained from the FFT. This step measures the energy
+     distribution across different frequency bands.
+
+4. Mel Filterbank:
+   - Apply a set of triangular filterbanks spaced in the Mel frequency scale
+     to the power spectrum. These filters mimic the human auditory system's
+     frequency response. Each filterbank sums the power spectrum values within
+     its band.
+
+5. Logarithmic Compression:
+   - Take the logarithm (typically base 10) of the filterbank values to
+     compress the dynamic range. This step mimics the logarithmic response of
+     the human ear to sound intensity.
+
+6. Discrete Cosine Transform (DCT):
+   - Apply the Discrete Cosine Transform to the log filterbank energies to
+     obtain the MFCC coefficients. This transformation helps decorrelate the
+     filterbank energies and captures the most important features of the audio
+     signal.
+
+7. Feature Extraction:
+   - Select a subset of the DCT coefficients to form the feature vector.
+     Often, the first few coefficients (e.g., 12-13) are used for most
+     applications.
+
+References:
+- Mel-Frequency Cepstral Coefficients (MFCCs):
+  https://en.wikipedia.org/wiki/Mel-frequency_cepstrum
+- Speech and Language Processing by Daniel Jurafsky & James H. Martin:
+  https://web.stanford.edu/~jurafsky/slp3/
+- Mel Frequency Cepstral Coefficient (MFCC) tutorial
+  http://practicalcryptography.com/miscellaneous/machine-learning
+  /guide-mel-frequency-cepstral-coefficients-mfccs/
+
+Author: Amir Lavasani
+"""
+
+
+import logging
+
+import numpy as np
+import scipy.fftpack as fft
+from scipy.signal import get_window
+
+logging.basicConfig(level=logging.WARNING)
+
+
+def mfcc(
+    audio: np.ndarray,
+    sample_rate: int,
+    ftt_size: int = 1024,
+    hop_length: int = 20,
+    mel_filter_num: int = 10,
+    dct_filter_num: int = 40,
+) -> np.ndarray:
+    """
+    Calculate Mel Frequency Cepstral Coefficients (MFCCs) from an audio signal.
+
+    Args:
+        audio: The input audio signal.
+        sample_rate: The sample rate of the audio signal (in Hz).
+        ftt_size: The size of the FFT window (default is 1024).
+        hop_length: The hop length for frame creation (default is 20ms).
+        mel_filter_num: The number of Mel filters (default is 10).
+        dct_filter_num: The number of DCT filters (default is 40).
+
+    Returns:
+        A matrix of MFCCs for the input audio.
+
+    Raises:
+        ValueError: If the input audio is empty.
+
+    Example:
+    >>> import numpy as np
+    >>> sample_rate = 44100  # Sample rate of 44.1 kHz
+    >>> duration = 2.0  # Duration of 1 second
+    >>> t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)
+    >>> audio = 0.5 * np.sin(2 * np.pi * 440.0 * t)  # Generate a 440 Hz sine wave
+    >>> mfccs = mfcc(audio, sample_rate)
+    >>> mfccs.shape
+    (40, 101)
+    """
+    logging.info(f"Sample rate: {sample_rate}Hz")
+    logging.info(f"Audio duration: {len(audio) / sample_rate}s")
+    logging.info(f"Audio min: {np.min(audio)}")
+    logging.info(f"Audio max: {np.max(audio)}")
+
+    # normalize audio
+    audio_normalized = normalize(audio)
+
+    logging.info(f"Normalized audio min: {np.min(audio_normalized)}")
+    logging.info(f"Normalized audio max: {np.max(audio_normalized)}")
+
+    # frame audio into
+    audio_framed = audio_frames(
+        audio_normalized, sample_rate, ftt_size=ftt_size, hop_length=hop_length
+    )
+
+    logging.info(f"Framed audio shape: {audio_framed.shape}")
+    logging.info(f"First frame: {audio_framed[0]}")
+
+    # convert to frequency domain
+    # For simplicity we will choose the Hanning window.
+    window = get_window("hann", ftt_size, fftbins=True)
+    audio_windowed = audio_framed * window
+
+    logging.info(f"Windowed audio shape: {audio_windowed.shape}")
+    logging.info(f"First frame: {audio_windowed[0]}")
+
+    audio_fft = calculate_fft(audio_windowed, ftt_size)
+    logging.info(f"fft audio shape: {audio_fft.shape}")
+    logging.info(f"First frame: {audio_fft[0]}")
+
+    audio_power = calculate_signal_power(audio_fft)
+    logging.info(f"power audio shape: {audio_power.shape}")
+    logging.info(f"First frame: {audio_power[0]}")
+
+    filters = mel_spaced_filterbank(sample_rate, mel_filter_num, ftt_size)
+    logging.info(f"filters shape: {filters.shape}")
+
+    audio_filtered = np.dot(filters, np.transpose(audio_power))
+    audio_log = 10.0 * np.log10(audio_filtered)
+    logging.info(f"audio_log shape: {audio_log.shape}")
+
+    dct_filters = discrete_cosine_transform(dct_filter_num, mel_filter_num)
+    cepstral_coefficents = np.dot(dct_filters, audio_log)
+
+    logging.info(f"cepstral_coefficents shape: {cepstral_coefficents.shape}")
+    return cepstral_coefficents
+
+
+def normalize(audio: np.ndarray) -> np.ndarray:
+    """
+    Normalize an audio signal by scaling it to have values between -1 and 1.
+
+    Args:
+        audio: The input audio signal.
+
+    Returns:
+        The normalized audio signal.
+
+    Examples:
+    >>> audio = np.array([1, 2, 3, 4, 5])
+    >>> normalized_audio = normalize(audio)
+    >>> np.max(normalized_audio)
+    1.0
+    >>> np.min(normalized_audio)
+    0.2
+    """
+    # Find the maximum absolute value in the audio signal
+    max_abs_value = np.max(np.abs(audio))
+
+    # Divide the entire audio signal by the maximum absolute value
+    return audio / max_abs_value
+
+
+def audio_frames(
+    audio: np.ndarray,
+    sample_rate: int,
+    hop_length: int = 20,
+    ftt_size: int = 1024,
+) -> np.ndarray:
+    """
+    Split an audio signal into overlapping frames.
+
+    Args:
+        audio: The input audio signal.
+        sample_rate: The sample rate of the audio signal.
+        hop_length: The length of the hopping (default is 20ms).
+        ftt_size: The size of the FFT window (default is 1024).
+
+    Returns:
+        An array of overlapping frames.
+
+    Examples:
+    >>> import numpy as np
+    >>> audio = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]*1000)
+    >>> sample_rate = 8000
+    >>> frames = audio_frames(audio, sample_rate, hop_length=10, ftt_size=512)
+    >>> frames.shape
+    (126, 512)
+    """
+
+    hop_size = np.round(sample_rate * hop_length / 1000).astype(int)
+
+    # Pad the audio signal to handle edge cases
+    audio = np.pad(audio, int(ftt_size / 2), mode="reflect")
+
+    # Calculate the number of frames
+    frame_count = int((len(audio) - ftt_size) / hop_size) + 1
+
+    # Initialize an array to store the frames
+    frames = np.zeros((frame_count, ftt_size))
+
+    # Split the audio signal into frames
+    for n in range(frame_count):
+        frames[n] = audio[n * hop_size : n * hop_size + ftt_size]
+
+    return frames
+
+
+def calculate_fft(audio_windowed: np.ndarray, ftt_size: int = 1024) -> np.ndarray:
+    """
+    Calculate the Fast Fourier Transform (FFT) of windowed audio data.
+
+    Args:
+        audio_windowed: The windowed audio signal.
+        ftt_size: The size of the FFT (default is 1024).
+
+    Returns:
+        The FFT of the audio data.
+
+    Examples:
+    >>> import numpy as np
+    >>> audio_windowed = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
+    >>> audio_fft = calculate_fft(audio_windowed, ftt_size=4)
+    >>> np.allclose(audio_fft[0], np.array([6.0+0.j, -1.5+0.8660254j, -1.5-0.8660254j]))
+    True
+    """
+    # Transpose the audio data to have time in rows and channels in columns
+    audio_transposed = np.transpose(audio_windowed)
+
+    # Initialize an array to store the FFT results
+    audio_fft = np.empty(
+        (int(1 + ftt_size // 2), audio_transposed.shape[1]),
+        dtype=np.complex64,
+        order="F",
+    )
+
+    # Compute FFT for each channel
+    for n in range(audio_fft.shape[1]):
+        audio_fft[:, n] = fft.fft(audio_transposed[:, n], axis=0)[: audio_fft.shape[0]]
+
+    # Transpose the FFT results back to the original shape
+    return np.transpose(audio_fft)
+
+
+def calculate_signal_power(audio_fft: np.ndarray) -> np.ndarray:
+    """
+    Calculate the power of the audio signal from its FFT.
+
+    Args:
+        audio_fft: The FFT of the audio signal.
+
+    Returns:
+        The power of the audio signal.
+
+    Examples:
+    >>> import numpy as np
+    >>> audio_fft = np.array([1+2j, 2+3j, 3+4j, 4+5j])
+    >>> power = calculate_signal_power(audio_fft)
+    >>> np.allclose(power, np.array([5, 13, 25, 41]))
+    True
+    """
+    # Calculate the power by squaring the absolute values of the FFT coefficients
+    audio_power = np.square(np.abs(audio_fft))
+
+    return audio_power
+
+
+def freq_to_mel(freq: float) -> float:
+    """
+    Convert a frequency in Hertz to the mel scale.
+
+    Args:
+        freq: The frequency in Hertz.
+
+    Returns:
+        The frequency in mel scale.
+
+    Examples:
+    >>> round(freq_to_mel(1000), 2)
+    999.99
+    """
+    # Use the formula to convert frequency to the mel scale
+    return 2595.0 * np.log10(1.0 + freq / 700.0)
+
+
+def mel_to_freq(mels: float) -> float:
+    """
+    Convert a frequency in the mel scale to Hertz.
+
+    Args:
+        mels: The frequency in mel scale.
+
+    Returns:
+        The frequency in Hertz.
+
+    Examples:
+    >>> round(mel_to_freq(999.99), 2)
+    1000.01
+    """
+    # Use the formula to convert mel scale to frequency
+    return 700.0 * (10.0 ** (mels / 2595.0) - 1.0)
+
+
+def mel_spaced_filterbank(
+    sample_rate: int, mel_filter_num: int = 10, ftt_size: int = 1024
+) -> np.ndarray:
+    """
+    Create a Mel-spaced filter bank for audio processing.
+
+    Args:
+        sample_rate: The sample rate of the audio.
+        mel_filter_num: The number of mel filters (default is 10).
+        ftt_size: The size of the FFT (default is 1024).
+
+    Returns:
+        Mel-spaced filter bank.
+
+    Examples:
+    >>> round(mel_spaced_filterbank(8000, 10, 1024)[0][1], 10)
+    0.0004603981
+    """
+    freq_min = 0
+    freq_high = sample_rate // 2
+
+    logging.info(f"Minimum frequency: {freq_min}")
+    logging.info(f"Maximum frequency: {freq_high}")
+
+    # Calculate filter points and mel frequencies
+    filter_points, mel_freqs = get_filter_points(
+        sample_rate,
+        freq_min,
+        freq_high,
+        mel_filter_num,
+        ftt_size,
+    )
+
+    filters = get_filters(filter_points, ftt_size)
+
+    # normalize filters
+    # taken from the librosa library
+    enorm = 2.0 / (mel_freqs[2 : mel_filter_num + 2] - mel_freqs[:mel_filter_num])
+    return filters * enorm[:, np.newaxis]
+
+
+def get_filters(filter_points: np.ndarray, ftt_size: int) -> np.ndarray:
+    """
+    Generate filters for audio processing.
+
+    Args:
+        filter_points: A list of filter points.
+        ftt_size: The size of the FFT.
+
+    Returns:
+        A matrix of filters.
+
+    Examples:
+    >>> get_filters(np.array([0, 20, 51, 95, 161, 256], dtype=int), 512).shape
+    (4, 257)
+    """
+    num_filters = len(filter_points) - 2
+    filters = np.zeros((num_filters, int(ftt_size / 2) + 1))
+
+    for n in range(num_filters):
+        start = filter_points[n]
+        mid = filter_points[n + 1]
+        end = filter_points[n + 2]
+
+        # Linearly increase values from 0 to 1
+        filters[n, start:mid] = np.linspace(0, 1, mid - start)
+
+        # Linearly decrease values from 1 to 0
+        filters[n, mid:end] = np.linspace(1, 0, end - mid)
+
+    return filters
+
+
+def get_filter_points(
+    sample_rate: int,
+    freq_min: int,
+    freq_high: int,
+    mel_filter_num: int = 10,
+    ftt_size: int = 1024,
+):
+    """
+    Calculate the filter points and frequencies for mel frequency filters.
+
+    Args:
+        sample_rate: The sample rate of the audio.
+        freq_min: The minimum frequency in Hertz.
+        freq_high: The maximum frequency in Hertz.
+        mel_filter_num: The number of mel filters (default is 10).
+        ftt_size: The size of the FFT (default is 1024).
+
+    Returns:
+        Filter points and corresponding frequencies.
+
+    Examples:
+    >>> get_filter_points(8000, 0, 4000, mel_filter_num=4, ftt_size=512)[0]
+    array([  0,  20,  51,  95, 161, 256])
+    """
+    # Convert minimum and maximum frequencies to mel scale
+    fmin_mel = freq_to_mel(freq_min)
+    fmax_mel = freq_to_mel(freq_high)
+
+    logging.info(f"MEL min: {fmin_mel}")
+    logging.info(f"MEL max: {fmax_mel}")
+
+    # Generate equally spaced mel frequencies
+    mels = np.linspace(fmin_mel, fmax_mel, num=mel_filter_num + 2)
+
+    # Convert mel frequencies back to Hertz
+    freqs = mel_to_freq(mels)
+
+    # Calculate filter points as integer values
+    filter_points = np.floor((ftt_size + 1) / sample_rate * freqs).astype(int)
+
+    return filter_points, freqs
+
+
+def discrete_cosine_transform(dct_filter_num: int, filter_num: int) -> np.ndarray:
+    """
+    Compute the Discrete Cosine Transform (DCT) basis matrix.
+
+    Args:
+        dct_filter_num: The number of DCT filters to generate.
+        filter_num: The number of the fbank filters.
+
+    Returns:
+        The DCT basis matrix.
+
+    Examples:
+    >>> round(discrete_cosine_transform(3, 5)[0][0], 5)
+    0.44721
+    """
+    basis = np.empty((dct_filter_num, filter_num))
+    basis[0, :] = 1.0 / np.sqrt(filter_num)
+
+    samples = np.arange(1, 2 * filter_num, 2) * np.pi / (2.0 * filter_num)
+
+    for i in range(1, dct_filter_num):
+        basis[i, :] = np.cos(i * samples) * np.sqrt(2.0 / filter_num)
+
+    return basis
+
+
+def example(wav_file_path: str = "./path-to-file/sample.wav") -> np.ndarray:
+    """
+    Example function to calculate Mel Frequency Cepstral Coefficients
+    (MFCCs) from an audio file.
+
+    Args:
+        wav_file_path: The path to the WAV audio file.
+
+    Returns:
+        np.ndarray: The computed MFCCs for the audio.
+    """
+    from scipy.io import wavfile
+
+    # Load the audio from the WAV file
+    sample_rate, audio = wavfile.read(wav_file_path)
+
+    # Calculate MFCCs
+    return mfcc(audio, sample_rate)
+
+
+if __name__ == "__main__":
+    import doctest
+
+    doctest.testmod()


Code review comments:
diff_hunk: @@ -0,0 +1,484 @@
+"""
+Mel Frequency Cepstral Coefficients (MFCC) Calculation
+
+MFCC is a feature widely used in audio and speech processing to represent the
+short-term power spectrum of a sound signal in a more compact and
+discriminative way. It is particularly popular in speech and audio processing
+tasks such as speech recognition and speaker identification.
+
+How MFCC is Calculated:
+1. Preprocessing:
+   - Load an audio signal and normalize it to ensure that the values fall
+     within a specific range (e.g., between -1 and 1).
+   - Frame the audio signal into overlapping, fixed-length segments, typically
+     using a technique like windowing to reduce spectral leakage.
+
+2. Fourier Transform:
+   - Apply a Fast Fourier Transform (FFT) to each audio frame to convert it
+     from the time domain to the frequency domain. This results in a
+     representation of the audio frame as a sequence of frequency components.
+
+3. Power Spectrum:
+   - Calculate the power spectrum by taking the squared magnitude of each
+     frequency component obtained from the FFT. This step measures the energy
+     distribution across different frequency bands.
+
+4. Mel Filterbank:
+   - Apply a set of triangular filterbanks spaced in the Mel frequency scale
+     to the power spectrum. These filters mimic the human auditory system's
+     frequency response. Each filterbank sums the power spectrum values within
+     its band.
+
+5. Logarithmic Compression:
+   - Take the logarithm (typically base 10) of the filterbank values to
+     compress the dynamic range. This step mimics the logarithmic response of
+     the human ear to sound intensity.
+
+6. Discrete Cosine Transform (DCT):
+   - Apply the Discrete Cosine Transform to the log filterbank energies to
+     obtain the MFCC coefficients. This transformation helps decorrelate the
+     filterbank energies and captures the most important features of the audio
+     signal.
+
+7. Feature Extraction:
+   - Select a subset of the DCT coefficients to form the feature vector.
+     Often, the first few coefficients (e.g., 12-13) are used for most
+     applications.
+
+References:
+- Mel-Frequency Cepstral Coefficients (MFCCs):
+  https://en.wikipedia.org/wiki/Mel-frequency_cepstrum
+- Speech and Language Processing by Daniel Jurafsky & James H. Martin:
+  https://web.stanford.edu/~jurafsky/slp3/
+- Mel Frequency Cepstral Coefficient (MFCC) tutorial
+  http://practicalcryptography.com/miscellaneous/machine-learning
+  /guide-mel-frequency-cepstral-coefficients-mfccs/
+
+Author: Amir Lavasani
+"""
+
+
+import logging
+
+import numpy as np
+import scipy.fftpack as fft
+from scipy.signal import get_window
+
+logging.basicConfig(level=logging.WARNING)
+
+
+def mfcc(
+    audio: np.ndarray,
+    sample_rate: int,
+    ftt_size: int = 1024,
+    hop_length: int = 20,
+    mel_filter_num: int = 10,
+    dct_filter_num: int = 40,
+) -> np.ndarray:
+    """
+    Calculate Mel Frequency Cepstral Coefficients (MFCCs) from an audio signal.
+
+    Args:
+        audio: The input audio signal.
+        sample_rate: The sample rate of the audio signal (in Hz).
+        ftt_size: The size of the FFT window (default is 1024).
+        hop_length: The hop length for frame creation (default is 20ms).
+        mel_filter_num: The number of Mel filters (default is 10).
+        dct_filter_num: The number of DCT filters (default is 40).
+
+    Returns:
+        A matrix of MFCCs for the input audio.
+
+    Raises:
+        ValueError: If the input audio is empty.
+
+    Example:
+    >>> import numpy as np
+    >>> sample_rate = 44100  # Sample rate of 44.1 kHz
+    >>> duration = 2.0  # Duration of 1 second
+    >>> t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)
+    >>> audio = 0.5 * np.sin(2 * np.pi * 440.0 * t)  # Generate a 440 Hz sine wave
+    >>> mfccs = mfcc(audio, sample_rate)
+    >>> mfccs.shape
+    (40, 101)
+    """
+    logging.info(f"Sample rate: {sample_rate}Hz")
+    logging.info(f"Audio duration: {len(audio) / sample_rate}s")
+    logging.info(f"Audio min: {np.min(audio)}")
+    logging.info(f"Audio max: {np.max(audio)}")
+
+    # normalize audio
+    audio_normalized = normalize(audio)
+
+    logging.info(f"Normalized audio min: {np.min(audio_normalized)}")
+    logging.info(f"Normalized audio max: {np.max(audio_normalized)}")
+
+    # frame audio into
+    audio_framed = audio_frames(
+        audio_normalized, sample_rate, ftt_size=ftt_size, hop_length=hop_length
+    )
+
+    logging.info(f"Framed audio shape: {audio_framed.shape}")
+    logging.info(f"First frame: {audio_framed[0]}")
+
+    # convert to frequency domain
+    # For simplicity we will choose the Hanning window.
+    window = get_window("hann", ftt_size, fftbins=True)
+    audio_windowed = audio_framed * window
+
+    logging.info(f"Windowed audio shape: {audio_windowed.shape}")
+    logging.info(f"First frame: {audio_windowed[0]}")
+
+    audio_fft = calculate_fft(audio_windowed, ftt_size)
+    logging.info(f"fft audio shape: {audio_fft.shape}")
+    logging.info(f"First frame: {audio_fft[0]}")
+
+    audio_power = calculate_signal_power(audio_fft)
+    logging.info(f"power audio shape: {audio_power.shape}")
+    logging.info(f"First frame: {audio_power[0]}")
+
+    filters = mel_spaced_filterbank(sample_rate, mel_filter_num, ftt_size)
+    logging.info(f"filters shape: {filters.shape}")
+
+    audio_filtered = np.dot(filters, np.transpose(audio_power))
+    audio_log = 10.0 * np.log10(audio_filtered)
+    logging.info(f"audio_log shape: {audio_log.shape}")
+
+    dct_filters = discrete_cosine_transform(dct_filter_num, mel_filter_num)
+    cepstral_coefficents = np.dot(dct_filters, audio_log)
+
+    logging.info(f"cepstral_coefficents shape: {cepstral_coefficents.shape}")
+    return cepstral_coefficents
+
+
+def normalize(audio: np.ndarray) -> np.ndarray:
+    """
+    Normalize an audio signal by scaling it to have values between -1 and 1.
+
+    Args:
+        audio: The input audio signal.
+
+    Returns:
+        The normalized audio signal.
+
+    Examples:
+    >>> audio = np.array([1, 2, 3, 4, 5])
+    >>> normalized_audio = normalize(audio)
+    >>> np.max(normalized_audio)
+    1.0
+    >>> np.min(normalized_audio)
+    0.2
+    """
+    # Find the maximum absolute value in the audio signal
+    max_abs_value = np.max(np.abs(audio))
+
+    # Divide the entire audio signal by the maximum absolute value
+    return audio / max_abs_value
+
+
+def audio_frames(
+    audio: np.ndarray,
+    sample_rate: int,
+    hop_length: int = 20,
+    ftt_size: int = 1024,
+) -> np.ndarray:
+    """
+    Split an audio signal into overlapping frames.
+
+    Args:
+        audio: The input audio signal.
+        sample_rate: The sample rate of the audio signal.
+        hop_length: The length of the hopping (default is 20ms).
+        ftt_size: The size of the FFT window (default is 1024).
+
+    Returns:
+        An array of overlapping frames.
+
+    Examples:
+    >>> import numpy as np
+    >>> audio = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]*1000)
+    >>> sample_rate = 8000
+    >>> frames = audio_frames(audio, sample_rate, hop_length=10, ftt_size=512)
+    >>> frames.shape
+    (126, 512)
+    """
+
+    hop_size = np.round(sample_rate * hop_length / 1000).astype(int)
+
+    # Pad the audio signal to handle edge cases
+    audio = np.pad(audio, int(ftt_size / 2), mode="reflect")
+
+    # Calculate the number of frames
+    frame_count = int((len(audio) - ftt_size) / hop_size) + 1
+
+    # Initialize an array to store the frames
+    frames = np.zeros((frame_count, ftt_size))
+
+    # Split the audio signal into frames
+    for n in range(frame_count):
+        frames[n] = audio[n * hop_size : n * hop_size + ftt_size]
+
+    return frames
+
+
+def calculate_fft(audio_windowed: np.ndarray, ftt_size: int = 1024) -> np.ndarray:
+    """
+    Calculate the Fast Fourier Transform (FFT) of windowed audio data.
+
+    Args:
+        audio_windowed: The windowed audio signal.
+        ftt_size: The size of the FFT (default is 1024).
+
+    Returns:
+        The FFT of the audio data.
+
+    Examples:
+    >>> import numpy as np
path: machine_learning/mfcc.py
text: @user1:
This was already done on line 63
```suggestion
```

diff_hunk: @@ -0,0 +1,484 @@
+"""
+Mel Frequency Cepstral Coefficients (MFCC) Calculation
+
+MFCC is a feature widely used in audio and speech processing to represent the
+short-term power spectrum of a sound signal in a more compact and
+discriminative way. It is particularly popular in speech and audio processing
+tasks such as speech recognition and speaker identification.
+
+How MFCC is Calculated:
+1. Preprocessing:
+   - Load an audio signal and normalize it to ensure that the values fall
+     within a specific range (e.g., between -1 and 1).
+   - Frame the audio signal into overlapping, fixed-length segments, typically
+     using a technique like windowing to reduce spectral leakage.
+
+2. Fourier Transform:
+   - Apply a Fast Fourier Transform (FFT) to each audio frame to convert it
+     from the time domain to the frequency domain. This results in a
+     representation of the audio frame as a sequence of frequency components.
+
+3. Power Spectrum:
+   - Calculate the power spectrum by taking the squared magnitude of each
+     frequency component obtained from the FFT. This step measures the energy
+     distribution across different frequency bands.
+
+4. Mel Filterbank:
+   - Apply a set of triangular filterbanks spaced in the Mel frequency scale
+     to the power spectrum. These filters mimic the human auditory system's
+     frequency response. Each filterbank sums the power spectrum values within
+     its band.
+
+5. Logarithmic Compression:
+   - Take the logarithm (typically base 10) of the filterbank values to
+     compress the dynamic range. This step mimics the logarithmic response of
+     the human ear to sound intensity.
+
+6. Discrete Cosine Transform (DCT):
+   - Apply the Discrete Cosine Transform to the log filterbank energies to
+     obtain the MFCC coefficients. This transformation helps decorrelate the
+     filterbank energies and captures the most important features of the audio
+     signal.
+
+7. Feature Extraction:
+   - Select a subset of the DCT coefficients to form the feature vector.
+     Often, the first few coefficients (e.g., 12-13) are used for most
+     applications.
+
+References:
+- Mel-Frequency Cepstral Coefficients (MFCCs):
+  https://en.wikipedia.org/wiki/Mel-frequency_cepstrum
+- Speech and Language Processing by Daniel Jurafsky & James H. Martin:
+  https://web.stanford.edu/~jurafsky/slp3/
+- Mel Frequency Cepstral Coefficient (MFCC) tutorial
+  http://practicalcryptography.com/miscellaneous/machine-learning
+  /guide-mel-frequency-cepstral-coefficients-mfccs/
+
+Author: Amir Lavasani
+"""
+
+
+import logging
+
+import numpy as np
+import scipy.fftpack as fft
+from scipy.signal import get_window
+
+logging.basicConfig(level=logging.WARNING)
+
+
+def mfcc(
+    audio: np.ndarray,
+    sample_rate: int,
+    ftt_size: int = 1024,
+    hop_length: int = 20,
+    mel_filter_num: int = 10,
+    dct_filter_num: int = 40,
+) -> np.ndarray:
+    """
+    Calculate Mel Frequency Cepstral Coefficients (MFCCs) from an audio signal.
+
+    Args:
+        audio: The input audio signal.
+        sample_rate: The sample rate of the audio signal (in Hz).
+        ftt_size: The size of the FFT window (default is 1024).
+        hop_length: The hop length for frame creation (default is 20ms).
+        mel_filter_num: The number of Mel filters (default is 10).
+        dct_filter_num: The number of DCT filters (default is 40).
+
+    Returns:
+        A matrix of MFCCs for the input audio.
+
+    Raises:
+        ValueError: If the input audio is empty.
+
+    Example:
+    >>> import numpy as np
+    >>> sample_rate = 44100  # Sample rate of 44.1 kHz
+    >>> duration = 2.0  # Duration of 1 second
+    >>> t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)
+    >>> audio = 0.5 * np.sin(2 * np.pi * 440.0 * t)  # Generate a 440 Hz sine wave
+    >>> mfccs = mfcc(audio, sample_rate)
+    >>> mfccs.shape
+    (40, 101)
+    """
+    logging.info(f"Sample rate: {sample_rate}Hz")
+    logging.info(f"Audio duration: {len(audio) / sample_rate}s")
+    logging.info(f"Audio min: {np.min(audio)}")
+    logging.info(f"Audio max: {np.max(audio)}")
+
+    # normalize audio
+    audio_normalized = normalize(audio)
+
+    logging.info(f"Normalized audio min: {np.min(audio_normalized)}")
+    logging.info(f"Normalized audio max: {np.max(audio_normalized)}")
+
+    # frame audio into
+    audio_framed = audio_frames(
+        audio_normalized, sample_rate, ftt_size=ftt_size, hop_length=hop_length
+    )
+
+    logging.info(f"Framed audio shape: {audio_framed.shape}")
+    logging.info(f"First frame: {audio_framed[0]}")
+
+    # convert to frequency domain
+    # For simplicity we will choose the Hanning window.
+    window = get_window("hann", ftt_size, fftbins=True)
+    audio_windowed = audio_framed * window
+
+    logging.info(f"Windowed audio shape: {audio_windowed.shape}")
+    logging.info(f"First frame: {audio_windowed[0]}")
+
+    audio_fft = calculate_fft(audio_windowed, ftt_size)
+    logging.info(f"fft audio shape: {audio_fft.shape}")
+    logging.info(f"First frame: {audio_fft[0]}")
+
+    audio_power = calculate_signal_power(audio_fft)
+    logging.info(f"power audio shape: {audio_power.shape}")
+    logging.info(f"First frame: {audio_power[0]}")
+
+    filters = mel_spaced_filterbank(sample_rate, mel_filter_num, ftt_size)
+    logging.info(f"filters shape: {filters.shape}")
+
+    audio_filtered = np.dot(filters, np.transpose(audio_power))
+    audio_log = 10.0 * np.log10(audio_filtered)
+    logging.info(f"audio_log shape: {audio_log.shape}")
+
+    dct_filters = discrete_cosine_transform(dct_filter_num, mel_filter_num)
+    cepstral_coefficents = np.dot(dct_filters, audio_log)
+
+    logging.info(f"cepstral_coefficents shape: {cepstral_coefficents.shape}")
+    return cepstral_coefficents
+
+
+def normalize(audio: np.ndarray) -> np.ndarray:
+    """
+    Normalize an audio signal by scaling it to have values between -1 and 1.
+
+    Args:
+        audio: The input audio signal.
+
+    Returns:
+        The normalized audio signal.
+
+    Examples:
+    >>> audio = np.array([1, 2, 3, 4, 5])
+    >>> normalized_audio = normalize(audio)
+    >>> np.max(normalized_audio)
+    1.0
+    >>> np.min(normalized_audio)
+    0.2
+    """
+    # Find the maximum absolute value in the audio signal
+    max_abs_value = np.max(np.abs(audio))
+
+    # Divide the entire audio signal by the maximum absolute value
+    return audio / max_abs_value
+
+
+def audio_frames(
+    audio: np.ndarray,
+    sample_rate: int,
+    hop_length: int = 20,
+    ftt_size: int = 1024,
+) -> np.ndarray:
+    """
+    Split an audio signal into overlapping frames.
+
+    Args:
+        audio: The input audio signal.
+        sample_rate: The sample rate of the audio signal.
+        hop_length: The length of the hopping (default is 20ms).
+        ftt_size: The size of the FFT window (default is 1024).
+
+    Returns:
+        An array of overlapping frames.
+
+    Examples:
+    >>> import numpy as np
path: machine_learning/mfcc.py
text: @user1:
This was already done on line 63
```suggestion
```

diff_hunk: @@ -0,0 +1,484 @@
+"""
+Mel Frequency Cepstral Coefficients (MFCC) Calculation
+
+MFCC is a feature widely used in audio and speech processing to represent the
+short-term power spectrum of a sound signal in a more compact and
+discriminative way. It is particularly popular in speech and audio processing
+tasks such as speech recognition and speaker identification.
+
+How MFCC is Calculated:
+1. Preprocessing:
+   - Load an audio signal and normalize it to ensure that the values fall
+     within a specific range (e.g., between -1 and 1).
+   - Frame the audio signal into overlapping, fixed-length segments, typically
+     using a technique like windowing to reduce spectral leakage.
+
+2. Fourier Transform:
+   - Apply a Fast Fourier Transform (FFT) to each audio frame to convert it
+     from the time domain to the frequency domain. This results in a
+     representation of the audio frame as a sequence of frequency components.
+
+3. Power Spectrum:
+   - Calculate the power spectrum by taking the squared magnitude of each
+     frequency component obtained from the FFT. This step measures the energy
+     distribution across different frequency bands.
+
+4. Mel Filterbank:
+   - Apply a set of triangular filterbanks spaced in the Mel frequency scale
+     to the power spectrum. These filters mimic the human auditory system's
+     frequency response. Each filterbank sums the power spectrum values within
+     its band.
+
+5. Logarithmic Compression:
+   - Take the logarithm (typically base 10) of the filterbank values to
+     compress the dynamic range. This step mimics the logarithmic response of
+     the human ear to sound intensity.
+
+6. Discrete Cosine Transform (DCT):
+   - Apply the Discrete Cosine Transform to the log filterbank energies to
+     obtain the MFCC coefficients. This transformation helps decorrelate the
+     filterbank energies and captures the most important features of the audio
+     signal.
+
+7. Feature Extraction:
+   - Select a subset of the DCT coefficients to form the feature vector.
+     Often, the first few coefficients (e.g., 12-13) are used for most
+     applications.
+
+References:
+- Mel-Frequency Cepstral Coefficients (MFCCs):
+  https://en.wikipedia.org/wiki/Mel-frequency_cepstrum
+- Speech and Language Processing by Daniel Jurafsky & James H. Martin:
+  https://web.stanford.edu/~jurafsky/slp3/
+- Mel Frequency Cepstral Coefficient (MFCC) tutorial
+  http://practicalcryptography.com/miscellaneous/machine-learning
+  /guide-mel-frequency-cepstral-coefficients-mfccs/
+
+Author: Amir Lavasani
+"""
+
+
+import logging
+
+import numpy as np
+import scipy.fftpack as fft
+from scipy.signal import get_window
+
+logging.basicConfig(level=logging.WARNING)
+
+
+def mfcc(
+    audio: np.ndarray,
+    sample_rate: int,
+    ftt_size: int = 1024,
+    hop_length: int = 20,
+    mel_filter_num: int = 10,
+    dct_filter_num: int = 40,
+) -> np.ndarray:
+    """
+    Calculate Mel Frequency Cepstral Coefficients (MFCCs) from an audio signal.
+
+    Args:
+        audio: The input audio signal.
+        sample_rate: The sample rate of the audio signal (in Hz).
+        ftt_size: The size of the FFT window (default is 1024).
+        hop_length: The hop length for frame creation (default is 20ms).
+        mel_filter_num: The number of Mel filters (default is 10).
+        dct_filter_num: The number of DCT filters (default is 40).
+
+    Returns:
+        A matrix of MFCCs for the input audio.
+
+    Raises:
+        ValueError: If the input audio is empty.
+
+    Example:
+    >>> import numpy as np
path: machine_learning/mfcc.py
text: @user1:
This was already done on line 63
```suggestion
```

diff_hunk: @@ -0,0 +1,484 @@
+"""
+Mel Frequency Cepstral Coefficients (MFCC) Calculation
+
+MFCC is a feature widely used in audio and speech processing to represent the
+short-term power spectrum of a sound signal in a more compact and
+discriminative way. It is particularly popular in speech and audio processing
+tasks such as speech recognition and speaker identification.
+
+How MFCC is Calculated:
+1. Preprocessing:
+   - Load an audio signal and normalize it to ensure that the values fall
+     within a specific range (e.g., between -1 and 1).
+   - Frame the audio signal into overlapping, fixed-length segments, typically
+     using a technique like windowing to reduce spectral leakage.
+
+2. Fourier Transform:
+   - Apply a Fast Fourier Transform (FFT) to each audio frame to convert it
+     from the time domain to the frequency domain. This results in a
+     representation of the audio frame as a sequence of frequency components.
+
+3. Power Spectrum:
+   - Calculate the power spectrum by taking the squared magnitude of each
+     frequency component obtained from the FFT. This step measures the energy
+     distribution across different frequency bands.
+
+4. Mel Filterbank:
+   - Apply a set of triangular filterbanks spaced in the Mel frequency scale
+     to the power spectrum. These filters mimic the human auditory system's
+     frequency response. Each filterbank sums the power spectrum values within
+     its band.
+
+5. Logarithmic Compression:
+   - Take the logarithm (typically base 10) of the filterbank values to
+     compress the dynamic range. This step mimics the logarithmic response of
+     the human ear to sound intensity.
+
+6. Discrete Cosine Transform (DCT):
+   - Apply the Discrete Cosine Transform to the log filterbank energies to
+     obtain the MFCC coefficients. This transformation helps decorrelate the
+     filterbank energies and captures the most important features of the audio
+     signal.
+
+7. Feature Extraction:
+   - Select a subset of the DCT coefficients to form the feature vector.
+     Often, the first few coefficients (e.g., 12-13) are used for most
+     applications.
+
+References:
+- Mel-Frequency Cepstral Coefficients (MFCCs):
+  https://en.wikipedia.org/wiki/Mel-frequency_cepstrum
+- Speech and Language Processing by Daniel Jurafsky & James H. Martin:
+  https://web.stanford.edu/~jurafsky/slp3/
+- Mel Frequency Cepstral Coefficient (MFCC) tutorial
+  http://practicalcryptography.com/miscellaneous/machine-learning
+  /guide-mel-frequency-cepstral-coefficients-mfccs/
+
+Author: Amir Lavasani
+"""
+
+
+import logging
+
+import numpy as np
+import scipy.fftpack as fft
+from scipy.signal import get_window
+
+logging.basicConfig(level=logging.WARNING)
+
+
+def mfcc(
+    audio: np.ndarray,
+    sample_rate: int,
+    ftt_size: int = 1024,
+    hop_length: int = 20,
+    mel_filter_num: int = 10,
+    dct_filter_num: int = 40,
+) -> np.ndarray:
+    """
+    Calculate Mel Frequency Cepstral Coefficients (MFCCs) from an audio signal.
+
+    Args:
+        audio: The input audio signal.
+        sample_rate: The sample rate of the audio signal (in Hz).
+        ftt_size: The size of the FFT window (default is 1024).
+        hop_length: The hop length for frame creation (default is 20ms).
+        mel_filter_num: The number of Mel filters (default is 10).
+        dct_filter_num: The number of DCT filters (default is 40).
+
+    Returns:
+        A matrix of MFCCs for the input audio.
+
+    Raises:
+        ValueError: If the input audio is empty.
+
+    Example:
+    >>> import numpy as np
+    >>> sample_rate = 44100  # Sample rate of 44.1 kHz
+    >>> duration = 2.0  # Duration of 1 second
+    >>> t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)
+    >>> audio = 0.5 * np.sin(2 * np.pi * 440.0 * t)  # Generate a 440 Hz sine wave
+    >>> mfccs = mfcc(audio, sample_rate)
+    >>> mfccs.shape
+    (40, 101)
+    """
+    logging.info(f"Sample rate: {sample_rate}Hz")
+    logging.info(f"Audio duration: {len(audio) / sample_rate}s")
+    logging.info(f"Audio min: {np.min(audio)}")
+    logging.info(f"Audio max: {np.max(audio)}")
+
+    # normalize audio
+    audio_normalized = normalize(audio)
+
+    logging.info(f"Normalized audio min: {np.min(audio_normalized)}")
+    logging.info(f"Normalized audio max: {np.max(audio_normalized)}")
+
+    # frame audio into
+    audio_framed = audio_frames(
+        audio_normalized, sample_rate, ftt_size=ftt_size, hop_length=hop_length
+    )
+
+    logging.info(f"Framed audio shape: {audio_framed.shape}")
+    logging.info(f"First frame: {audio_framed[0]}")
+
+    # convert to frequency domain
+    # For simplicity we will choose the Hanning window.
+    window = get_window("hann", ftt_size, fftbins=True)
+    audio_windowed = audio_framed * window
+
+    logging.info(f"Windowed audio shape: {audio_windowed.shape}")
+    logging.info(f"First frame: {audio_windowed[0]}")
+
+    audio_fft = calculate_fft(audio_windowed, ftt_size)
+    logging.info(f"fft audio shape: {audio_fft.shape}")
+    logging.info(f"First frame: {audio_fft[0]}")
+
+    audio_power = calculate_signal_power(audio_fft)
+    logging.info(f"power audio shape: {audio_power.shape}")
+    logging.info(f"First frame: {audio_power[0]}")
+
+    filters = mel_spaced_filterbank(sample_rate, mel_filter_num, ftt_size)
+    logging.info(f"filters shape: {filters.shape}")
+
+    audio_filtered = np.dot(filters, np.transpose(audio_power))
+    audio_log = 10.0 * np.log10(audio_filtered)
+    logging.info(f"audio_log shape: {audio_log.shape}")
+
+    dct_filters = discrete_cosine_transform(dct_filter_num, mel_filter_num)
+    cepstral_coefficents = np.dot(dct_filters, audio_log)
+
+    logging.info(f"cepstral_coefficents shape: {cepstral_coefficents.shape}")
+    return cepstral_coefficents
+
+
+def normalize(audio: np.ndarray) -> np.ndarray:
+    """
+    Normalize an audio signal by scaling it to have values between -1 and 1.
+
+    Args:
+        audio: The input audio signal.
+
+    Returns:
+        The normalized audio signal.
+
+    Examples:
+    >>> audio = np.array([1, 2, 3, 4, 5])
+    >>> normalized_audio = normalize(audio)
+    >>> np.max(normalized_audio)
+    1.0
+    >>> np.min(normalized_audio)
+    0.2
+    """
+    # Find the maximum absolute value in the audio signal
+    max_abs_value = np.max(np.abs(audio))
+
+    # Divide the entire audio signal by the maximum absolute value
+    return audio / max_abs_value
+
+
+def audio_frames(
+    audio: np.ndarray,
+    sample_rate: int,
+    hop_length: int = 20,
+    ftt_size: int = 1024,
+) -> np.ndarray:
+    """
+    Split an audio signal into overlapping frames.
+
+    Args:
+        audio: The input audio signal.
+        sample_rate: The sample rate of the audio signal.
+        hop_length: The length of the hopping (default is 20ms).
+        ftt_size: The size of the FFT window (default is 1024).
+
+    Returns:
+        An array of overlapping frames.
+
+    Examples:
+    >>> import numpy as np
+    >>> audio = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]*1000)
+    >>> sample_rate = 8000
+    >>> frames = audio_frames(audio, sample_rate, hop_length=10, ftt_size=512)
+    >>> frames.shape
+    (126, 512)
+    """
+
+    hop_size = np.round(sample_rate * hop_length / 1000).astype(int)
+
+    # Pad the audio signal to handle edge cases
+    audio = np.pad(audio, int(ftt_size / 2), mode="reflect")
+
+    # Calculate the number of frames
+    frame_count = int((len(audio) - ftt_size) / hop_size) + 1
+
+    # Initialize an array to store the frames
+    frames = np.zeros((frame_count, ftt_size))
+
+    # Split the audio signal into frames
+    for n in range(frame_count):
+        frames[n] = audio[n * hop_size : n * hop_size + ftt_size]
+
+    return frames
+
+
+def calculate_fft(audio_windowed: np.ndarray, ftt_size: int = 1024) -> np.ndarray:
+    """
+    Calculate the Fast Fourier Transform (FFT) of windowed audio data.
+
+    Args:
+        audio_windowed: The windowed audio signal.
+        ftt_size: The size of the FFT (default is 1024).
+
+    Returns:
+        The FFT of the audio data.
+
+    Examples:
+    >>> import numpy as np
+    >>> audio_windowed = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
+    >>> audio_fft = calculate_fft(audio_windowed, ftt_size=4)
+    >>> np.allclose(audio_fft[0], np.array([6.0+0.j, -1.5+0.8660254j, -1.5-0.8660254j]))
+    True
+    """
+    # Transpose the audio data to have time in rows and channels in columns
+    audio_transposed = np.transpose(audio_windowed)
+
+    # Initialize an array to store the FFT results
+    audio_fft = np.empty(
+        (int(1 + ftt_size // 2), audio_transposed.shape[1]),
+        dtype=np.complex64,
+        order="F",
+    )
+
+    # Compute FFT for each channel
+    for n in range(audio_fft.shape[1]):
+        audio_fft[:, n] = fft.fft(audio_transposed[:, n], axis=0)[: audio_fft.shape[0]]
+
+    # Transpose the FFT results back to the original shape
+    return np.transpose(audio_fft)
+
+
+def calculate_signal_power(audio_fft: np.ndarray) -> np.ndarray:
+    """
+    Calculate the power of the audio signal from its FFT.
+
+    Args:
+        audio_fft: The FFT of the audio signal.
+
+    Returns:
+        The power of the audio signal.
+
+    Examples:
+    >>> import numpy as np
path: machine_learning/mfcc.py
text: @user1:
This was already done on line 63
```suggestion
```