예제 #1
0
    def test_loudness_roundtrip(self):
        for lo in (55, 62, 73, 84):
            for frequency in (58, 120, 223, 489, 4000, 9800):
                spl = loudness.loudness_to_spl(lo, frequency)
                lo_roundtrip = loudness.spl_to_loudness(spl, frequency)

                self.assertLess(abs(lo - lo_roundtrip), 5e-2)
예제 #2
0
def plot_iso_examples(data: Dict[int, Dict[str, Any]], path: str):
    """Plot ISO equal loudness curves w/ markers for the data examples."""
    _, ax = plt.subplots(1, 1, figsize=(12, 14))
    frequencies_on_range = [i for i in range(20, 20000, 10)]

    # These are the colors that will be used in the plot
    colors = [
        "#1f77b4", "#aec7e8", "#ff7f0e", "#ffbb78", "#2ca02c", "#98df8a",
        "#d62728", "#ff9896", "#9467bd", "#c5b0d5", "#8c564b", "#c49c94",
        "#e377c2", "#f7b6d2", "#7f7f7f", "#c7c7c7", "#bcbd22", "#dbdb8d",
        "#17becf", "#9edae5"
    ]
    ax.set_prop_cycle(color=colors)
    plt.xscale("log")
    ax.set_xlabel("Frequency (Hz)")
    ax.set_ylabel("SPL (dB)")
    ax.set_title("ISO equal loudness curves")
    phons_levels = [i * 10 for i in range(10)]
    legend_handles = ["{} Phons".format(phons) for phons in phons_levels]
    levels_per_phons = []
    for phons in phons_levels:
        levels = []
        for frequency in frequencies_on_range:
            level = loudness.loudness_to_spl(phons, frequency)
            levels.append(level)
        levels_per_phons.append(levels)
    for i, y in enumerate(levels_per_phons):
        plt.plot(frequencies_on_range, y, label=legend_handles[i])

    for i, examples in enumerate(data.values()):
        level = examples["ref1000_spl"]
        plt.scatter(1000, level, marker="x", c="b")
        color = colors[i]
        for other_tone in examples["other_tones"]:
            if "error" in other_tone:
                plt.errorbar(other_tone["frequency"],
                             other_tone["level"],
                             c=color,
                             yerr=other_tone["error"],
                             fmt="o")
            else:
                plt.scatter(other_tone["frequency"],
                            other_tone["level"],
                            marker="x",
                            c=color)
    ax.legend()
    plt.savefig(os.path.join(path, "iso_repro.png"))
예제 #3
0
def plot_loudness_conversion(path: str, phons_levels: List[int],
                             frequencies: List[int]):
    """PLot ISO loudness curves with loudness to decibel conversion."""
    _, ax = plt.subplots(1, 1, figsize=(12, 14))

    # These are the colors that will be used in the plot
    ax.set_prop_cycle(color=[
        "#1f77b4", "#aec7e8", "#ff7f0e", "#ffbb78", "#2ca02c", "#98df8a",
        "#d62728", "#ff9896", "#9467bd", "#c5b0d5", "#8c564b", "#c49c94",
        "#e377c2", "#f7b6d2", "#7f7f7f", "#c7c7c7", "#bcbd22", "#dbdb8d",
        "#17becf", "#9edae5"
    ])
    plt.xscale("log")
    levels_per_phons = []
    for phons in phons_levels:
        levels = []
        for frequency in frequencies:
            level = loudness.loudness_to_spl(phons, frequency)
            levels.append(level)
        levels_per_phons.append(levels)
    for y in levels_per_phons:
        plt.plot(frequencies, y)
    plt.savefig(path)
