Update Kirika, allow configuring queue sample format and bitdepth
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
DataHoarder 2022-07-19 17:21:51 +02:00
parent c25d374cef
commit bf1b5265f3
Signed by: DataHoarder
SSH key fingerprint: SHA256:OLTRf6Fl87G52SiR7sWLGNzlJt4WOX+tfI2yxo0z7xk
7 changed files with 41 additions and 43 deletions

View file

@ -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"`

View file

@ -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.

2
go.mod
View file

@ -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

4
go.sum
View file

@ -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=

View file

@ -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)
}

View file

@ -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()

View file

@ -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
}