///|
fn valid_any_node_inputs(
  node : DspNode,
  node_count : Int,
  sample_rate : Double,
  output_kind : DspNodeKind,
) -> Bool {
  match node.kind {
    Constant => @dsp.is_finite(node.value0)
    Oscillator =>
      if node.input0 >= 0 {
        valid_reference(node.input0, node_count)
      } else {
        @dsp.is_finite(node.value0)
      }
    Noise => true
    Adsr =>
      @dsp.is_finite(node.value0) &&
      @dsp.is_finite(node.value1) &&
      @dsp.is_finite(node.value2) &&
      @dsp.is_finite(node.value3)
    Biquad =>
      valid_reference(node.input0, node_count) &&
      valid_biquad_graph_params(sample_rate, node.value0, node.value1)
    Delay =>
      valid_reference(node.input0, node_count) &&
      valid_delay_feedback(node.value0) &&
      valid_delay_samples(node.delay_samples, node.delay_max_samples)
    Gain =>
      valid_reference(node.input0, node_count) && @dsp.is_finite(node.value0)
    Mul =>
      valid_reference(node.input0, node_count) &&
      valid_reference(node.input1, node_count)
    Mix =>
      valid_reference(node.input0, node_count) &&
      valid_reference(node.input1, node_count)
    Clip =>
      valid_reference(node.input0, node_count) &&
      @dsp.is_finite(node.value0) &&
      node.value0 > 0.0
    Output =>
      node.kind == output_kind && valid_reference(node.input0, node_count)
    Pan =>
      valid_reference(node.input0, node_count) && @dsp.is_finite(node.value0)
    StereoGain =>
      valid_reference(node.input0, node_count) && @dsp.is_finite(node.value0)
    StereoClip =>
      valid_reference(node.input0, node_count) &&
      @dsp.is_finite(node.value0) &&
      node.value0 > 0.0
    StereoBiquad =>
      valid_reference(node.input0, node_count) &&
      valid_biquad_graph_params(sample_rate, node.value0, node.value1)
    StereoDelay =>
      valid_reference(node.input0, node_count) &&
      valid_delay_feedback(node.value0) &&
      valid_delay_samples(node.delay_samples, node.delay_max_samples)
    StereoMixDown => valid_reference(node.input0, node_count)
    StereoOutput =>
      node.kind == output_kind && valid_reference(node.input0, node_count)
  }
}

///|
fn valid_node_inputs(
  node : DspNode,
  node_count : Int,
  sample_rate : Double,
) -> Bool {
  valid_any_node_inputs(node, node_count, sample_rate, Output)
}

///|
fn valid_stereo_node_inputs(
  node : DspNode,
  node_count : Int,
  sample_rate : Double,
) -> Bool {
  valid_any_node_inputs(node, node_count, sample_rate, StereoOutput)
}

///|
fn valid_reference(input : Int, node_count : Int) -> Bool {
  input >= 0 && input < node_count
}

///|

