Пример #1
0
def test_interval():
    """Tests if the interval functionality of the ExponentialSweep class works"""
    sweep1 = sumpf.ExponentialSweep(interval=(200, 800), length=2**10)
    sweep2 = sumpf.ExponentialSweep(length=600)
    assert sweep1[:, 200:800].channels() == pytest.approx(sweep2.channels())
    sweep3 = sumpf.ExponentialSweep(interval=(0.1, -0.1), length=2**10)
    sweep4 = sumpf.ExponentialSweep(
        length=int(round(2**10 * 0.9) - round(2**10 * 0.1)))
    assert sweep3[:, 0.1:-0.1].channels() == pytest.approx(sweep4.channels())
Пример #2
0
def test_ports_and_connections_when_deactivated():
    """tests the creation of connections and ports while the client is deactivated."""
    with _create_client() as client:
        client.inports.register("input")
        client.outports.register("output")
        # test creating and connecting ports, when the client is deactivated
        signal1 = sumpf.MergeSignals([
            sumpf.SineWave(length=2**12),
            sumpf.HannWindow(length=2**12),
            sumpf.ExponentialSweep(length=2**12)
        ]).output()
        xruns = _XRunHandler()
        sumpf_jack = sumpf.Jack(name="port_creation", input_signal=signal1)
        sumpf_jack.xruns.connect(xruns.xrun)
        sumpf_jack.add_input_port("index1")
        sumpf_jack.add_input_port("shortname")
        sumpf_jack.add_input_port("name")
        sumpf_jack.add_input_port("index2")
        sumpf_jack.connect(0, 0)
        sumpf_jack.connect("Hann window", "shortname")
        sumpf_jack.connect("port_creation:Sweep", "port_creation:name")
        sumpf_jack.connect(0, "Testclient:input")
        sumpf_jack.connect("Testclient:output", 3)
        sumpf_jack.start()
        reference = sumpf.MergeSignals([
            signal1, signal1[0, 0:-client.blocksize].shift(client.blocksize)
        ]).output()
        assert sumpf_jack.output().channels() == pytest.approx(
            reference.channels())
        # test that the connections remain established, when the output ports change
        signal2 = sumpf.MergeSignals([
            sumpf.BartlettWindow(length=2**12),
            sumpf.ExponentialSweep(length=2**12),
            sumpf.SineWave(length=2**12)
        ]).output()
        sumpf_jack.input(signal2)
        sumpf_jack.start()
        reference = sumpf.MergeSignals([
            signal2, signal2[0, 0:-client.blocksize].shift(client.blocksize)
        ]).output()
        assert sumpf_jack.output().channels() == pytest.approx(
            reference.channels())
        # test that the connections are broken, when the input ports are changed
        sumpf_jack.remove_input_port("index1")
        sumpf_jack.remove_input_port("shortname")
        sumpf_jack.remove_input_port("name")
        sumpf_jack.remove_input_port("index2")
        sumpf_jack.add_input_port("a")
        sumpf_jack.add_input_port("b")
        sumpf_jack.add_input_port("c")
        sumpf_jack.add_input_port("d")
        sumpf_jack.start()
        assert (sumpf_jack.output().channels() == numpy.zeros(
            shape=sumpf_jack.output().shape())).all()
Пример #3
0
def test_output_ports():
    """Tests if the output ports of the :class:`~sumpf.Jack` instances are created
    from the labels of their input signal.
    """
    with _create_client() as client:
        xruns = _XRunHandler()
        jack = sumpf.Jack("CUT")  # Client Under Test
        jack.xruns.connect(xruns.xrun)
        # check the output port for the empty default input signal
        assert ["CUT:output_1"] == [
            p.name for p in client.get_ports(is_output=True)
            if p.name.startswith("CUT:")
        ]
        # check adding and renaming output ports
        jack.input(
            sumpf.MergeSignals([sumpf.BetaNoise(),
                                sumpf.SquareWave()]).output())
        assert ["CUT:Beta noise", "CUT:Square wave"] == [p.name for p in client.get_ports(is_output=True) if p.name.startswith("CUT:")]  # pylint: disable=line-too-long
        # check removing and renaming output ports
        jack.input(sumpf.ExponentialSweep())
        assert ["CUT:Sweep"] == [
            p.name for p in client.get_ports(is_output=True)
            if p.name.startswith("CUT:")
        ]
        # check generating port names from a signal with crooked labels
        jack.input(
            sumpf.Signal(channels=numpy.eye(3), labels=(None, "output_1"))
        )  # one label None, one label already exists as port name and one label is missing
        assert ["CUT:output_1", "CUT:output_2", "CUT:output_3"] == [p.name for p in client.get_ports(is_output=True) if p.name.startswith("CUT:")]  # pylint: disable=line-too-long
        assert xruns.xruns == []
