Ejemplo n.º 1
0
def main():
    stock = FrequencyResponse.read_from_csv('data/HE400S stock.csv')
    focus = FrequencyResponse.read_from_csv('data/HE400S focus.csv')
    base = FrequencyResponse.read_from_csv('innerfidelity/data/HiFiMAN HE400S/HiFiMAN HE400S.csv')

    stock.interpolate(f_min=20, f_max=20000)
    stock.center()
    focus.interpolate(f_min=20, f_max=20000)
    focus.center()
    base.interpolate(f=stock.frequency)
    base.center()
    diff = FrequencyResponse(name='Diff', frequency=stock.frequency, raw=focus.raw-stock.raw)
    fr = FrequencyResponse(name='HE400S with Focus Pads', frequency=stock.frequency, raw=base.raw+diff.raw)

    _fr = FrequencyResponse(name='debug', frequency=stock.frequency, raw=base.raw, smoothed=diff.raw, equalization=fr.raw)
    _fr.plot_graph()

    fr.smoothen_fractional_octave(window_size=1 / 5, iterations=10, treble_window_size=1 / 2, treble_iterations=100)


    #stock.plot_graph()
    #focus.plot_graph()
    #diff.plot_graph()
    #base.plot_graph()
    #fr.plot_graph()
    os.makedirs('innerfidelity/data/HiFiMAN HE400S with Focus Pads', exist_ok=True)
    fr.write_to_csv(file_path='innerfidelity/data/HiFiMAN HE400S with Focus Pads/HiFiMAN HE400S with Focus Pads ORIG.csv')
    fr.equalize(max_gain=12, smoothen=True, window_size=1 / 5, bass_target=4)
    fr.write_to_csv(file_path='innerfidelity/data/HiFiMAN HE400S with Focus Pads/HiFiMAN HE400S with Focus Pads.csv')
    fig, ax = fr.plot_graph(show=False, file_path='innerfidelity/data/HiFiMAN HE400S with Focus Pads/HiFiMAN HE400S with Focus Pads.png')
    plt.close(fig)
    fr.write_eqapo_graphic_eq('innerfidelity/data/HiFiMAN HE400S with Focus Pads/HiFiMAN HE400S with Focus Pads EqAPO.txt')
    def limited_delta(cls, x, y, limit):
        peak_finder = cls(name='peak_finder', frequency=x, raw=y)
        peak_finder.smoothen_fractional_octave(window_size=1 / 12,
                                               treble_window_size=1 / 3,
                                               treble_f_lower=9000,
                                               treble_f_upper=11000)
        peaks = scipy.signal.find_peaks(-peak_finder.smoothed)[0]
        forward_start = peaks[0]
        backward_start = len(x) - peaks[-1] - 1

        limited_forward = cls.limited_forward_delta(x,
                                                    y,
                                                    limit,
                                                    start_index=forward_start)
        limited_backward = cls.limited_forward_delta(
            np.flip(x), np.flip(y), -limit, start_index=backward_start)
        limited_backward = np.flip(limited_backward)

        _fr = FrequencyResponse(name='limiter',
                                frequency=x.copy(),
                                raw=np.min(np.vstack(
                                    [limited_forward, limited_backward]),
                                           axis=0))
        _fr.smoothen_fractional_octave(window_size=1 / 6,
                                       treble_window_size=1 / 6)
        return _fr.smoothed.copy()
    def parse_image(im, model):
        """Parses graph image downloaded from innerfidelity.com"""
        # Crop by left and right edges
        box = (69, 31, 550, 290)
        im = im.crop(box)

        px_a_max = 0
        px_a_min = im.size[1]
        # im.show()

        # X axis
        f_min = 20
        f_max = 20000
        f_step = (f_max / f_min)**(1 / im.size[0])
        f = [f_min]
        for _ in range(1, im.size[0]):
            f.append(f[-1] * f_step)

        # Y axis
        a_max = 150
        a_min = 66
        a_res = (a_max - a_min) / (px_a_min - px_a_max)

        # Try blue curve
        _im = im.copy()
        inspection = _im.load()
        amplitude, _im, _inspection = ReferenceAudioAnalyzerCrawler.find_curve(
            _im, inspection, 203, 206, 0.8, 1.0, a_max, a_res)
        if len([x for x in amplitude if x is None]) >= 0.5 * len(amplitude):
            # More than half of the pixels were discarded, try green curve
            _im = im.copy()
            inspection = _im.load()
            amplitude, _im, _inspection = ReferenceAudioAnalyzerCrawler.find_curve(
                _im, inspection, 119, 121, 0.8, 1.0, a_max, a_res)

        # Inspection image
        draw = ImageDraw.Draw(_im)
        x0 = np.log(30 / f_min) / np.log(f_step)
        x1 = np.log(10000 / f_min) / np.log(f_step)
        y_0 = px_a_max + 12 / a_res
        y_1 = px_a_min - 12 / a_res
        draw.rectangle(((x0, y_0), (x1, y_1)), outline='magenta')
        draw.rectangle(((x0 + 1, y_0 + 1), (x1 - 1, y_1 - 1)),
                       outline='magenta')

        # Create frequency response
        fr = FrequencyResponse(model, f, amplitude)
        fr.interpolate()
        if len(fr.frequency) < 2:
            im.show()
            raise ValueError(f'Failed to parse image for {fr.name}')
        fr.smoothen_fractional_octave(window_size=1 / 3,
                                      treble_window_size=1 / 3)
        fr.raw = fr.smoothed.copy()
        fr.smoothed = np.array([])
        fr.center()

        return fr, _im