///|
fn valid_feedback_terminal_mono_graph(
  nodes : FixedArray[DspNode],
  feedback_edges : FixedArray[(Int, Int, Int)],
) -> Bool {
  // Build per-slot back-edge source maps for shape resolution.
  let be_input0_src = FixedArray::make(nodes.length(), -1)
  let be_input1_src = FixedArray::make(nodes.length(), -1)
  for i = 0; i < feedback_edges.length(); i = i + 1 {
    let edge = feedback_edges[i]
    if edge.2 == 0 {
      be_input0_src[edge.1] = edge.0
    } else {
      be_input1_src[edge.1] = edge.0
    }
  }
  let shapes = FixedArray::make(nodes.length(), MONO_SIGNAL_SHAPE)
  for index = 0; index < nodes.length(); index = index + 1 {
    let node = nodes[index]
    let shape = match node.kind {
      Constant => MONO_SIGNAL_SHAPE
      Oscillator =>
        if node.input0 >= 0 {
          match
            feedback_resolved_input_shape(
              node.input0,
              be_input0_src[index],
              shapes,
              nodes,
            ) {
            Some(MONO_SIGNAL_SHAPE) => MONO_SIGNAL_SHAPE
            _ => return false
          }
        } else {
          MONO_SIGNAL_SHAPE
        }
      Noise => MONO_SIGNAL_SHAPE
      Adsr => MONO_SIGNAL_SHAPE
      Biquad =>
        match
          feedback_resolved_input_shape(
            node.input0,
            be_input0_src[index],
            shapes,
            nodes,
          ) {
          Some(MONO_SIGNAL_SHAPE) => MONO_SIGNAL_SHAPE
          _ => return false
        }
      Delay =>
        match
          feedback_resolved_input_shape(
            node.input0,
            be_input0_src[index],
            shapes,
            nodes,
          ) {
          Some(MONO_SIGNAL_SHAPE) => MONO_SIGNAL_SHAPE
          _ => return false
        }
      Gain =>
        match
          feedback_resolved_input_shape(
            node.input0,
            be_input0_src[index],
            shapes,
            nodes,
          ) {
          Some(MONO_SIGNAL_SHAPE) => MONO_SIGNAL_SHAPE
          _ => return false
        }
      Mul => {
        let s0 = feedback_resolved_input_shape(
          node.input0,
          be_input0_src[index],
          shapes,
          nodes,
        )
        let s1 = feedback_resolved_input_shape(
          node.input1,
          be_input1_src[index],
          shapes,
          nodes,
        )
        if is_mono_shape(s0) && is_mono_shape(s1) {
          MONO_SIGNAL_SHAPE
        } else {
          return false
        }
      }
      Mix => {
        let s0 = feedback_resolved_input_shape(
          node.input0,
          be_input0_src[index],
          shapes,
          nodes,
        )
        let s1 = feedback_resolved_input_shape(
          node.input1,
          be_input1_src[index],
          shapes,
          nodes,
        )
        if is_mono_shape(s0) && is_mono_shape(s1) {
          MONO_SIGNAL_SHAPE
        } else {
          return false
        }
      }
      Clip =>
        match
          feedback_resolved_input_shape(
            node.input0,
            be_input0_src[index],
            shapes,
            nodes,
          ) {
          Some(MONO_SIGNAL_SHAPE) => MONO_SIGNAL_SHAPE
          _ => return false
        }
      Output =>
        match
          feedback_resolved_input_shape(
            node.input0,
            be_input0_src[index],
            shapes,
            nodes,
          ) {
          Some(MONO_SIGNAL_SHAPE) => MONO_SIGNAL_SHAPE
          _ => return false
        }
      Pan =>
        match
          feedback_resolved_input_shape(
            node.input0,
            be_input0_src[index],
            shapes,
            nodes,
          ) {
          Some(MONO_SIGNAL_SHAPE) => STEREO_SIGNAL_SHAPE
          _ => return false
        }
      StereoGain =>
        match
          feedback_resolved_input_shape(
            node.input0,
            be_input0_src[index],
            shapes,
            nodes,
          ) {
          Some(STEREO_SIGNAL_SHAPE) => STEREO_SIGNAL_SHAPE
          _ => return false
        }
      StereoClip =>
        match
          feedback_resolved_input_shape(
            node.input0,
            be_input0_src[index],
            shapes,
            nodes,
          ) {
          Some(STEREO_SIGNAL_SHAPE) => STEREO_SIGNAL_SHAPE
          _ => return false
        }
      StereoBiquad =>
        match
          feedback_resolved_input_shape(
            node.input0,
            be_input0_src[index],
            shapes,
            nodes,
          ) {
          Some(STEREO_SIGNAL_SHAPE) => STEREO_SIGNAL_SHAPE
          _ => return false
        }
      StereoDelay =>
        match
          feedback_resolved_input_shape(
            node.input0,
            be_input0_src[index],
            shapes,
            nodes,
          ) {
          Some(STEREO_SIGNAL_SHAPE) => STEREO_SIGNAL_SHAPE
          _ => return false
        }
      StereoMixDown =>
        match
          feedback_resolved_input_shape(
            node.input0,
            be_input0_src[index],
            shapes,
            nodes,
          ) {
          Some(STEREO_SIGNAL_SHAPE) => MONO_SIGNAL_SHAPE
          _ => return false
        }
      StereoOutput => return false
    }
    shapes[index] = shape
  }
  // Verify no feedback edges go through Output nodes
  for index = 0; index < feedback_edges.length(); index = index + 1 {
    let edge = feedback_edges[index]
    if nodes[edge.0].kind is Output || nodes[edge.1].kind is Output {
      return false
    }
    // Verify shape compatibility
    match feedback_target_shape(nodes[edge.1], edge.2) {
      Some(expected_shape) =>
        if shapes[edge.0] != expected_shape {
          return false
        }
      None => return false
    }
  }
  let last = nodes.length() - 1
  nodes[last].kind is DspNodeKind::Output && shapes[last] == MONO_SIGNAL_SHAPE
}