Пример #4
0
def test_autodetect_format_on_reading():
    """Tests if auto-detecting the file format, when reading a file works."""
    signal = sumpf.ExponentialSweep() * 0.9
    with tempfile.TemporaryDirectory() as d:
        for file_format in sumpf.Signal.file_formats:
            if file_format != sumpf.Signal.file_formats.AUTO:
                path = os.path.join(d, "test_file")
                assert not os.path.exists(path)
                try:
                    signal.save(path, file_format)
                except ValueError:  # no writer for the given file found
                    pass
                else:
                    loaded = sumpf.Signal.load(path)
                    assert loaded.shape() == signal.shape()
                    os.remove(path)
Пример #5
0
def test_min_and_max_frequencies(start_frequency, stop_frequency,
                                 sampling_rate, length, interval_start,
                                 interval_stop):
    """Tests if the methods for computing the minimum and the maximum frequencies work as expected"""
    interval = (interval_start, interval_stop)
    start, stop = sumpf_internal.index(interval, length)
    frequency_ratio = stop_frequency / start_frequency
    sweep_length = stop - start
    sweep = sumpf.ExponentialSweep(start_frequency=start_frequency,
                                   stop_frequency=stop_frequency,
                                   interval=interval,
                                   sampling_rate=sampling_rate,
                                   length=length)
    assert sweep.minimum_frequency() == pytest.approx(
        start_frequency * frequency_ratio**(-start / sweep_length))
    assert sweep.maximum_frequency() == pytest.approx(stop_frequency * frequency_ratio**((length - stop) / sweep_length))  # pylint: disable=line-too-long; nothing complicated here, only long variable names
Пример #6
0
def test_instantaneous_frequency(start_frequency, stop_frequency,
                                 sampling_rate, length, interval_start,
                                 interval_stop):
    """Tests the instantaneous_frequency method of the ExponentialSweep class"""
    sweep = sumpf.ExponentialSweep(start_frequency=start_frequency,
                                   stop_frequency=stop_frequency,
                                   interval=(interval_start, interval_stop),
                                   sampling_rate=sampling_rate,
                                   length=length)
    assert sweep.instantaneous_frequency(0.0) == sweep.minimum_frequency()
    assert sweep.instantaneous_frequency(round(interval_start * length) / sampling_rate) == pytest.approx(start_frequency)  # pylint: disable=line-too-long
    assert sweep.instantaneous_frequency(round(interval_stop * length) / sampling_rate) == pytest.approx(stop_frequency)  # pylint: disable=line-too-long
    assert sweep.instantaneous_frequency(
        sweep.duration()) == sweep.maximum_frequency()
    diff = numpy.diff(sweep.instantaneous_frequency(sweep.time_samples()))
    assert (diff > 0).all()  # the frequency should increase monotonically
Пример #7
0
def test_compare_with_other_formula(start_frequency, stop_frequency, sine,
                                    sampling_rate, length):
    """Compares the current implementation with a sweep, that is created by a different but equivalent formula."""
    if sine:
        phase = 0.0
    else:
        phase = math.pi / 2.0
    sweep1 = sumpf.ExponentialSweep(start_frequency=start_frequency,
                                    stop_frequency=stop_frequency,
                                    phase=phase,
                                    sampling_rate=sampling_rate,
                                    length=length)
    sweep2 = other_sweep_formula(start_frequency=start_frequency,
                                 stop_frequency=stop_frequency,
                                 sampling_rate=sampling_rate,
                                 length=length,
                                 sine=sine)
    assert sweep1.channels() == pytest.approx(sweep2.channels(), abs=1e-7)
Пример #8
0
def test_min_and_max_frequencies_inversed(start_frequency, stop_frequency,
                                          sampling_rate, length,
                                          interval_start, interval_stop):
    """Tests if the methods for computing the minimum and the maximum frequencies work as expected"""
    start, stop = sumpf_internal.index((interval_start, interval_stop), length)
    sweep = sumpf.ExponentialSweep(start_frequency=start_frequency,
                                   stop_frequency=stop_frequency,
                                   interval=(start, stop),
                                   sampling_rate=sampling_rate,
                                   length=length)
    isweep = sumpf.InverseExponentialSweep(start_frequency=start_frequency,
                                           stop_frequency=stop_frequency,
                                           interval=(length - stop,
                                                     length - start),
                                           sampling_rate=sampling_rate,
                                           length=length)
    assert isweep.minimum_frequency() == pytest.approx(
        sweep.minimum_frequency())
    assert isweep.maximum_frequency() == pytest.approx(
        sweep.maximum_frequency())