Ejemplo n.º 4
0
def main():
    fig, ax = plt.subplots()
    diffs = []
    # Calculate differences for all models
    for file in glob(os.path.join('compensation', 'compensated', '**',
                                  '*.csv'),
                     recursive=True):
        file = os.path.abspath(file)
        comp = FrequencyResponse.read_from_csv(file)
        comp.interpolate()
        comp.center()
        raw_data_path = file.replace('compensated', 'raw')
        raw = FrequencyResponse.read_from_csv(raw_data_path)
        raw.interpolate()
        raw.center()
        diff = FrequencyResponse(name=comp.name,
                                 frequency=comp.frequency,
                                 raw=raw.raw - comp.raw)
        plt.plot(diff.frequency, diff.raw)
        diffs.append(diff.raw)

    # Average and smoothen difference
    f = FrequencyResponse.generate_frequencies()
    diffs = np.vstack(diffs)
    diff = np.mean(diffs, axis=0)
    diff = FrequencyResponse(name='Headphone.com Compensation',
                             frequency=f,
                             raw=diff)
    diff.smoothen_fractional_octave(window_size=1 / 9, iterations=10)
    diff.raw = diff.smoothed
    diff.smoothed = np.array([])

    plt.xlabel('Frequency (Hz)')
    plt.semilogx()
    plt.xlim([20, 20000])
    plt.ylabel('Amplitude (dBr)')
    plt.ylim([-15, 15])
    plt.grid(which='major')
    plt.grid(which='minor')
    plt.title('Headphone.com Compensation Function')
    ax.xaxis.set_major_formatter(ticker.StrMethodFormatter('{x:.0f}'))
    plt.show()

    diff.write_to_csv('headphonecom_compensation.csv')
    diff.plot_graph(show=True,
                    f_min=10,
                    f_max=20000,
                    file_path='headphonecom_compensation.png')
