package audio import ( "sync/atomic" "time" ) type Sink interface { Process(source Source) } type NullSink struct { Sink } func NewNullSink() *NullSink { return &NullSink{} } func (n *NullSink) Process(source Source) { if f32Source, ok := source.(TypedSource[float32]); ok { for range f32Source.GetBlocks() { } } else if i16Source, ok := source.(TypedSource[int16]); ok { for range i16Source.GetBlocks() { } } else if i32Source, ok := source.(TypedSource[int32]); ok { for range i32Source.GetBlocks() { } } else { for range source.ToFloat32().GetBlocks() { } } } type ForwardSink struct { Sink Target Sink SamplesRead atomic.Uint64 Duration atomic.Int64 } func NewForwardSink(target Sink) *ForwardSink { return &ForwardSink{ Target: target, } } func (n *ForwardSink) GetDuration() time.Duration { return time.Duration(n.Duration.Load()) } func (n *ForwardSink) GetSamplesRead() uint64 { return n.SamplesRead.Load() } func forwardSinkProcess[T AllowedSourceTypes](n *ForwardSink, source TypedSource[T]) { processor := NewSource[T](source.GetBitDepth(), source.GetSampleRate(), source.GetChannels()) go func() { defer processor.Close() for block := range source.GetBlocks() { n.Duration.Add(int64((time.Second * time.Duration(len(block)/source.GetChannels())) / time.Duration(source.GetSampleRate()))) n.SamplesRead.Add(uint64(len(block) / source.GetChannels())) processor.IngestNative(block, source.GetBitDepth()) } }() n.Target.Process(processor) } func (n *ForwardSink) Process(source Source) { if f32Source, ok := source.(TypedSource[float32]); ok { forwardSinkProcess(n, f32Source) } else if i16Source, ok := source.(TypedSource[int16]); ok { forwardSinkProcess(n, i16Source) } else if i32Source, ok := source.(TypedSource[int32]); ok { forwardSinkProcess(n, i32Source) } else { forwardSinkProcess(n, source.ToFloat32()) } }