Added format / decoder guesser
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
DataHoarder 2022-03-06 14:52:01 +01:00
parent 2f33745b66
commit 3cdb593d2c
5 changed files with 118 additions and 12 deletions

View file

@ -5,13 +5,12 @@ import (
"crypto/sha256"
"encoding/hex"
"git.gammaspectra.live/S.O.N.G/Kirika/audio"
"git.gammaspectra.live/S.O.N.G/Kirika/audio/format"
"git.gammaspectra.live/S.O.N.G/Kirika/audio/format/aac"
"git.gammaspectra.live/S.O.N.G/Kirika/audio/format/flac"
"git.gammaspectra.live/S.O.N.G/Kirika/audio/format/guess"
"git.gammaspectra.live/S.O.N.G/Kirika/audio/format/mp3"
"git.gammaspectra.live/S.O.N.G/Kirika/audio/format/opus"
"git.gammaspectra.live/S.O.N.G/Kirika/audio/format/tta"
"git.gammaspectra.live/S.O.N.G/Kirika/audio/format/vorbis"
"git.gammaspectra.live/S.O.N.G/Kirika/audio/packetizer"
"git.gammaspectra.live/S.O.N.G/Kirika/hasher"
"io"
@ -30,7 +29,7 @@ const TestSingleSample24 = "resources/samples/cYsmix - Haunted House/11. The Gre
const TestSingleSample16 = "resources/samples/Babbe Music - RADIANT DANCEFLOOR/01. ENTER.flac"
const TestSingleSample16TTA = "resources/samples/Babbe Music - RADIANT DANCEFLOOR/01. ENTER.tta"
func doTest(format format.Decoder, ext string, t *testing.T) {
func doTest(ext string, t *testing.T) {
for _, location := range TestSampleLocations {
entries, err := os.ReadDir(location)
if err != nil {
@ -48,7 +47,12 @@ func doTest(format format.Decoder, ext string, t *testing.T) {
return
}
defer fp.Close()
source, err := format.Open(fp)
decoders, err := guess.GetDecoders(fp, fullPath)
if err != nil {
t.Error(err)
return
}
source, err := guess.Open(fp, decoders)
if err != nil {
t.Error(err)
return
@ -65,19 +69,19 @@ func doTest(format format.Decoder, ext string, t *testing.T) {
}
func TestFLACDecode(t *testing.T) {
doTest(flac.NewFormat(), ".flac", t)
doTest(".flac", t)
}
func TestTTADecode(t *testing.T) {
doTest(tta.NewFormat(), ".tta", t)
doTest(".tta", t)
}
func TestOpusDecode(t *testing.T) {
doTest(opus.NewFormat(), ".opus", t)
doTest(".opus", t)
}
func TestMP3Decode(t *testing.T) {
doTest(mp3.NewFormat(), ".mp3", t)
doTest(".mp3", t)
}
func TestVorbisDecode(t *testing.T) {
doTest(vorbis.NewFormat(), ".vorbis", t)
doTest(".vorbis", t)
}
func TestHasher24(t *testing.T) {

View file

@ -144,5 +144,5 @@ func (f Format) Encode(source audio.Source, writer io.WriteCloser, options map[s
}
func (f Format) Identify(peek []byte, extension string) bool {
return /*bytes.Compare(peek[:4], []byte{'f', 'L', 'a', 'C'}) == 0 || */ extension == "aac"
return extension == "aac"
}

View file

@ -206,5 +206,5 @@ func (f Format) Encode(source audio.Source, writer io.WriteCloser, options map[s
}
func (f Format) Identify(peek []byte, extension string) bool {
return bytes.Compare(peek[:4], []byte{'O', 'g', 'g', 'S'}) == 0 || bytes.Compare(peek[:4], []byte{'f', 'L', 'a', 'C'}) == 0 || extension == "flac" || extension == "ogg"
return bytes.Compare(peek[:4], []byte{'O', 'g', 'g', 'S'}) == 0 || bytes.Compare(peek[:4], []byte{'f', 'L', 'a', 'C'}) == 0 || (bytes.Compare(peek[:3], []byte{'I', 'D', '3'}) == 0 && extension != "mp3") || extension == "flac" || extension == "ogg"
}

100
audio/format/guess/guess.go Normal file
View file

@ -0,0 +1,100 @@
package guess
import (
"errors"
"git.gammaspectra.live/S.O.N.G/Kirika/audio"
"git.gammaspectra.live/S.O.N.G/Kirika/audio/format"
"git.gammaspectra.live/S.O.N.G/Kirika/audio/format/aac"
"git.gammaspectra.live/S.O.N.G/Kirika/audio/format/flac"
"git.gammaspectra.live/S.O.N.G/Kirika/audio/format/mp3"
"git.gammaspectra.live/S.O.N.G/Kirika/audio/format/opus"
"git.gammaspectra.live/S.O.N.G/Kirika/audio/format/tta"
"git.gammaspectra.live/S.O.N.G/Kirika/audio/format/vorbis"
"io"
"path"
"strings"
)
var knownFormats = []format.Format{
aac.NewFormat(),
flac.NewFormat(),
mp3.NewFormat(),
opus.NewFormat(),
tta.NewFormat(),
vorbis.NewFormat(),
}
func GetDecoders(r io.ReadSeekCloser, pathName string) (decoders []format.Decoder, err error) {
extension := strings.ToLower(strings.TrimPrefix(path.Ext(pathName), "."))
var startOffset int64
startOffset, err = r.Seek(0, io.SeekCurrent)
if err != nil {
return
}
peek := make([]byte, 16)
if _, err = r.Read(peek); err != nil {
return
}
if _, err = r.Seek(startOffset, io.SeekStart); err != nil {
return
}
for _, formatEntry := range knownFormats {
if decoder, ok := formatEntry.(format.Decoder); ok {
if decoder.Identify(peek, extension) {
decoders = append(decoders, decoder)
}
}
}
if len(decoders) == 0 {
return nil, errors.New("could not find decoder")
}
return
}
func Open(r io.ReadSeekCloser, decoders []format.Decoder) (source audio.Source, err error) {
var startOffset int64
startOffset, err = r.Seek(0, io.SeekCurrent)
for _, decoder := range decoders {
if _, err = r.Seek(startOffset, io.SeekStart); err != nil {
return
}
source, err = decoder.Open(r)
if source.Blocks != nil && err == nil {
return
}
}
//seek back
if _, err = r.Seek(startOffset, io.SeekStart); err != nil {
return
}
return audio.Source{}, errors.New("could not open stream")
}
func OpenAnalyzer(r io.ReadSeekCloser, decoders []format.Decoder) (source audio.Source, analyzerChannel format.AnalyzerChannel, err error) {
var startOffset int64
startOffset, err = r.Seek(0, io.SeekCurrent)
for _, decoder := range decoders {
if analyzer, ok := decoder.(format.AnalyzerDecoder); ok {
if _, err = r.Seek(startOffset, io.SeekStart); err != nil {
return
}
source, analyzerChannel, err = analyzer.OpenAnalyzer(r)
if source.Blocks != nil && err == nil {
return
}
}
}
//seek back
if _, err = r.Seek(startOffset, io.SeekStart); err != nil {
return
}
return audio.Source{}, nil, errors.New("could not open stream analyzer")
}

View file

@ -6,6 +6,7 @@ package mp3
*/
import "C"
import (
"bytes"
"errors"
"fmt"
"git.gammaspectra.live/S.O.N.G/Kirika/audio"
@ -159,5 +160,6 @@ func (f Format) Encode(source audio.Source, writer io.WriteCloser, options map[s
}
func (f Format) Identify(peek []byte, extension string) bool {
return /*bytes.Compare(peek[:4], []byte{'f', 'L', 'a', 'C'}) == 0 || */ extension == "mp3"
//match ID3 / sync header
return peek[0] == 0xff && (peek[1]>>5) == 0b111 || (bytes.Compare(peek[:3], []byte{'I', 'D', '3'}) == 0 && extension != "flac") || extension == "mp3"
}