Ejemplo n.º 5
0
def main():
    harman_onear = FrequencyResponse.read_from_csv(
        os.path.join(ROOT_DIR, 'compensation', 'harman_over-ear_2018.csv'))
    harman_onear_wo_bass = FrequencyResponse.read_from_csv(
        os.path.join(ROOT_DIR, 'compensation',
                     'harman_over-ear_2018_wo_bass.csv'))
    harman_inear = FrequencyResponse.read_from_csv(
        os.path.join(ROOT_DIR, 'compensation', 'harman_in-ear_2019v2.csv'))
    harman_inear_wo_bass = FrequencyResponse.read_from_csv(
        os.path.join(ROOT_DIR, 'compensation',
                     'harman_in-ear_2019v2_wo_bass.csv'))

    oratory1990_onear = get_measurements(
        os.path.join(MEASUREMENTS, 'oratory1990', 'data', 'onear'))
    oratory1990_inear = get_measurements(
        os.path.join(MEASUREMENTS, 'oratory1990', 'data', 'inear'))

    crinacle_inear = get_measurements(
        os.path.join(MEASUREMENTS, 'crinacle', 'data', 'inear'))
    inear_ref = oratory1990_inear.copy()
    inear_names = [fr.name for fr in inear_ref]
    for fr in crinacle_inear:
        if fr.name not in inear_names:
            inear_ref.append(fr)

    dbs = [
        ('crinacle_harman_in-ear_2019v2_wo_bass', crinacle_inear,
         oratory1990_inear, None),
        ('crinacle_ears-711_harman_over-ear_2018_wo_bass',
         get_measurements(
             os.path.join(MEASUREMENTS, 'crinacle', 'data', 'onear',
                          'Ears-711')), oratory1990_onear, None),
        ('headphonecom_harman_over-ear_2018_wo_bass',
         get_measurements(
             os.path.join(MEASUREMENTS, 'headphonecom', 'data',
                          'onear')), oratory1990_onear,
         FrequencyResponse.read_from_csv(
             os.path.join(MEASUREMENTS, 'headphonecom', 'resources',
                          'headphonecom_compensation_sbaf-serious.csv'))),
        ('headphonecom_harman_in-ear_2019v2_wo_bass',
         get_measurements(
             os.path.join(MEASUREMENTS, 'headphonecom', 'data',
                          'inear')), inear_ref,
         FrequencyResponse.read_from_csv(
             os.path.join(MEASUREMENTS, 'headphonecom', 'resources',
                          'headphonecom_compensation_sbaf-serious.csv'))),
        ('innerfidelity_harman_over-ear_2018_wo_bass',
         get_measurements(
             os.path.join(MEASUREMENTS, 'innerfidelity', 'data',
                          'onear')), oratory1990_onear,
         FrequencyResponse.read_from_csv(
             os.path.join(MEASUREMENTS, 'innerfidelity', 'resources',
                          'innerfidelity_compensation_sbaf-serious.csv'))),
        ('innerfidelity_harman_in-ear_2019v2_wo_bass',
         get_measurements(
             os.path.join(MEASUREMENTS, 'innerfidelity', 'data',
                          'inear')), inear_ref,
         FrequencyResponse.read_from_csv(
             os.path.join(MEASUREMENTS, 'innerfidelity', 'resources',
                          'innerfidelity_compensation_sbaf-serious.csv'))),
        ('referenceaudioanalyzer_hdm-x_harman_over-ear_2018_wo_bass',
         get_measurements(
             os.path.join(MEASUREMENTS, 'referenceaudioanalyzer', 'data',
                          'onear', 'HDM-X')), oratory1990_onear, None),
        ('referenceaudioanalyzer_hdm1_harman_over-ear_2018_wo_bass',
         get_measurements(
             os.path.join(MEASUREMENTS, 'referenceaudioanalyzer', 'data',
                          'onear', 'HDM1')), oratory1990_onear, None),
        ('referenceaudioanalyzer_siec_harman_in-ear_2019v2_wo_bass',
         get_measurements(
             os.path.join(MEASUREMENTS, 'referenceaudioanalyzer', 'data',
                          'inear', 'SIEC')), inear_ref, None),
        ('rtings_harman_over-ear_2018_wo_bass',
         get_measurements(os.path.join(MEASUREMENTS, 'rtings', 'data',
                                       'onear')), oratory1990_onear,
         FrequencyResponse.read_from_csv(
             os.path.join(MEASUREMENTS, 'rtings', 'resources',
                          'rtings_compensation_avg.csv'))),
        ('rtings_harman_in-ear_2019v2_wo_bass',
         get_measurements(os.path.join(MEASUREMENTS, 'rtings', 'data',
                                       'inear')), inear_ref,
         FrequencyResponse.read_from_csv(
             os.path.join(MEASUREMENTS, 'rtings', 'resources',
                          'rtings_compensation_avg.csv'))),
        ('crinacle_gras_43ag-7_harman_over-ear_2018_wo_bass',
         get_measurements(
             os.path.join(MEASUREMENTS, 'crinacle', 'data', 'onear',
                          'GRAS 43AG-7')), oratory1990_onear, None)
    ]

    stds = []
    for name, measurements, ref, original_target in dbs:
        print(f'Calibrating {name}...')
        # Find matching pairs
        pairs = []
        for fr in measurements:
            for candidate in ref:
                if fr.name.lower() == candidate.name.lower():
                    pairs.append((fr, candidate))

        fig, axs = plt.subplots(1, 3)
        fig.set_size_inches(30, 8)
        fig.suptitle(name)
        description = 'Calibrated against reference measurements with headphones: '
        line_len = len(description)
        for fr, _ in pairs:
            if line_len > 240:
                description += '\n'
                line_len = 0
            description += f'{fr.name}, '
            line_len += len(fr.name) + 2
        description = description[:-2]
        fig.text(0.5, -0.05, description, ha='center')

        # Individual errors
        errors = []
        i = 0
        for fr, target in pairs:
            fr.compensate(target, min_mean_error=True)
            errors.append(fr.error)
            fr.raw = fr.error.copy()
            fr.error = []
            fr.target = []
            fr.plot_graph(fig=fig,
                          ax=axs[0],
                          show=False,
                          raw_plot_kwargs={
                              'color': 'C0',
                              'alpha': 0.3
                          })
            i += 1
        axs[0].set_ylim([-15, 15])
        axs[0].set_title('Individual Errors')
        axs[0].legend(['Error'])

        # Mean and standard deviation
        errors = np.vstack(errors)
        mean = np.mean(errors, axis=0)
        std = np.std(errors, axis=0)
        stds.append(FrequencyResponse(name=name, raw=std))
        fr = FrequencyResponse(name='Mean and Standard Deviation')
        fr.raw = mean
        fr.smoothen_fractional_octave(window_size=1 / 3,
                                      treble_window_size=1 / 3)
        fr.raw = fr.smoothed.copy()
        fr.smoothed = []
        fr.plot_graph(fig=fig, ax=axs[1], color='C0', show=False)
        axs[1].fill_between(fr.frequency,
                            mean - std,
                            mean + std,
                            facecolor='#c1dff5')
        axs[1].set_ylim([-15, 15])
        axs[1].legend(['Mean', 'STD'])

        # Target curves
        ref_target = harman_onear_wo_bass if 'over-ear' in name else harman_inear_wo_bass
        ref_target.plot_graph(fig=fig, ax=axs[2], show=False, color='C0')
        target = ref_target.copy()
        target.name = name
        target.raw += fr.raw
        target.plot_graph(fig=fig, ax=axs[2], show=False, color='C1')
        if original_target is not None:
            original_target.plot_graph(fig=fig,
                                       ax=axs[2],
                                       show=False,
                                       color='C2')
            axs[2].legend([ref_target.name, target.name, original_target.name])
        else:
            axs[2].legend([ref_target.name, target.name])
        axs[2].set_title(f'{name} target')
        axs[2].set_ylim([-15, 15])

        fig.savefig(os.path.join(DIR_PATH, f'calibration_{name}.png'),
                    bbox_inches='tight')

        target.plot_graph(show=False,
                          file_path=os.path.join(DIR_PATH, f'{name}.png'),
                          color='C0')
        target.write_to_csv(file_path=os.path.join(DIR_PATH, f'{name}.csv'))
        plt.close(fig)

    fig, axs = plt.subplots(1, 2)
    fig.set_size_inches(20, 8)
    onear_labels = []
    inear_labels = []
    for fr in stds:
        if 'over-ear' in fr.name:
            fr.plot_graph(fig=fig,
                          ax=axs[0],
                          color=f'C{len(onear_labels)}',
                          show=False)
            onear_labels.append(fr.name)
        else:
            fr.plot_graph(fig=fig,
                          ax=axs[1],
                          color=f'C{len(inear_labels)}',
                          show=False)
            inear_labels.append(fr.name)
    axs[0].legend(onear_labels)
    axs[1].legend(inear_labels)
    axs[0].set_title('On-ear')
    axs[1].set_title('In-ear')
    axs[0].set_ylim([0, 8])
    axs[1].set_ylim([0, 8])
    fig.savefig(os.path.join(DIR_PATH, 'STDs.png'))
    plt.close(fig)
    def equalize(self,
                 max_gain=DEFAULT_MAX_GAIN,
                 limit=18,
                 limit_decay=0.0,
                 concha_interference=False,
                 window_size=1 / 12,
                 treble_window_size=2,
                 treble_f_lower=DEFAULT_TREBLE_F_LOWER,
                 treble_f_upper=DEFAULT_TREBLE_F_UPPER):
        """Creates equalization curve and equalized curve.

        Args:
            max_gain: Maximum positive gain in dB
            limit: Maximum slope in dB per octave
            limit_decay: Decay coefficient (per octave) for the limit. Value of 0.5 would reduce limit by 50% in an octave
                when traversing a single limitation zone.
            concha_interference: Do measurements include concha interference which produced a narrow dip around 9 kHz?
            window_size: Smoothing window size in octaves.
            treble_window_size: Smoothing window size in octaves in the treble region.
            treble_f_lower: Lower boundary of transition frequency region. In the transition region normal filter is \
                            switched to treble filter with sigmoid weighting function.
            treble_f_upper: Upper boundary of transition frequency reqion. In the transition region normal filter is \
                            switched to treble filter with sigmoid weighting function.

        Returns:

        """
        fr = FrequencyResponse(name='fr',
                               frequency=self.frequency,
                               raw=self.error)
        # Smoothen data heavily in the treble region to avoid problems caused by peakiness
        fr.smoothen_fractional_octave(window_size=window_size,
                                      treble_window_size=treble_window_size,
                                      treble_f_lower=treble_f_lower,
                                      treble_f_upper=treble_f_upper)

        # Copy data
        x = np.array(fr.frequency)
        y = np.array(-fr.smoothed)  # Inverse of the smoothed error

        # Find peaks and notches
        peak_inds, peak_props = find_peaks(y, prominence=1)
        dip_inds, dip_props = find_peaks(-y, prominence=1)

        if not len(peak_inds) and not len(dip_inds):
            self.equalization = y
            # Equalized
            self.equalized_raw = self.raw + self.equalization
            if len(self.smoothed):
                self.equalized_smoothed = self.smoothed + self.equalization
            return y, fr.smoothed.copy(), np.array([]), np.array([False] * len(y)), np.array([]),\
                np.array([False] * len(y)), np.array([]), np.array([]), len(y) - 1, np.array([False] * len(y))

        else:
            limit_free_mask = self.protection_mask(y, peak_inds, dip_inds)
            if concha_interference:
                # 8 kHz - 11.5 kHz should not be limit free zone
                limit_free_mask[np.logical_and(x >= 8000, x <= 11500)] = False

            # Find rtl start index
            rtl_start = self.find_rtl_start(y, peak_inds, dip_inds)

            # Find ltr and rtl limitations
            # limited_ltr is y but with slopes limited when traversing left to right
            # clipped_ltr is boolean mask for limited samples when traversing left to right
            # limited_rtl is found using ltr algorithm but with flipped data
            limited_ltr, clipped_ltr, regions_ltr = self.limited_ltr_slope(
                x,
                y,
                limit,
                limit_decay=limit_decay,
                start_index=0,
                peak_inds=peak_inds,
                limit_free_mask=limit_free_mask,
                concha_interference=concha_interference)
            limited_rtl, clipped_rtl, regions_rtl = self.limited_rtl_slope(
                x,
                y,
                limit,
                limit_decay=limit_decay,
                start_index=rtl_start,
                peak_inds=peak_inds,
                limit_free_mask=limit_free_mask,
                concha_interference=concha_interference)

            # ltr and rtl limited curves are combined with min function
            combined = self.__class__(name='limiter',
                                      frequency=x,
                                      raw=np.min(np.vstack(
                                          [limited_ltr, limited_rtl]),
                                                 axis=0))
            # Gain can be reduced in the treble region
            # Clip positive gain to max gain
            combined.raw = np.min(np.vstack(
                [combined.raw,
                 np.ones(combined.raw.shape) * max_gain]),
                                  axis=0)
            # Smoothen the curve to get rid of hard kinks
            combined.smoothen_fractional_octave(window_size=1 / 5,
                                                treble_window_size=1 / 5)

            # TODO: Fix trend by comparing super heavy smoothed equalizer frequency responses: limited vs unlimited

            # Equalization curve
            self.equalization = combined.smoothed

        # Equalized
        self.equalized_raw = self.raw + self.equalization
        if len(self.smoothed):
            self.equalized_smoothed = self.smoothed + self.equalization

        return combined.smoothed.copy(), fr.smoothed.copy(), limited_ltr, clipped_ltr, limited_rtl,\
            clipped_rtl, peak_inds, dip_inds, rtl_start, limit_free_mask
