Exemplo n.º 1
0
def test_fft_ifft_split(graph):
    #--------------------------------------------------------------------------------
    # Verify that fft -> ifft returns the same output, with output
    # buffer smaller than FFT size.
    #--------------------------------------------------------------------------------
    buffer_a = Buffer(1, fft_size)
    buffer_b1 = Buffer(1, fft_size // 2)
    buffer_b2 = Buffer(1, fft_size // 2)
    buffer_b3 = Buffer(1, fft_size // 2)

    process_tree(SineOscillator(440), buffer_a)

    fft = FFT(SineOscillator(440), fft_size=fft_size, hop_size=fft_size, do_window=False)
    ifft = IFFT(fft)
    process_tree(ifft, buffer_b1)
    process_tree(ifft, buffer_b2)
    process_tree(ifft, buffer_b3)
    #--------------------------------------------------------------------------------
    # Note that the first buffer will be empty as no output will be
    # generated until 1 fft_size worth of samples has been processed.
    #--------------------------------------------------------------------------------
    buffer_b_concatenate = np.concatenate((buffer_b2.data[0], buffer_b3.data[0]))

    assert np.all(buffer_b1.data[0] == 0)
    assert np.all(np.abs(buffer_a.data[0] - buffer_b_concatenate) < 0.00001)
Exemplo n.º 2
0
def test_fft_windowed(graph):
    #--------------------------------------------------------------------------------
    # Verify that single-hop FFT output matches numpy's fft
    #--------------------------------------------------------------------------------
    buffer_a = Buffer(1, fft_size)
    buffer_b = Buffer(1, num_bins * 2)

    process_tree(SineOscillator(440), buffer_a)

    # Modify to match the symmetry of vDSP FFT
    window = np.hanning(buffer_a.num_frames + 1)[:buffer_a.num_frames]
    windowed = buffer_a.data[0] * window
    spectrum = np.fft.rfft(windowed)
    mags_py = np.abs(spectrum)[:num_bins]
    angles_py = np.angle(spectrum)[:num_bins]

    fft = FFT(SineOscillator(440), fft_size=fft_size, hop_size=fft_size, do_window=True)
    process_tree(fft, buffer_b)

    # Apple vDSP applies a scaling factor of 2x after forward FFT
    mags_out = np.copy(buffer_b.data[0][:num_bins]) / 2
    angles_out = np.copy(buffer_b.data[0][num_bins:])

    # phases are mismatched for some reason
    assert np.all(np.abs(mags_py - mags_out) < 0.0001)
def test_expansion_buffer_reallocation(graph):
    a = SineOscillator([440] * 4)
    assert a.num_output_channels == 4
    assert a.num_output_channels_allocated == 32
    a.set_input("frequency", [440] * 100)
    assert a.num_output_channels == 100
    assert a.num_output_channels_allocated == 100
Exemplo n.º 4
0
def test_buffer_recorder(graph):
    record_buf = Buffer(2, 1024)

    sine = SineOscillator(440)
    recorder = BufferRecorder(record_buf, sine, loop=False)
    assert recorder.num_input_channels == 2
    assert recorder.num_output_channels == 0
    assert recorder.state == SIGNALFLOW_NODE_STATE_ACTIVE
    graph.add_node(recorder)
    graph.render(len(record_buf) + 1)
    assert recorder.state == SIGNALFLOW_NODE_STATE_STOPPED
    sine_rendered = np.sin(
        np.arange(len(record_buf)) * np.pi * 2 * 440 / graph.sample_rate)
    assert list(record_buf.data[0]) == pytest.approx(sine_rendered, abs=0.0001)
    assert list(record_buf.data[1]) == pytest.approx(sine_rendered, abs=0.0001)

    sine2 = SineOscillator(2000)
    recorder2 = BufferRecorder(record_buf, sine2, feedback=0.5, loop=True)
    assert recorder2.num_input_channels == 2
    assert recorder2.num_output_channels == 0
    assert recorder2.state == SIGNALFLOW_NODE_STATE_ACTIVE

    process_tree(recorder2, num_frames=len(record_buf))
    assert recorder2.state == SIGNALFLOW_NODE_STATE_ACTIVE
    sine_rendered2 = 0.5 * sine_rendered + np.sin(
        np.arange(len(record_buf)) * np.pi * 2 * 2000 / graph.sample_rate)
    assert list(record_buf.data[0]) == pytest.approx(sine_rendered2, abs=0.001)

    sine3 = SineOscillator(2000)
    recorder2.set_input("input", sine3)
    process_tree(recorder2, num_frames=len(record_buf))
    assert recorder2.state == SIGNALFLOW_NODE_STATE_ACTIVE
    sine_rendered2 = 0.5 * sine_rendered2 + np.sin(
        np.arange(len(record_buf)) * np.pi * 2 * 2000 / graph.sample_rate)
    assert list(record_buf.data[0]) == pytest.approx(sine_rendered2, abs=0.001)
Exemplo n.º 5
0
def test_graph_render():
    graph = AudioGraph()
    sine = SineOscillator(440)
    graph.play(sine)
    with pytest.raises(RuntimeError):
        graph.render(441000)
    del graph
def test_expansion_channel_mismatch(graph):
    a = SineOscillator([440, 880])
    with pytest.raises(InvalidChannelCountException):
        b = LinearPanner(2, a)
    b = Buffer([1, 2, 3])
    c = BufferPlayer(b)
    with pytest.raises(InvalidChannelCountException):
        c.set_input("rate", [1, 1.5])
Exemplo n.º 7
0
def test_fft_ifft(graph):
    #--------------------------------------------------------------------------------
    # Verify that fft -> ifft returns the same output.
    # Buffer must be at least fft_size or the results will be delayed until fft_size
    # samples are queued.
    #--------------------------------------------------------------------------------
    for buffer_size in [ fft_size, fft_size * 2, fft_size * 4 ]:
        buffer_a = Buffer(1, buffer_size)
        buffer_b = Buffer(1, buffer_size)

        process_tree(SineOscillator(440), buffer_a)

        fft = FFT(SineOscillator(440), fft_size=fft_size, hop_size=fft_size, do_window=False)
        ifft = IFFT(fft)
        process_tree(ifft, buffer_b)

        assert np.all(np.abs(buffer_a.data[0] - buffer_b.data[0]) < 0.000001)
Exemplo n.º 8
0
def test_fft_convolve(graph):
    if no_fft:
        return

    buffer_a = Buffer(1, fft_size)
    process_tree(Impulse(0), buffer_a)

    buffer_b = Buffer(1, fft_size)
    process_tree(SineOscillator(440), buffer_b)

    fft = FFT(SineOscillator(440), fft_size=fft_size, hop_size=fft_size, do_window=False)
    convolve = FFTConvolve(fft, buffer_a)
    ifft = IFFT(convolve) * 0.5
    buffer_c = Buffer(1, fft_size)
    process_tree(ifft, buffer_c)

    assert np.all(np.abs(buffer_b.data[0] - buffer_c.data[0]) < 0.000001)
Exemplo n.º 9
0
def test_patch_free(graph):
    prototype = Patch()
    sine = prototype.add_node(SineOscillator(440))
    envelope = prototype.add_node(EnvelopeASR(0.0, 0.0, 0.01))
    output = sine * envelope
    prototype.set_output(output)
    spec = prototype.create_spec()

    patch = Patch(spec)
    patch.auto_free = True
def test_expansion_channel_array(graph):
    a = SineOscillator(440)
    b = SineOscillator(880)
    c = SineOscillator([1760, 3520])
    d = ChannelArray([a, b, c])
    e = ChannelMixer(1, d)
    assert a.num_input_channels == a.num_output_channels == 1
    assert b.num_input_channels == b.num_output_channels == 1
    assert c.num_input_channels == c.num_output_channels == 2
    assert d.num_input_channels == d.num_output_channels == 4
    assert e.num_input_channels == 4
    assert e.num_output_channels == 1
    buf = Buffer(1, DEFAULT_BUFFER_LENGTH)
    process_tree(e, buffer=buf)
    peak_frequencies = get_peak_frequencies(buf.data[0], graph.sample_rate)
    assert np.all(
        peak_frequencies == pytest.approx([440, 880, 1760, 3520],
                                          abs=(graph.sample_rate /
                                               DEFAULT_BUFFER_LENGTH / 2)))
Exemplo n.º 11
0
def test_graph_dummy_audioout():
    output = AudioOut_Dummy(2)
    graph = AudioGraph(output_device=output)

    sine = SineOscillator([ 220, 440 ])
    graph.play(sine)

    graph.render(1024)
    samples = graph.output.output_buffer[0][:1024]
    assert len(samples) == 1024

    del graph
Exemplo n.º 12
0
def test_nodes_oscillators_sine(graph):
    a = SineOscillator([10, 20])
    process_tree(a, num_frames=N)

    #--------------------------------------------------------------------------------
    # The output of SineOscillator() seems to diverge from np.sin() so this precision is
    # not very high. Should find out why at some point -- probably numerical
    # precision.
    #--------------------------------------------------------------------------------
    expected = np.sin(np.arange(N) * np.pi * 2 * 10 / graph.sample_rate)
    assert list(a.output_buffer[0]) == pytest.approx(expected, abs=0.0001)

    expected = np.sin(np.arange(N) * np.pi * 2 * 20 / graph.sample_rate)
    assert list(a.output_buffer[1]) == pytest.approx(expected, abs=0.0001)
Exemplo n.º 13
0
def test_graph_sample_rate():
    graph = AudioGraph()
    assert graph.sample_rate > 0
    graph.sample_rate = 1000
    assert graph.sample_rate == 1000

    with pytest.raises(Exception):
        graph.sample_rate = 0

    buf = Buffer(1, 1000)
    sine = SineOscillator(10)
    process_tree(sine, buf)
    assert count_zero_crossings(buf.data[0]) == 10

    del graph
Exemplo n.º 14
0
def test_fft(graph):
    #--------------------------------------------------------------------------------
    # Verify that single-hop FFT output matches numpy's fft
    # 1026 = 512 bins plus Nyquist
    #--------------------------------------------------------------------------------
    buffer_a = Buffer(1, fft_size)
    buffer_b = Buffer(1, num_bins * 2)

    process_tree(SineOscillator(440), buffer_a)

    spectrum = np.fft.rfft(buffer_a.data[0])
    mags_py = np.abs(spectrum)[:num_bins]
    angles_py = np.angle(spectrum)[:num_bins]

    fft = FFT(SineOscillator(440), fft_size=fft_size, hop_size=fft_size, do_window=False)
    process_tree(fft, buffer_b)

    # Apple vDSP applies a scaling factor of 2x after forward FFT
    # TODO: Fix this (and remove scaling factor in fftw forward fft)
    mags_out = np.copy(buffer_b.data[0][:num_bins]) / 2
    angles_out = np.copy(buffer_b.data[0][num_bins:])

    assert np.all(np.abs(mags_py - mags_out) < 0.001)
    assert np.all(np.abs(angles_py - angles_out) < 0.001)
def test_expansion_multi(graph):
    """
    When passed an array as an argument, the input is automatically converted
    into a ChannelArray and the output number of channels should be increased.
    """
    a = SineOscillator([0.0, 1.0])
    assert a.num_output_channels == 2
    assert a.num_input_channels == 2

    frequency = a.inputs["frequency"]
    assert frequency.name == "channel-array"
    assert frequency.inputs["input0"].name == "constant"
    assert frequency.inputs["input1"].name == "constant"

    process_tree(a)
    assert np.all(frequency.inputs["input0"].output_buffer[0] == 0.0)
    assert np.all(frequency.inputs["input1"].output_buffer[0] == 1.0)
Exemplo n.º 16
0
def test_fft_convolve_split(graph):
    if no_fft:
        return

    buffer_ir = Buffer(1, fft_size * 4)
    envelope_duration_seconds = buffer_ir.num_frames / graph.sample_rate
    envelope = EnvelopeASR(0, 0, envelope_duration_seconds) * SineOscillator(440)
    process_tree(envelope, buffer_ir)

    fft = FFT(Impulse(0), fft_size=fft_size, hop_size=fft_size, do_window=False)
    convolve = FFTConvolve(fft, buffer_ir)
    ifft = IFFT(convolve) * 0.5

    buffer_out = Buffer(1, fft_size)
    output_samples = np.zeros(0)
    for n in range(4):
        process_tree(ifft, buffer_out)
        output_samples = np.concatenate((output_samples, buffer_out.data[0]))
    assert np.all(np.abs(output_samples - buffer_ir.data[0]) < 0.000001)
def test_expansion_many_channels(graph):
    """
    Generate 100 sine channels, mix down to mono, and confirm that
    the correct 100 frequencies are present.

    By multiplying the Sine output by 0.5, we also test that the
    Multiply nodes re-allocates the output buffers of its inputs
    (the Constant(0.5)) for automatic upmixing by the AudioGraph.
    """
    frequencies = 1000 + np.arange(100) * 100
    a = SineOscillator(frequencies) * 0.5
    mixer = ChannelMixer(1, a)
    graph.render_subgraph(mixer, 2048)
    peak_frequencies = get_peak_frequencies(mixer.output_buffer[0],
                                            graph.sample_rate)
    peak_frequencies_rounded = np.round(peak_frequencies, -2)
    assert np.array_equal(peak_frequencies_rounded, frequencies)
    assert a.num_output_channels_allocated == 100
    assert a.output_buffer.shape == (100, 2048)
def test_expansion_recursive(graph):
    """
    Check that num_output_channels values propagate through the graph.
    """
    a = SineOscillator(440)
    b = BiquadFilter(a)
    buf = WaveShaperBuffer(256)
    buf.fill(lambda n: n**2)
    c = WaveShaper(b, buf)
    d = AllpassDelay(c)
    assert a.num_input_channels == a.num_output_channels == 1
    assert b.num_input_channels == b.num_output_channels == 1
    assert c.num_input_channels == c.num_output_channels == 1
    assert d.num_input_channels == d.num_output_channels == 1
    a.set_input("frequency", [440, 880, 1320])
    assert a.num_input_channels == a.num_output_channels == 3
    assert b.num_input_channels == b.num_output_channels == 3
    assert c.num_input_channels == c.num_output_channels == 3
    assert d.num_input_channels == d.num_output_channels == 3
    a.set_input("frequency", 100)
    assert a.num_input_channels == a.num_output_channels == 1
    assert b.num_input_channels == b.num_output_channels == 1
    assert c.num_input_channels == c.num_output_channels == 1
    assert d.num_input_channels == d.num_output_channels == 1
Exemplo n.º 19
0
def test_node_no_graph():
    with pytest.raises(Exception):
        a = SineOscillator(440)
Exemplo n.º 20
0
def test_node_set_input(graph):
    a = SineOscillator(440)

    #--------------------------------------------------------------------------------
    # Note that, when an input is set to a scalar value and the existing input
    # node is of type Constant, the value of the existing Constant is updated.
    # This means that we can re-use the same `frequency` reference below.
    #--------------------------------------------------------------------------------
    frequency = a.get_input("frequency")
    process_tree(a, num_frames=1024)
    assert frequency.output_buffer[0][0] == 440
    a.set_input("frequency", 880)
    process_tree(a, num_frames=1024)
    assert frequency.output_buffer[0][0] == 880

    a.set_input("frequency", Line(0, 1, 1.0))
    process_tree(a, num_frames=1024)
    line = a.get_input("frequency")
    assert line.output_buffer[0][0] == 0
    assert line.output_buffer[0][1] > 0

    #--------------------------------------------------------------------------------
    # Check that replacing a non-Constant with a Constant works as expected.
    #--------------------------------------------------------------------------------
    a.set_input("frequency", 1760)
    frequency = a.get_input("frequency")
    process_tree(a, num_frames=1024)
    assert frequency.output_buffer[0][0] == 1760
Exemplo n.º 21
0
def test_node_process(graph):
    a = SineOscillator(440)
    a.process(1024)
    assert a.output_buffer.shape == (32, 1024)
Exemplo n.º 22
0
def test_node_add_input(graph):
    a = SineOscillator(440)
    b = SineOscillator(440)
    with pytest.raises(RuntimeError):
        a.add_input(b)
def test_expansion_mono(graph):
    a = SineOscillator(1)
    assert a.num_output_channels == 1
    assert a.num_input_channels == 1