From bf1b5265f3b6ca67e097c86427e0700389a063bd Mon Sep 17 00:00:00 2001 From: WeebDataHoarder <57538841+WeebDataHoarder@users.noreply.github.com> Date: Tue, 19 Jul 2022 17:21:51 +0200 Subject: [PATCH] Update Kirika, allow configuring queue sample format and bitdepth --- config.go | 2 ++ example_config.toml | 10 +++++++++- go.mod | 2 +- go.sum | 4 ++-- mount.go | 8 ++++---- queue.go | 28 +++++++++++++++++++++++----- utilities.go | 30 ------------------------------ 7 files changed, 41 insertions(+), 43 deletions(-) delete mode 100644 utilities.go diff --git a/config.go b/config.go index 5efe6f6..d167aed 100644 --- a/config.go +++ b/config.go @@ -22,6 +22,8 @@ type Config struct { ReplayGain bool `toml:"replaygain"` Length int `toml:"length"` SampleRate int `toml:"samplerate"` + SampleFormat string `toml:"sampleformat"` + BitDepth int `toml:"bitdepth"` } `toml:"queue"` Radio struct { Port int `toml:"port"` diff --git a/example_config.toml b/example_config.toml index f9d3d9e..2af3566 100644 --- a/example_config.toml +++ b/example_config.toml @@ -58,9 +58,17 @@ buffer_duration=0 replaygain=false # Set the sample rate of the queue. Default is 44100 -# Some codecs (example: Opus) will output at a different samplerate. +# If incoming audio is different than this sample rate, it will be resampled. +# Some codecs (example: Opus) will output at a different samplerates. #samplerate=44100 +# Set the sample format and bit depth of the queue. Default is int16 for sample format, and sample format size for bit depth. +# If incoming audio has a different format / bitdepth than this sample rate, it will be converted. +# See Kirika's Codec table (Decoder Sample Format) column. Conversion from int32 to int16 is fast. +# Possible values (plus other aliases): i16, s16, int16, s16le, i32, s32, int32, int, s32le, f32, float, float32, f32le +#sampleformat=int16 +#bitdepth=0 + [radio] # # The port to stream actual audio on. MeteorLight will listen on localhost. diff --git a/go.mod b/go.mod index 28bce8c..bd57f14 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module git.gammaspectra.live/S.O.N.G/MeteorLight go 1.18 require ( - git.gammaspectra.live/S.O.N.G/Kirika v0.0.0-20220716231852-431f8e745cde + git.gammaspectra.live/S.O.N.G/Kirika v0.0.0-20220719151756-674b19a2bcc9 github.com/BurntSushi/toml v1.1.0 github.com/dhowden/tag v0.0.0-20220618230019-adf36e896086 github.com/enriquebris/goconcurrentqueue v0.6.3 diff --git a/go.sum b/go.sum index c1e1231..cdf76aa 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -git.gammaspectra.live/S.O.N.G/Kirika v0.0.0-20220716231852-431f8e745cde h1:7BKRV5h8Oh68KYpDqtcvL68NaVLROYXOKt14G5vBCCM= -git.gammaspectra.live/S.O.N.G/Kirika v0.0.0-20220716231852-431f8e745cde/go.mod h1:HrYZb1M5dv2hOfpUhLOYkK4qQqBu+7hg7p14R19ebvs= +git.gammaspectra.live/S.O.N.G/Kirika v0.0.0-20220719151756-674b19a2bcc9 h1:DQqURP3MFbMTqWl/rGyvJh7k6+3HTTxpqT2KvkZ009E= +git.gammaspectra.live/S.O.N.G/Kirika v0.0.0-20220719151756-674b19a2bcc9/go.mod h1:HrYZb1M5dv2hOfpUhLOYkK4qQqBu+7hg7p14R19ebvs= git.gammaspectra.live/S.O.N.G/go-alac v0.0.0-20220421115623-d0b3bfe57e0f h1:CxN7zlk5FdAieyRKQSbwBGBsvQ2cDF8JVCODZpzcRkA= git.gammaspectra.live/S.O.N.G/go-alac v0.0.0-20220421115623-d0b3bfe57e0f/go.mod h1:f1+h7KOnuM9zcEQp7ri4UaVvgX4m1NFFIXgReIyjGMA= git.gammaspectra.live/S.O.N.G/go-ebur128 v0.0.0-20220418202343-73a167e76255 h1:BWRx2ZFyhp5+rsXhdDZtk5Gld+L44lxlN9ASqB9Oj0M= diff --git a/mount.go b/mount.go index 5e7b2a2..0b60e91 100644 --- a/mount.go +++ b/mount.go @@ -57,9 +57,9 @@ func NewStreamMount(source audio.Source, config *StreamConfig) *StreamMount { bitrate := config.GetOption("bitrate", nil) - sampleRate := config.GetIntOption("samplerate", source.SampleRate) + sampleRate := config.GetIntOption("samplerate", source.GetSampleRate()) - channels := config.GetIntOption("channels", source.Channels) + channels := config.GetIntOption("channels", source.GetChannels()) switch config.Codec { case "vorbis": @@ -160,7 +160,7 @@ func NewStreamMount(source audio.Source, config *StreamConfig) *StreamMount { go func() { defer writer.Close() - if channels != source.Channels { + if channels != source.GetChannels() { if channels == 1 { source = filter.MonoFilter{}.Process(source) } else if channels == 2 { @@ -168,7 +168,7 @@ func NewStreamMount(source audio.Source, config *StreamConfig) *StreamMount { } } - if sampleRate != source.SampleRate { + if sampleRate != source.GetSampleRate() { source = filter.NewResampleFilter(sampleRate, filter.QualityFastest, 0).Process(source) } diff --git a/queue.go b/queue.go index e8fc463..24519bd 100644 --- a/queue.go +++ b/queue.go @@ -49,7 +49,7 @@ type QueueTrackEntry struct { } func (e *QueueTrackEntry) Load() error { - if e.source.Blocks != nil { + if e.source != nil { return nil } @@ -95,7 +95,7 @@ func (e *QueueTrackEntry) Load() error { return err } - if source.Blocks == nil { + if source == nil { return fmt.Errorf("could not find decoder for %s", e.Path) } @@ -194,15 +194,33 @@ func NewQueue(config *Config) *Queue { if config.Queue.SampleRate <= 0 { config.Queue.SampleRate = 44100 } + + sampleFormat := audio.SourceInt16 + bitDepth := 16 + switch config.Queue.SampleFormat { + case "f32", "float", "float32", "f32le": + sampleFormat = audio.SourceFloat32 + bitDepth = 0 + case "i32", "s32", "int32", "int", "s32le": + sampleFormat = audio.SourceInt32 + bitDepth = 32 + case "i16", "s16", "int16", "s16le": + sampleFormat = audio.SourceInt16 + bitDepth = 16 + } + if config.Queue.BitDepth > 0 { + bitDepth = config.Queue.BitDepth + } + q := &Queue{ NowPlaying: make(chan *QueueTrackEntry, 1), QueueEmpty: make(chan *QueueTrackEntry), config: config, - audioQueue: queue.NewQueue(config.Queue.SampleRate, 2), + audioQueue: queue.NewQueue(sampleFormat, bitDepth, config.Queue.SampleRate, 2), } blocksPerSecond := 20 - sources := SplitAudioSource(filter.NewFilterChain(q.audioQueue.GetSource(), filter.NewBufferFilter(16), filter.NewRealTimeFilter(blocksPerSecond), filter.NewBufferFilter(maxBufferSize*blocksPerSecond)), len(config.Streams)) + sources := filter.NewFilterChain(q.audioQueue.GetSource(), filter.NewBufferFilter(16), filter.NewRealTimeFilter(blocksPerSecond), filter.NewBufferFilter(maxBufferSize*blocksPerSecond)).Split(len(config.Streams)) for i, s := range q.config.Streams { mount := NewStreamMount(sources[i], s) if mount == nil { @@ -248,7 +266,7 @@ func (q *Queue) AddTrack(entry *QueueTrackEntry, tail bool) error { } removeCallback := func(queue *queue.Queue, entry *queue.QueueEntry) { - atomic.AddInt64((*int64)(&q.Duration), int64((time.Second*time.Duration(entry.ReadSamples))/time.Duration(entry.Source.SampleRate))) + atomic.AddInt64((*int64)(&q.Duration), int64((time.Second*time.Duration(entry.ReadSamples))/time.Duration(entry.Source.GetSampleRate()))) q.Remove(entry.Identifier) q.HandleQueue() diff --git a/utilities.go b/utilities.go deleted file mode 100644 index cf715a9..0000000 --- a/utilities.go +++ /dev/null @@ -1,30 +0,0 @@ -package main - -import "git.gammaspectra.live/S.O.N.G/Kirika/audio" - -func SplitAudioSource(source audio.Source, split int) (sources []audio.Source) { - for i := 0; i < split; i++ { - sources = append(sources, audio.Source{ - SampleRate: source.SampleRate, - Channels: source.Channels, - Blocks: make(chan []float32), - }) - } - - go func() { - defer func() { - for _, s := range sources { - close(s.Blocks) - } - }() - - for block := range source.Blocks { - //TODO: this might block? - for _, s := range sources { - s.Blocks <- block - } - } - }() - - return -}