Ejemplo n.º 7
0
def main():
    # Filenames
    if_files = list(
        glob(os.path.join('innerfidelity', 'data', '**', '*.csv'),
             recursive=True))
    if_file_names = [os.path.split(os.path.abspath(f))[-1] for f in if_files]
    normalized_if_files = [normalize(s) for s in if_file_names]
    hp_files = list(
        glob(os.path.join('rtings', 'data', '**', '*.csv'), recursive=True))

    # Find matching files
    matching_if_files = []
    matching_hp_files = []
    for hp_file in hp_files:
        file_name = os.path.split(os.path.abspath(hp_file))[-1]
        for i in range(len(normalized_if_files)):
            if normalized_if_files[i] == normalize(file_name):
                matching_hp_files.append(hp_file)
                matching_if_files.append(if_files[i])

    # Write mathces to file for manual inspection
    df = pd.DataFrame(
        np.array([matching_hp_files, matching_if_files]).transpose())
    df.to_csv('matches.csv', index=False, header=False)

    fig, ax = plt.subplots()
    diffs = []
    # Calculate differences for all models
    if_compensation = FrequencyResponse.read_from_csv(
        os.path.join('innerfidelity', 'resources',
                     'innerfidelity_compensation_2017.csv'))
    if_compensation.interpolate()
    hp_compensation = FrequencyResponse.read_from_csv(
        os.path.join('rtings', 'resources', 'rtings_compensation.csv'))
    hp_compensation.interpolate()
    for i in range(len(matching_if_files)):
        if_fr = FrequencyResponse.read_from_csv(matching_if_files[i])
        if_fr.interpolate()
        if_fr.center()
        #if_fr.compensate(if_compensation)
        hp_fr = FrequencyResponse.read_from_csv(matching_hp_files[i])
        hp_fr.interpolate()
        hp_fr.center()
        #hp_fr.compensate(hp_compensation)
        #diff = FrequencyResponse(name=if_fr.name, frequency=if_fr.frequency, raw=hp_fr.error - if_fr.error)
        diff = FrequencyResponse(name=if_fr.name,
                                 frequency=if_fr.frequency,
                                 raw=hp_fr.raw - if_fr.raw)
        plt.plot(diff.frequency, diff.raw)
        diffs.append(diff.raw)

    # Average and smoothen difference
    f = FrequencyResponse.generate_frequencies()
    diffs = np.vstack(diffs)
    diff = np.mean(diffs, axis=0)
    std = np.std(diffs, axis=0)
    diff = FrequencyResponse(name='Rtings Raw to Innerfidelity Raw',
                             frequency=f,
                             raw=diff)
    #diff.smoothen(window_size=1/7, iterations=10)
    diff.smoothen_fractional_octave(window_size=1 / 5, iterations=100)
    diff.raw = diff.smoothed
    diff.smoothed = np.array([])

    plt.xlabel('Frequency (Hz)')
    plt.semilogx()
    plt.xlim([20, 20000])
    plt.ylabel('Amplitude (dBr)')
    plt.ylim([-15, 15])
    plt.grid(which='major')
    plt.grid(which='minor')
    plt.title('Rtings Raw to Innerfidelity Raw')
    ax.xaxis.set_major_formatter(ticker.StrMethodFormatter('{x:.0f}'))
    plt.show()

    fig, ax = diff.plot_graph(f_min=20, f_max=20000, show=False, color=None)
    ax.fill_between(diff.frequency,
                    diff.raw + std,
                    diff.raw - std,
                    facecolor='lightblue')
    plt.legend(['Rtings Raw to Innerfidelity Raw', 'Standard Deviation'])
    plt.ylim([-10, 10])
    fig.savefig(os.path.join('calibration', 'rtings_to_innerfidelity.png'),
                dpi=240)
    plt.show()
    diff.write_to_csv(
        os.path.join('calibration', 'rtings_to_innerfidelity.csv'))

    diff.raw *= -1
    diff.name = 'Innerfidelity Raw to Rtings Raw'
    fig, ax = diff.plot_graph(f_min=20, f_max=20000, show=False, color=None)
    ax.fill_between(diff.frequency,
                    diff.raw + std,
                    diff.raw - std,
                    facecolor='lightblue')
    plt.legend(['Innerfidelity Raw to Rtings Raw', 'Standard Deviation'])
    plt.ylim([-10, 10])
    fig.savefig(os.path.join('calibration', 'innerfidelity_to_rtings.png'),
                dpi=240)
    plt.show()
    diff.write_to_csv(
        os.path.join('calibration', 'innerfidelity_to_rtings.csv'))