///|
fn valid_terminal_mono_shapes(nodes : FixedArray[DspNode]) -> Bool {
  let shapes = FixedArray::make(nodes.length(), MONO_SIGNAL_SHAPE)
  for index = 0; index < nodes.length(); index = index + 1 {
    let node = nodes[index]
    let shape = match node.kind {
      Constant => MONO_SIGNAL_SHAPE
      Oscillator =>
        if node.input0 >= 0 {
          match input_shape(node.input0, shapes) {
            Some(MONO_SIGNAL_SHAPE) => MONO_SIGNAL_SHAPE
            _ => return false
          }
        } else {
          MONO_SIGNAL_SHAPE
        }
      Noise => MONO_SIGNAL_SHAPE
      Adsr => MONO_SIGNAL_SHAPE
      Biquad =>
        match input_shape(node.input0, shapes) {
          Some(MONO_SIGNAL_SHAPE) => MONO_SIGNAL_SHAPE
          _ => return false
        }
      Delay =>
        match input_shape(node.input0, shapes) {
          Some(MONO_SIGNAL_SHAPE) => MONO_SIGNAL_SHAPE
          _ => return false
        }
      Gain =>
        match input_shape(node.input0, shapes) {
          Some(MONO_SIGNAL_SHAPE) => MONO_SIGNAL_SHAPE
          _ => return false
        }
      Mul =>
        if is_mono_shape(input_shape(node.input0, shapes)) &&
          is_mono_shape(input_shape(node.input1, shapes)) {
          MONO_SIGNAL_SHAPE
        } else {
          return false
        }
      Mix =>
        if is_mono_shape(input_shape(node.input0, shapes)) &&
          is_mono_shape(input_shape(node.input1, shapes)) {
          MONO_SIGNAL_SHAPE
        } else {
          return false
        }
      Clip =>
        match input_shape(node.input0, shapes) {
          Some(MONO_SIGNAL_SHAPE) => MONO_SIGNAL_SHAPE
          _ => return false
        }
      Output =>
        match input_shape(node.input0, shapes) {
          Some(MONO_SIGNAL_SHAPE) => MONO_SIGNAL_SHAPE
          _ => return false
        }
      Pan =>
        match input_shape(node.input0, shapes) {
          Some(MONO_SIGNAL_SHAPE) => STEREO_SIGNAL_SHAPE
          _ => return false
        }
      StereoGain =>
        match input_shape(node.input0, shapes) {
          Some(STEREO_SIGNAL_SHAPE) => STEREO_SIGNAL_SHAPE
          _ => return false
        }
      StereoClip =>
        match input_shape(node.input0, shapes) {
          Some(STEREO_SIGNAL_SHAPE) => STEREO_SIGNAL_SHAPE
          _ => return false
        }
      StereoBiquad =>
        match input_shape(node.input0, shapes) {
          Some(STEREO_SIGNAL_SHAPE) => STEREO_SIGNAL_SHAPE
          _ => return false
        }
      StereoDelay =>
        match input_shape(node.input0, shapes) {
          Some(STEREO_SIGNAL_SHAPE) => STEREO_SIGNAL_SHAPE
          _ => return false
        }
      StereoMixDown =>
        match input_shape(node.input0, shapes) {
          Some(STEREO_SIGNAL_SHAPE) => MONO_SIGNAL_SHAPE
          _ => return false
        }
      StereoOutput => return false
    }
    shapes[index] = shape
  }

  let last = nodes.length() - 1
  nodes[last].kind is DspNodeKind::Output && shapes[last] == MONO_SIGNAL_SHAPE
}

///|
fn valid_terminal_stereo_shapes(nodes : FixedArray[DspNode]) -> Bool {
  compiled_stereo_shapes(nodes) is Some(_)
}

