import os
from pathlib import Path

import numpy as np

AUDIO_FILENAME_ENDINGS = (".aiff", ".flac", ".m4a", ".mp3", ".ogg", ".opus", ".wav")


def get_file_paths(
    root_path, filename_endings=AUDIO_FILENAME_ENDINGS, traverse_subdirectories=True
):
    """Return a list of paths to all files with the given filename extensions in a directory.
    Also traverses subdirectories by default.
    """
    file_paths = []

    for root, dirs, filenames in os.walk(root_path):
        filenames = sorted(filenames)
        for filename in filenames:
            input_path = os.path.abspath(root)
            file_path = os.path.join(input_path, filename)

            if filename.lower().endswith(filename_endings):
                file_paths.append(Path(file_path))
        if not traverse_subdirectories:
            # prevent descending into subfolders
            break

    return file_paths


def calculate_rms(samples):
    """Given a numpy array of audio samples, return its Root Mean Square (RMS)."""
    return np.sqrt(np.mean(np.square(samples), axis=-1))


def calculate_desired_noise_rms(clean_rms, snr):
    """
    Given the Root Mean Square (RMS) of a clean sound and a desired signal-to-noise ratio (SNR),
    calculate the desired RMS of a noise sound to be mixed in.

    Based on https://github.com/Sato-Kunihiko/audio-SNR/blob/8d2c933b6c0afe6f1203251f4877e7a1068a6130/create_mixed_audio_file.py#L20
    :param clean_rms: Root Mean Square (RMS) - a value between 0.0 and 1.0
    :param snr: Signal-to-Noise (SNR) Ratio in dB - typically somewhere between -20 and 60
    :return:
    """
    a = float(snr) / 20
    noise_rms = clean_rms / (10 ** a)
    return noise_rms


def convert_decibels_to_amplitude_ratio(decibels):
    return 10 ** (decibels / 20)


def is_waveform_multichannel(samples):
    """
    Return bool that answers the question: Is the given ndarray a multichannel waveform or not?

    :param samples: numpy ndarray
    :return:
    """
    return len(samples.shape) > 1


def is_spectrogram_multichannel(spectrogram):
    """
    Return bool that answers the question: Is the given ndarray a multichannel spectrogram?

    :param samples: numpy ndarray
    :return:
    """
    return len(spectrogram.shape) > 2 and spectrogram.shape[-1] > 1


def convert_float_samples_to_int16(y):
    """Convert floating-point numpy array of audio samples to int16."""
    if not issubclass(y.dtype.type, np.floating):
        raise ValueError("input samples not floating-point")
    return (y * np.iinfo(np.int16).max).astype(np.int16)

[((17, 33, 17, 51), 'os.walk', 'os.walk', ({(17, 41, 17, 50): 'root_path'}, {}), '(root_path)', False, 'import os\n'), ((20, 25, 20, 46), 'os.path.abspath', 'os.path.abspath', ({(20, 41, 20, 45): 'root'}, {}), '(root)', False, 'import os\n'), ((21, 24, 21, 58), 'os.path.join', 'os.path.join', ({(21, 37, 21, 47): 'input_path', (21, 49, 21, 57): 'filename'}, {}), '(input_path, filename)', False, 'import os\n'), ((34, 27, 34, 45), 'numpy.square', 'np.square', ({(34, 37, 34, 44): 'samples'}, {}), '(samples)', True, 'import numpy as np\n'), ((24, 34, 24, 49), 'pathlib.Path', 'Path', ({(24, 39, 24, 48): 'file_path'}, {}), '(file_path)', False, 'from pathlib import Path\n'), ((80, 16, 80, 34), 'numpy.iinfo', 'np.iinfo', ({(80, 25, 80, 33): 'np.int16'}, {}), '(np.int16)', True, 'import numpy as np\n')]