Пример #9
0
def test_convolution_with_sweep():
    """Tests if the convolution with the respective sweep results in a unit impulse"""
    for kwargs in ({
            "start_frequency": 300.0,
            "length": 1024
    }, {
            "stop_frequency": 12747.0,
            "length": 1024
    }, {
            "phase": 2.9,
            "length": 1024
    }, {
            "interval": (0.15, 0.9),
            "length": 2048
    }, {
            "stop_frequency": 5000.0,
            "sampling_rate": 18312.7,
            "length": 1024
    }):
        a, b = kwargs.get("interval", (0, 1.0))
        sweep = sumpf.ExponentialSweep(**kwargs)
        isweep = sumpf.InverseExponentialSweep(**kwargs)
        impulse = sweep[:, a:b].convolve(isweep[:, a:b])
        peak = max(impulse.channels()[0])
        peak_index = impulse.channels()[0].argmax()
        other = max(
            max(abs(impulse.channels()[0, 0:-impulse.offset() - 1])
                ),  # the maximum of the absolute values of the impulse except
            max(abs(impulse.channels()[
                0, -impulse.offset() +
                2:])))  # for the sample at t=0 and the two neighboring samples
        assert abs(peak - 1.0
                   ) < 0.005  # the highest peak should be one (unit impulse)
        assert peak_index == -impulse.offset(
        )  # the highest peak should be at time value 0
        assert peak > 5 * abs(
            other
        )  # other peaks and notches should be much smaller than the impulse
Пример #10
0
def test_autodetect_format_on_saving():
    """Tests if auto-detecting the file format from the file extension, when writing a file works."""
    try:
        import soundfile  # noqa; pylint: disable=unused-import,import-outside-toplevel; this shall raise an ImportError, if the soundfile library cannot be imported
    except ImportError:
        file_formats = [(sumpf.Signal.file_formats.TEXT_CSV, ".csv",
                         signal_readers.CsvReader),
                        (sumpf.Signal.file_formats.TEXT_JSON, ".json",
                         signal_readers.JsonReader),
                        (sumpf.Signal.file_formats.TEXT_JSON, ".js",
                         signal_readers.JsonReader),
                        (sumpf.Signal.file_formats.NUMPY_NPZ, ".npz",
                         signal_readers.NumpyReader),
                        (sumpf.Signal.file_formats.NUMPY_NPY, ".npy",
                         signal_readers.NumpyReader),
                        (sumpf.Signal.file_formats.PYTHON_PICKLE, ".pickle",
                         signal_readers.PickleReader),
                        (sumpf.Signal.file_formats.WAV_INT32, ".wav",
                         signal_readers.WaveReader),
                        (sumpf.Signal.file_formats.AIFF_INT32, ".aiff",
                         signal_readers.AifcReader),
                        (sumpf.Signal.file_formats.AIFF_INT32, ".aifc",
                         signal_readers.AifcReader),
                        (sumpf.Signal.file_formats.AIFF_INT32, ".aif",
                         signal_readers.AifcReader)]
    else:
        file_formats = [(sumpf.Signal.file_formats.TEXT_CSV, ".csv",
                         signal_readers.CsvReader),
                        (sumpf.Signal.file_formats.TEXT_JSON, ".json",
                         signal_readers.JsonReader),
                        (sumpf.Signal.file_formats.TEXT_JSON, ".js",
                         signal_readers.JsonReader),
                        (sumpf.Signal.file_formats.NUMPY_NPZ, ".npz",
                         signal_readers.NumpyReader),
                        (sumpf.Signal.file_formats.NUMPY_NPY, ".npy",
                         signal_readers.NumpyReader),
                        (sumpf.Signal.file_formats.PYTHON_PICKLE, ".pickle",
                         signal_readers.PickleReader),
                        (sumpf.Signal.file_formats.WAV_FLOAT32, ".wav",
                         signal_readers.SoundfileReader),
                        (sumpf.Signal.file_formats.AIFF_FLOAT32, ".aiff",
                         signal_readers.SoundfileReader),
                        (sumpf.Signal.file_formats.AIFF_FLOAT32, ".aifc",
                         signal_readers.SoundfileReader),
                        (sumpf.Signal.file_formats.AIFF_FLOAT32, ".aif",
                         signal_readers.SoundfileReader),
                        (sumpf.Signal.file_formats.FLAC_INT24, ".flac",
                         signal_readers.SoundfileReader),
                        (sumpf.Signal.file_formats.OGG_VORBIS, ".ogg",
                         signal_readers.SoundfileReader),
                        (sumpf.Signal.file_formats.OGG_VORBIS, ".oga",
                         signal_readers.SoundfileReader)]
    signal = sumpf.ExponentialSweep() * 0.9
    with tempfile.TemporaryDirectory() as d:
        for file_format, ending, Reader in file_formats:
            reader = Reader()
            auto_path = os.path.join(d, "test_file" + ending)
            reference_path = os.path.join(d, "test_file")
            assert not os.path.exists(auto_path)
            assert not os.path.exists(reference_path)
            signal.save(auto_path)
            signal.save(reference_path, file_format)
            auto_loaded = sumpf.Signal.load(auto_path)
            reader_loaded = reader(auto_path)
            reference = reader(reference_path)
            assert auto_loaded.shape() == signal.shape()
            assert auto_loaded == reader_loaded
            assert reader_loaded.sampling_rate() == reference.sampling_rate()
            assert reader_loaded.offset() == reference.offset()
            assert (reader_loaded.channels() == reference.channels()).all()
            os.remove(auto_path)
            os.remove(reference_path)