///|
fn compiled_stereo_shapes(nodes : FixedArray[DspNode]) -> FixedArray[Int]? {
  let shapes = FixedArray::make(nodes.length(), MONO_SIGNAL_SHAPE)
  for index = 0; index < nodes.length(); index = index + 1 {
    let node = nodes[index]
    let shape = match node.kind {
      Constant => MONO_SIGNAL_SHAPE
      Oscillator =>
        if node.input0 >= 0 {
          match input_shape(node.input0, shapes) {
            Some(MONO_SIGNAL_SHAPE) => MONO_SIGNAL_SHAPE
            _ => return None
          }
        } else {
          MONO_SIGNAL_SHAPE
        }
      Noise => MONO_SIGNAL_SHAPE
      Adsr => MONO_SIGNAL_SHAPE
      Biquad =>
        match input_shape(node.input0, shapes) {
          Some(MONO_SIGNAL_SHAPE) => MONO_SIGNAL_SHAPE
          _ => return None
        }
      Delay =>
        match input_shape(node.input0, shapes) {
          Some(MONO_SIGNAL_SHAPE) => MONO_SIGNAL_SHAPE
          _ => return None
        }
      Gain =>
        match input_shape(node.input0, shapes) {
          Some(MONO_SIGNAL_SHAPE) => MONO_SIGNAL_SHAPE
          _ => return None
        }
      Mul =>
        if is_mono_shape(input_shape(node.input0, shapes)) &&
          is_mono_shape(input_shape(node.input1, shapes)) {
          MONO_SIGNAL_SHAPE
        } else {
          return None
        }
      Mix =>
        if is_mono_shape(input_shape(node.input0, shapes)) &&
          is_mono_shape(input_shape(node.input1, shapes)) {
          MONO_SIGNAL_SHAPE
        } else {
          return None
        }
      Clip =>
        match input_shape(node.input0, shapes) {
          Some(MONO_SIGNAL_SHAPE) => MONO_SIGNAL_SHAPE
          _ => return None
        }
      Output => return None
      Pan =>
        match input_shape(node.input0, shapes) {
          Some(MONO_SIGNAL_SHAPE) => STEREO_SIGNAL_SHAPE
          _ => return None
        }
      StereoGain =>
        match input_shape(node.input0, shapes) {
          Some(STEREO_SIGNAL_SHAPE) => STEREO_SIGNAL_SHAPE
          _ => return None
        }
      StereoClip =>
        match input_shape(node.input0, shapes) {
          Some(STEREO_SIGNAL_SHAPE) => STEREO_SIGNAL_SHAPE
          _ => return None
        }
      StereoBiquad =>
        match input_shape(node.input0, shapes) {
          Some(STEREO_SIGNAL_SHAPE) => STEREO_SIGNAL_SHAPE
          _ => return None
        }
      StereoDelay =>
        match input_shape(node.input0, shapes) {
          Some(STEREO_SIGNAL_SHAPE) => STEREO_SIGNAL_SHAPE
          _ => return None
        }
      StereoMixDown => return None
      StereoOutput =>
        match input_shape(node.input0, shapes) {
          Some(STEREO_SIGNAL_SHAPE) => STEREO_SIGNAL_SHAPE
          _ => return None
        }
    }
    shapes[index] = shape
  }

  let last = nodes.length() - 1
  if nodes[last].kind is DspNodeKind::StereoOutput &&
    shapes[last] == STEREO_SIGNAL_SHAPE {
    Some(shapes)
  } else {
    None
  }
}