Ejemplo n.º 8
0
def limited_slope(x, y, limit, limit_decay=0.0):
    """Bi-directional slope limitation for a frequency response curve

    Args:
        x: frequencies
        y: amplitudes
        limit: Maximum slope in dB per octave
        limit_decay: Decay coefficient (per octave) for the limit. Value of 0.5 would reduce limit by 50% in an octave
            when traversing a single limitation zone.

    Returns:

    """
    fr = FrequencyResponse(name='fr', frequency=x, raw=y)
    # Smoothen data, heavily on treble to avoid problems in +10 kHz region
    fr.smoothen_fractional_octave(window_size=1 / 12,
                                  treble_window_size=2,
                                  treble_f_lower=9000,
                                  treble_f_upper=11500)

    # Copy data
    x = fr.frequency.copy()
    y = fr.smoothed.copy()

    # Find peaks and notches
    # TODO: these affect which regions are rejected
    peak_inds, peak_props = scipy.signal.find_peaks(y, prominence=1)
    dip_inds, dip_props = scipy.signal.find_peaks(-y, prominence=1)

    limit_free_mask = protection_mask(y, dip_inds)

    # Find backward start index
    backward_start = find_backward_start(y, peak_inds,
                                         dip_inds)  # TODO: backward start

    # Find forward and backward limitations
    # limited_forward is y but with slopes limited when traversing left to right
    # clipped_forward is boolean mask for limited samples when traversing left to right
    # limited_backward is found using forward algorithm but with flipped data
    limited_forward, clipped_forward, regions_forward = limited_forward_slope(
        x,
        y,
        limit,
        limit_decay=limit_decay,
        start_index=0,
        peak_inds=peak_inds,
        limit_free_mask=limit_free_mask)
    limited_backward, clipped_backward, regions_backward = limited_backward_slope(
        x,
        y,
        limit,
        limit_decay=limit_decay,
        start_index=backward_start,
        peak_inds=peak_inds,
        limit_free_mask=limit_free_mask)

    # TODO: Find notches which are lower in level than adjacent notches
    # TODO: Set detected notches as slope clipping free zones up to levels of adjacent notches

    # Forward and backward limited curves are combined with min function
    # Combination function is smoothed to get rid of hard kinks
    limiter = FrequencyResponse(name='limiter',
                                frequency=x.copy(),
                                raw=np.min(np.vstack(
                                    [limited_forward, limited_backward]),
                                           axis=0))
    limiter.smoothen_fractional_octave(window_size=1 / 5,
                                       treble_window_size=1 / 5)
    #limiter.smoothed = limiter.raw.copy()

    return limiter.smoothed.copy(), fr.smoothed.copy(), limited_forward, clipped_forward, limited_backward, clipped_backward, \
        peak_inds, dip_inds, backward_start, limit_free_mask
