///|
pub(all) struct Source {
  samples : Array[Double]
  keyframes : Array[Keyframe]
  start : Int32
  gain : Double
}

///|
let offline_frame_limit : Int32 = 1440000

///|
let offline_work_limit : Int64 = 384000000L

// Bounded offline mixing. Each source has an independent convolution history.
// Summation is unclipped; clipping is measured before optional PCM quantization.

///|
pub fn render_mix(
  sources : Array[Source],
  measurements : Array[Measurement],
  sample_rate : Int32,
  hrir_rate : Int32,
  fade_samples : Int32,
) -> SceneResult raise AudioError {
  if sources.is_empty() || sources.length() > 16 || measurements.is_empty() {
    raise InvalidAudio
  }
  let taps = measurements[0].hrir.left.length()
  let mut work = 0L
  let mut length = 0
  for s in sources {
    if s.start < 0 ||
      s.start >= offline_frame_limit ||
      !finite(s.gain) ||
      s.gain.abs() > 16.0 {
      raise InvalidAudio
    }
    work += s.samples.length().to_int64() * taps.to_int64()
    if work > offline_work_limit || s.samples.length() > offline_frame_limit {
      raise InvalidAudio
    }
    let end = s.start + s.samples.length() + taps - 1
    if end > length {
      length = end
    }
  }
  if length > offline_frame_limit {
    raise InvalidAudio
  }
  let result : Stereo = {
    left: Array::make(length, 0.0),
    right: Array::make(length, 0.0),
  }
  for s in sources {
    let audio = render_scene(
        s.samples,
        measurements,
        s.keyframes,
        sample_rate,
        hrir_rate,
        fade_samples,
      ).audio
    for i in 0.. peak {
        peak = x.abs()
      }
      if x.abs() > 1.0 {
        clipped += 1
      }
    }
  }
  { audio: result, peak, clipped_samples: clipped, }
}