예제 #4
0
def generate_data(
        num_examples_per_cb: int,
        min_tones: int,
        max_tones: int,
        clip_db: int,
        desired_skewness: int,
        desired_mean: int,
        desired_variance: int,
        min_frequency: int,
        max_frequency: int,
        critical_bands: List[int],
        min_phons=0,
        max_phons=80,
        max_iterations=1000000) -> Dict[int, List[Dict[str, List[int]]]]:
    """Generates all listening data.

  Generates examples until each critical band has exactly
  `num_examples_per_cb` number of examples.
  TODO(lauraruis): check if n_per_cb cannot be generated (e.g.
  due to too large beatrange)

  Args:
    num_examples_per_cb: how many frequencies to sample per CB
    min_tones: minimum number of frequencies per example
    max_tones: maximum number of frequencies per example
    clip_db: maximum spl
    desired_skewness: skewness of skewed normal distr. for phons
    desired_mean: mean of skewed normal distr. for phons
    desired_variance: variance of skewed normal distr. for phons
    min_frequency: below this frequency no tones will be generated
    max_frequency: above this frequency no tones will be generated
    critical_bands: list of critical bands
    min_phons: minimum level of phons for examples
    max_phons: maximum level of phons for examples
    max_iterations: how long to try combining examples

  Returns:
    Dict with data examples.
  """
    # Initialize the structures for keeping the data.
    data = {i: [] for i in range(min_tones, max_tones + 1)}
    num_examples = 0

    # Calculate the needed shift and scaling for the SN distribution over phons.
    sn_shift, sn_scale = distributions.calculate_shift_scaling_skewnorm(
        desired_variance=desired_variance,
        desired_mean=desired_mean,
        alpha=desired_skewness)

    # Sample n examples per critical band.
    examples_per_cb = sample_n_per_cb(num_examples_per_cb, critical_bands)

    # Generate examples by combining a subset of tones until they run out.
    while len(examples_per_cb) >= min_tones:
        # Sample a number of tones for the example.
        num_tones = min(len(examples_per_cb),
                        random.randint(min_tones, max_tones))

        # If less than max_tones examples left, check how far apart they are.
        if len(examples_per_cb) <= max_tones:
            if not check_frequencies(
                    np.array([ex.frequency for ex in examples_per_cb]),
                    min_frequency, max_frequency):
                break

        # Sample frequencies from the pre-generated tones until they satisfy the
        # constraint of being beat_range apart.
        frequencies = np.array([100] * num_tones)
        iteration = 0
        while not check_frequencies(
                frequencies, min_frequency,
                max_frequency) and iteration < max_iterations:
            sampled_idxs = random.sample(range(len(examples_per_cb)),
                                         k=num_tones)
            for i, sampled_idx in enumerate(sampled_idxs):
                frequencies[i] = examples_per_cb[sampled_idx].frequency
            iteration += 1
        # If the correct frequencies weren't found, stop generation.
        if iteration >= max_iterations:
            print("WARNING: didn't find correct frequencies: ", frequencies)
            break

        # Delete the used tones from the pregenerated list.
        for sampled_idx in sorted(sampled_idxs, reverse=True):
            del examples_per_cb[sampled_idx]

        # Sample phons for each tone on a skewed normal distribution.
        phons = np.array([100] * num_tones)
        while not check_phons(phons, min_phons, max_phons):
            phons = distributions.sample_skewed_distribution(
                sn_shift,
                sn_scale,
                num_samples=num_tones,
                alpha=desired_skewness)
            phons = np.round(phons)
        num_examples += 1

        # Convert phons to sound pressure level in decibel with the ISO 226.
        sp_levels = [
            np.round(
                loudness.loudness_to_spl(loudness_phon=loudness_phons,
                                         frequency=frequency))
            for loudness_phons, frequency in zip(phons, frequencies)
        ]
        sp_levels = np.clip(np.array(sp_levels), a_min=0, a_max=clip_db)

        data[num_tones].append({
            "frequencies": list(frequencies),
            "phons": list(phons),
            "levels": list(sp_levels)
        })
    return data
예제 #5
0
def generate_iso_repro_examples(num_examples_per_curve: int,
                                num_curves: int,
                                clip_db=80) -> Dict[int, Dict[str, Any]]:
    """Generates a particular amount of examples per ISO equal loudness curve.

  Args:
    num_examples_per_curve: how many examples per curve to generate
    num_curves: how many equal loudness curves (phons) to generate examples from
    clip_db: maximum decibel level

  Returns:
    The data in a dict with as key the phons level (curve) and as values
    the reference tone (1000 Hz) level and a list of examples for that curve.

  """
    available_phons_levels = [20, 30, 40, 50, 60, 70, 80]
    assert num_curves < len(
        available_phons_levels), "Too many curves specified."
    phons_ptr = len(available_phons_levels) // 2
    chosen_phons_levels = []
    while len(chosen_phons_levels) < num_curves:
        chosen_phons_levels.append(available_phons_levels[phons_ptr])
        del available_phons_levels[phons_ptr]
        phons_ptr = len(available_phons_levels) // 2
    chosen_phons_levels.sort()

    # Find minimum frequency and phons level before it exceeds 90 dB
    max_phons = chosen_phons_levels[-1]
    frequency_ptr = -1
    spl = clip_db + 1
    while spl > clip_db and frequency_ptr < len(ISO_FREQUENCIES):
        frequency_ptr += 1
        spl = loudness.loudness_to_spl(max_phons,
                                       ISO_FREQUENCIES[frequency_ptr])
    min_frequency = ISO_FREQUENCIES[frequency_ptr]

    # Find maximum frequency and phons level before it exceeds 90 dB
    frequency_ptr = len(ISO_FREQUENCIES) // 2
    spl = 0
    while spl < clip_db and frequency_ptr < len(ISO_FREQUENCIES):
        spl = loudness.loudness_to_spl(max_phons,
                                       ISO_FREQUENCIES[frequency_ptr])
        frequency_ptr += 1
    max_frequency = ISO_FREQUENCIES[frequency_ptr - 1]
    if min_frequency > max_frequency:
        raise ValueError(
            "Can't generate examples below {} decibel for {} curves. "
            "Consider increasing clip_db or decreasing num_curves.".format(
                clip_db, num_curves))

    # Get the right number of frequencies with equal distance between them.
    frequency_steps = (np.log(max_frequency) -
                       np.log(min_frequency)) / num_examples_per_curve
    frequencies = [
        np.log(min_frequency) + frequency_steps * i
        for i in range(num_examples_per_curve)
    ]
    frequencies = [np.round(np.exp(frequency)) for frequency in frequencies]

    # Generate the data
    data = {}
    for phons_level in chosen_phons_levels:
        data[phons_level] = {
            "ref1000_spl":
            np.round(loudness.loudness_to_spl(phons_level, 1000)),
            "other_tones": []
        }
        for frequency in frequencies:
            spl = loudness.loudness_to_spl(phons_level, frequency)
            data[phons_level]["other_tones"].append({
                "frequency": frequency,
                "level": np.round(spl)
            })
    return data