///|
/// Infer signal shapes for a stereo feedback graph. Back-edge inputs are
/// resolved from the source node's intrinsic output shape rather than from
/// forward propagation, which lets the analysis succeed for cycles that cross
/// the mono/stereo boundary (e.g. Pan -> StereoGain -> StereoMixDown -> Mix).
fn compiled_feedback_stereo_shapes(
  nodes : FixedArray[DspNode],
  feedback_edges : FixedArray[(Int, Int, Int)],
) -> FixedArray[Int]? {
  // Build per-slot back-edge source maps so we can skip them during forward
  // shape propagation.
  let be_input0_src = FixedArray::make(nodes.length(), -1)
  let be_input1_src = FixedArray::make(nodes.length(), -1)
  for i = 0; i < feedback_edges.length(); i = i + 1 {
    let edge = feedback_edges[i]
    if edge.2 == 0 {
      be_input0_src[edge.1] = edge.0
    } else {
      be_input1_src[edge.1] = edge.0
    }
  }
  let shapes = FixedArray::make(nodes.length(), MONO_SIGNAL_SHAPE)
  for index = 0; index < nodes.length(); index = index + 1 {
    let node = nodes[index]
    let shape = match node.kind {
      Constant => MONO_SIGNAL_SHAPE
      Oscillator =>
        if node.input0 >= 0 {
          let s = feedback_resolved_input_shape(
            node.input0,
            be_input0_src[index],
            shapes,
            nodes,
          )
          match s {
            Some(MONO_SIGNAL_SHAPE) => MONO_SIGNAL_SHAPE
            _ => return None
          }
        } else {
          MONO_SIGNAL_SHAPE
        }
      Noise => MONO_SIGNAL_SHAPE
      Adsr => MONO_SIGNAL_SHAPE
      Biquad => {
        let s = feedback_resolved_input_shape(
          node.input0,
          be_input0_src[index],
          shapes,
          nodes,
        )
        match s {
          Some(MONO_SIGNAL_SHAPE) => MONO_SIGNAL_SHAPE
          _ => return None
        }
      }
      Delay => {
        let s = feedback_resolved_input_shape(
          node.input0,
          be_input0_src[index],
          shapes,
          nodes,
        )
        match s {
          Some(MONO_SIGNAL_SHAPE) => MONO_SIGNAL_SHAPE
          _ => return None
        }
      }
      Gain => {
        let s = feedback_resolved_input_shape(
          node.input0,
          be_input0_src[index],
          shapes,
          nodes,
        )
        match s {
          Some(MONO_SIGNAL_SHAPE) => MONO_SIGNAL_SHAPE
          _ => return None
        }
      }
      Mul => {
        let s0 = feedback_resolved_input_shape(
          node.input0,
          be_input0_src[index],
          shapes,
          nodes,
        )
        let s1 = feedback_resolved_input_shape(
          node.input1,
          be_input1_src[index],
          shapes,
          nodes,
        )
        if is_mono_shape(s0) && is_mono_shape(s1) {
          MONO_SIGNAL_SHAPE
        } else {
          return None
        }
      }
      Mix => {
        let s0 = feedback_resolved_input_shape(
          node.input0,
          be_input0_src[index],
          shapes,
          nodes,
        )
        let s1 = feedback_resolved_input_shape(
          node.input1,
          be_input1_src[index],
          shapes,
          nodes,
        )
        if is_mono_shape(s0) && is_mono_shape(s1) {
          MONO_SIGNAL_SHAPE
        } else {
          return None
        }
      }
      Clip => {
        let s = feedback_resolved_input_shape(
          node.input0,
          be_input0_src[index],
          shapes,
          nodes,
        )
        match s {
          Some(MONO_SIGNAL_SHAPE) => MONO_SIGNAL_SHAPE
          _ => return None
        }
      }
      Output => {
        let s = feedback_resolved_input_shape(
          node.input0,
          be_input0_src[index],
          shapes,
          nodes,
        )
        match s {
          Some(MONO_SIGNAL_SHAPE) => MONO_SIGNAL_SHAPE
          _ => return None
        }
      }
      Pan => {
        let s = feedback_resolved_input_shape(
          node.input0,
          be_input0_src[index],
          shapes,
          nodes,
        )
        match s {
          Some(MONO_SIGNAL_SHAPE) => STEREO_SIGNAL_SHAPE
          _ => return None
        }
      }
      StereoGain => {
        let s = feedback_resolved_input_shape(
          node.input0,
          be_input0_src[index],
          shapes,
          nodes,
        )
        match s {
          Some(STEREO_SIGNAL_SHAPE) => STEREO_SIGNAL_SHAPE
          _ => return None
        }
      }
      StereoClip => {
        let s = feedback_resolved_input_shape(
          node.input0,
          be_input0_src[index],
          shapes,
          nodes,
        )
        match s {
          Some(STEREO_SIGNAL_SHAPE) => STEREO_SIGNAL_SHAPE
          _ => return None
        }
      }
      StereoBiquad => {
        let s = feedback_resolved_input_shape(
          node.input0,
          be_input0_src[index],
          shapes,
          nodes,
        )
        match s {
          Some(STEREO_SIGNAL_SHAPE) => STEREO_SIGNAL_SHAPE
          _ => return None
        }
      }
      StereoDelay => {
        let s = feedback_resolved_input_shape(
          node.input0,
          be_input0_src[index],
          shapes,
          nodes,
        )
        match s {
          Some(STEREO_SIGNAL_SHAPE) => STEREO_SIGNAL_SHAPE
          _ => return None
        }
      }
      StereoMixDown => {
        let s = feedback_resolved_input_shape(
          node.input0,
          be_input0_src[index],
          shapes,
          nodes,
        )
        match s {
          Some(STEREO_SIGNAL_SHAPE) => MONO_SIGNAL_SHAPE
          _ => return None
        }
      }
      StereoOutput => {
        let s = feedback_resolved_input_shape(
          node.input0,
          be_input0_src[index],
          shapes,
          nodes,
        )
        match s {
          Some(STEREO_SIGNAL_SHAPE) => STEREO_SIGNAL_SHAPE
          _ => return None
        }
      }
    }
    shapes[index] = shape
  }

  let last = nodes.length() - 1
  if nodes[last].kind is DspNodeKind::StereoOutput &&
    shapes[last] == STEREO_SIGNAL_SHAPE {
    Some(shapes)
  } else {
    None
  }
}

