def test_expansion_recursive_processing():
    """
    Check that processing/upmixing propagates through the graph when
    an input's channel count is increased.

    Needs to be run through AudioGraph because simple process_tree
    doesn't do upmixing.
    """
    output = AudioOut_Dummy(2)
    graph = AudioGraph(output_device=output)

    a = Constant(4)
    b = a + 1
    c = b * 2
    d = c - 1
    buf = Buffer(2, 1024)
    graph.play(d)
    graph.render_to_buffer(buf)
    assert np.all(buf.data[0] == 9)

    buf = Buffer(2, 1024)
    b.set_input("input0", [5, 6])
    graph.render_to_buffer(buf)
    assert np.all(buf.data[0] == 11)
    assert np.all(buf.data[1] == 13)
    graph.stop(d)
def test_expansion_upmix():
    output = AudioOut_Dummy(4)
    graph = AudioGraph(output_device=output)
    a = SquareOscillator([440, 880, 1320], [0.3, 0.7])
    assert a.num_input_channels == 3
    assert a.num_output_channels == 3

    process_tree(a)
    assert np.all(a.inputs["frequency"].output_buffer[0] == 440.0)
    assert np.all(a.inputs["frequency"].output_buffer[1] == 880.0)
    assert np.all(a.inputs["frequency"].output_buffer[2] == 1320.0)
    assert np.all(a.inputs["width"].output_buffer[0] == 0.3)
    assert np.all(a.inputs["width"].output_buffer[1] == 0.7)
    assert np.all(a.inputs["width"].output_buffer[2] == 0.0)

    assert count_zero_crossings(a.output_buffer[0]) == math.ceil(
        440 * DEFAULT_BUFFER_LENGTH / graph.sample_rate)
    assert count_zero_crossings(a.output_buffer[1]) == math.ceil(
        880 * DEFAULT_BUFFER_LENGTH / graph.sample_rate)
    assert count_zero_crossings(a.output_buffer[2]) == 0

    #--------------------------------------------------------------------------------
    # When processed through a Graph, multichannel expansion duplicates the
    # 2-channel width node to become 3-channel
    #--------------------------------------------------------------------------------
    a = SquareOscillator([440, 880, 1320], [0.3, 0.7])
    graph.play(a)
    graph.render(1024)
    assert graph.node_count == 4
    assert np.all(a.inputs["frequency"].output_buffer[0] == 440.0)
    assert np.all(a.inputs["frequency"].output_buffer[1] == 880.0)
    assert np.all(a.inputs["frequency"].output_buffer[2] == 1320.0)
    assert np.all(a.inputs["width"].output_buffer[0] == 0.3)
    assert np.all(a.inputs["width"].output_buffer[1] == 0.7)
    assert np.all(a.inputs["width"].output_buffer[2] == 0.3)
    assert count_zero_crossings(a.output_buffer[0]) == math.ceil(
        440 * DEFAULT_BUFFER_LENGTH / graph.sample_rate)
    assert count_zero_crossings(a.output_buffer[1]) == math.ceil(
        880 * DEFAULT_BUFFER_LENGTH / graph.sample_rate)
    assert count_zero_crossings(a.output_buffer[2]) == math.ceil(
        1320 * DEFAULT_BUFFER_LENGTH / graph.sample_rate)

    graph.stop(a)
    graph.render(1024)
    #--------------------------------------------------------------------------------
    # Only remaining note is the abstract audio output node
    #--------------------------------------------------------------------------------
    assert graph.node_count == 1