Ejemplo n.º 9
0
def limited_slope_plots(fr, limit):
    # Smoothen data, heavily on treble to avoid problems in +10 kHz region
    fr.smoothen_fractional_octave(window_size=1 / 12,
                                  treble_window_size=2,
                                  treble_f_lower=9000,
                                  treble_f_upper=12000)

    # Copy data
    x = fr.frequency.copy()
    y = fr.smoothed.copy()

    # Find peaks and notches
    peak_inds, peak_props = scipy.signal.find_peaks(
        y, prominence=1)  # TODO: this affects which regions are rejected
    notch_inds, notch_props = scipy.signal.find_peaks(
        -y, prominence=1)  # TODO: this affects which regions are protected

    # Find backward start index
    backward_start = find_backward_start(y, peak_inds,
                                         notch_inds)  # TODO: backward start
    #backward_start = 0

    # Find forward and backward limitations
    # limited_forward is y but with slopes limited when traversing left to right
    # clipped_forward is boolean mask for limited samples when traversing left to right
    # limited_backward is found using forward algorithm but with flipped data
    limited_forward, clipped_forward, regions_forward = limited_forward_slope(
        x, y, limit, start_index=0, peak_inds=peak_inds)
    limited_backward, clipped_backward, regions_backward = limited_backward_slope(
        x, y, limit, start_index=backward_start, peak_inds=peak_inds)

    # TODO: Find notches which have backward region on left side, forward region on right side and are lower in level than adjacent notches

    # TODO: Set detected notches as slope clipping free zones up to levels of adjacent notches

    # Forward and backward limited curves are combined with min function
    # Combination function is smoothed to get rid of hard kinks
    limiter = FrequencyResponse(name='limiter',
                                frequency=x.copy(),
                                raw=np.min(np.vstack(
                                    [limited_forward, limited_backward]),
                                           axis=0))
    limiter.smoothen_fractional_octave(window_size=1 / 3,
                                       treble_window_size=1 / 3)
    limited = limiter.smoothed

    # Plot graphs
    fig, ax = fr.plot_graph(show=False,
                            raw_plot_kwargs={'color': 'C2'},
                            smoothed_plot_kwargs={
                                'color': 'C2',
                                'linewidth': 1,
                                'linestyle': 'dashed'
                            })
    fig.set_size_inches(20, 9)
    ax.plot(x, limited, label='Limited', color='C1')
    ax.fill_between(x,
                    clipped_forward * -5,
                    clipped_forward * 10,
                    label='Clipped left to right',
                    color='blue',
                    alpha=0.1)
    ax.fill_between(x,
                    clipped_backward * -10,
                    clipped_backward * 5,
                    label='Clipped right to left',
                    color='red',
                    alpha=0.1)
    ax.scatter(x[peak_inds], y[peak_inds], color='red')
    ax.scatter(x[notch_inds], y[notch_inds], color='blue')
    ax.scatter(x[backward_start],
               y[backward_start],
               300,
               marker='X',
               label='Backward start',
               color='magenta',
               alpha=0.4)
    ax.legend()
    ax.set_xlim([300, 20000])

    return fig, ax