///|
/// Resolve the shape of an input, accounting for back-edges. If the input is a
/// back-edge, use the intrinsic output shape of the source node.
fn feedback_resolved_input_shape(
  input_index : Int,
  back_edge_source : Int,
  shapes : FixedArray[Int],
  nodes : FixedArray[DspNode],
) -> Int? {
  if back_edge_source >= 0 {
    Some(nodes[back_edge_source].signal_shape())
  } else {
    input_shape(input_index, shapes)
  }
}

///|
fn valid_feedback_terminal_stereo_graph(
  nodes : FixedArray[DspNode],
  feedback_edges : FixedArray[(Int, Int, Int)],
) -> Bool {
  let shapes = match compiled_feedback_stereo_shapes(nodes, feedback_edges) {
    Some(shapes) => shapes
    None => return false
  }
  for index = 0; index < feedback_edges.length(); index = index + 1 {
    let edge = feedback_edges[index]
    if nodes[edge.0].kind is Output ||
      nodes[edge.0].kind is StereoOutput ||
      nodes[edge.1].kind is Output ||
      nodes[edge.1].kind is StereoOutput {
      return false
    }
    // Verify shape compatibility: source shape must match what target slot expects
    match feedback_target_shape(nodes[edge.1], edge.2) {
      Some(expected_shape) =>
        if shapes[edge.0] != expected_shape {
          return false
        }
      None => return false
    }
  }
  true
}

///|
fn feedback_target_shape(node : DspNode, target_slot : Int) -> Int? {
  match node.kind {
    Biquad => if target_slot == 0 { Some(MONO_SIGNAL_SHAPE) } else { None }
    Delay => if target_slot == 0 { Some(MONO_SIGNAL_SHAPE) } else { None }
    Gain => if target_slot == 0 { Some(MONO_SIGNAL_SHAPE) } else { None }
    Mul =>
      if target_slot == 0 || target_slot == 1 {
        Some(MONO_SIGNAL_SHAPE)
      } else {
        None
      }
    Mix =>
      if target_slot == 0 || target_slot == 1 {
        Some(MONO_SIGNAL_SHAPE)
      } else {
        None
      }
    Clip => if target_slot == 0 { Some(MONO_SIGNAL_SHAPE) } else { None }
    Pan => if target_slot == 0 { Some(MONO_SIGNAL_SHAPE) } else { None }
    StereoGain =>
      if target_slot == 0 {
        Some(STEREO_SIGNAL_SHAPE)
      } else {
        None
      }
    StereoClip =>
      if target_slot == 0 {
        Some(STEREO_SIGNAL_SHAPE)
      } else {
        None
      }
    StereoBiquad =>
      if target_slot == 0 {
        Some(STEREO_SIGNAL_SHAPE)
      } else {
        None
      }
    StereoDelay =>
      if target_slot == 0 {
        Some(STEREO_SIGNAL_SHAPE)
      } else {
        None
      }
    StereoMixDown =>
      if target_slot == 0 {
        Some(STEREO_SIGNAL_SHAPE)
      } else {
        None
      }
    Output => if target_slot == 0 { Some(MONO_SIGNAL_SHAPE) } else { None }
    StereoOutput =>
      if target_slot == 0 {
        Some(STEREO_SIGNAL_SHAPE)
      } else {
        None
      }
    _ => None
  }
}

///|
fn input_shape(input : Int, shapes : FixedArray[Int]) -> Int? {
  if input < 0 || input >= shapes.length() {
    None
  } else {
    Some(shapes[input])
  }
}