// Copyright 2026 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
pub struct Mixer {
has_pending : Ref[Bool]
pending_sources : Ref[Array[DynSource]]
channels : ChannelCount
sample_rate : SampleRate
}
///|
pub struct MixerSource {
current_sources : Ref[Array[DynSource]]
input : Mixer
sample_count : Ref[Int]
}
///|
pub fn mixer(
channels : ChannelCount,
sample_rate : SampleRate,
) -> (Mixer, MixerSource) {
guard channels > 0 else { panic() }
guard sample_rate > 0 else { panic() }
let input = {
has_pending: @ref.new(false),
pending_sources: @ref.new([]),
channels,
sample_rate,
}
let output = {
current_sources: @ref.new([]),
input,
sample_count: @ref.new(0),
}
(input, output)
}
///|
pub fn[S : Source] Mixer::add(self : Mixer, source : S) -> Unit {
let uniform = uniform_source(to_dyn(source), self.channels, self.sample_rate)
self.pending_sources.val.push(uniform)
self.has_pending.val = true
}
///|
fn MixerSource::start_pending_sources(self : MixerSource) -> Unit {
if !self.input.has_pending.val {
return
}
let mut has_pending = false
let still_pending : Array[DynSource] = []
for source in self.input.pending_sources.val {
let in_step = self.sample_count.val % source.channels() == 0
if in_step {
self.current_sources.val.push(source)
} else {
still_pending.push(source)
has_pending = true
}
}
self.input.pending_sources.val = still_pending
self.input.has_pending.val = has_pending
}
///|
fn MixerSource::sum_current_sources(self : MixerSource) -> (Sample, Int) {
let mut sum = 0.0
let still_current : Array[DynSource] = []
for source in self.current_sources.val {
match source.next() {
None => ()
Some(value) => {
sum += value
still_current.push(source)
}
}
}
let active = still_current.length()
self.current_sources.val = still_current
(sum, active)
}
///|
pub fn MixerSource::next(self : MixerSource) -> Sample? {
self.start_pending_sources()
self.sample_count.val += 1
let (sum, active) = self.sum_current_sources()
if active == 0 {
None
} else {
Some(sum)
}
}
///|
pub fn MixerSource::channels(self : MixerSource) -> ChannelCount {
self.input.channels
}
///|
pub fn MixerSource::sample_rate(self : MixerSource) -> SampleRate {
self.input.sample_rate
}
///|
pub impl Source for MixerSource with fn next(self : MixerSource) {
self.next()
}
///|
pub impl Source for MixerSource with fn channels(self : MixerSource) {
self.channels()
}
///|
pub impl Source for MixerSource with fn sample_rate(self : MixerSource) {
self.sample_rate()
}
///|
pub impl Source for MixerSource with fn current_span_len(_self : MixerSource) {
source_default_current_span_len()
}
///|
pub impl Source for MixerSource with fn total_duration(_self : MixerSource) {
source_default_total_duration()
}
///|
pub impl Source for MixerSource with fn try_seek(
_self : MixerSource,
pos : @moon_cpal.Duration,
) -> Unit raise SeekError {
source_default_try_seek(pos)
}