Пример #11
0
def test_harmonic_impulse_response():
    """Does some trivial tests with the harmonic_impulse_response method"""
    # create a sweep, an inverse sweep and a distorted version of the sweep
    sweep = sumpf.ExponentialSweep(start_frequency=20.0,
                                   stop_frequency=7800.0,
                                   sampling_rate=48000,
                                   length=2**14)
    inverse = sumpf.InverseExponentialSweep(start_frequency=20.0, stop_frequency=7800.0, sampling_rate=48000, length=2**14)  # pylint: disable=line-too-long
    distorted = 0.5 * sweep**3 - 0.6 * sweep**2 + 0.1 * sweep + 0.02
    lowpass = sumpf.ButterworthFilter(cutoff_frequency=1000.0,
                                      order=16,
                                      highpass=False)
    highpass = sumpf.ButterworthFilter(cutoff_frequency=1000.0,
                                       order=16,
                                       highpass=True)
    response = distorted * highpass * lowpass
    # test with non-circular deconvolution
    impulse_response = response.convolve(
        inverse, mode=sumpf.Signal.convolution_modes.SPECTRUM_PADDED)
    h1 = sweep.harmonic_impulse_response(impulse_response=impulse_response,
                                         harmonic=1)
    h2 = sweep.harmonic_impulse_response(impulse_response=impulse_response,
                                         harmonic=2,
                                         length=h1.length())
    h3 = sweep.harmonic_impulse_response(impulse_response=impulse_response,
                                         harmonic=3)
    assert h1.length() == h2.length(
    )  # check if the length parameter has worked
    assert h3.length() < h2.length(
    )  # the impulse responses of the higher order harmonics are shorter
    harmonics = sumpf.Merge([h1, h2, h3]).output()
    spectrum = harmonics.fourier_transform()
    magnitude = spectrum.magnitude()
    max_indices = magnitude.argmax(axis=1)
    assert max(max_indices) - min(
        max_indices
    ) <= 1  # the maximums of all harmonics' transfer functions should be roughly the same
    assert max_indices.mean() * spectrum.resolution() == pytest.approx(
        1000.0, rel=1e-3)  # the maximums should be around 1000Hz
    # test with circular deconvolution
    impulse_response = response.convolve(
        inverse, mode=sumpf.Signal.convolution_modes.SPECTRUM).shift(None)
    h1 = sweep.harmonic_impulse_response(impulse_response=impulse_response,
                                         harmonic=1,
                                         length=2048)
    h2 = sweep.harmonic_impulse_response(impulse_response=impulse_response,
                                         harmonic=2,
                                         length=2048)
    h3 = sweep.harmonic_impulse_response(impulse_response=impulse_response,
                                         harmonic=3,
                                         length=2048)
    assert h1.length() == 2048
    assert h2.length() == 2048
    assert h3.length() == 2048
    harmonics = sumpf.Merge([h1, h2, h3]).output()
    spectrum = harmonics.fourier_transform()
    magnitude = spectrum.magnitude()
    max_indices = magnitude.argmax(axis=1)
    assert max(max_indices) - min(
        max_indices
    ) <= 5  # the maximums of all harmonics' transfer functions should be roughly the same
    assert max_indices.mean() * spectrum.resolution() == pytest.approx(
        1000.0, rel=8e-3)  # the maximums should be around 1000Hz