added dsf support, reusing the existing ID3 parser (#43)
This commit is contained in:
parent
34f7f1e3c8
commit
a9f04c2798
110
dsf.go
Normal file
110
dsf.go
Normal file
@ -0,0 +1,110 @@
|
||||
// Copyright 2015, David Howden
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tag
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
// ReadDSFTags reads DSF metadata from the io.ReadSeeker, returning the resulting
|
||||
// metadata in a Metadata implementation, or non-nil error if there was a problem.
|
||||
// samples: http://www.2l.no/hires/index.html
|
||||
func ReadDSFTags(r io.ReadSeeker) (Metadata, error) {
|
||||
dsd, err := readString(r, 4)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if dsd != "DSD " {
|
||||
return nil, errors.New("expected 'DSD '")
|
||||
}
|
||||
|
||||
_, err = r.Seek(int64(16), io.SeekCurrent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
n4, err := readBytes(r, 8)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
id3Pointer := getIntLittleEndian(n4)
|
||||
|
||||
_, err = r.Seek(int64(id3Pointer), io.SeekStart)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id3, err := ReadID3v2Tags(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return metadataDSF{id3}, nil
|
||||
}
|
||||
|
||||
type metadataDSF struct {
|
||||
id3 Metadata
|
||||
}
|
||||
|
||||
func (m metadataDSF) Format() Format {
|
||||
return m.id3.Format()
|
||||
}
|
||||
|
||||
func (m metadataDSF) FileType() FileType {
|
||||
return DSF
|
||||
}
|
||||
|
||||
func (m metadataDSF) Title() string {
|
||||
return m.id3.Title()
|
||||
}
|
||||
|
||||
func (m metadataDSF) Album() string {
|
||||
return m.id3.Album()
|
||||
}
|
||||
|
||||
func (m metadataDSF) Artist() string {
|
||||
return m.id3.Artist()
|
||||
}
|
||||
|
||||
func (m metadataDSF) AlbumArtist() string {
|
||||
return m.id3.AlbumArtist()
|
||||
}
|
||||
|
||||
func (m metadataDSF) Composer() string {
|
||||
return m.id3.Composer()
|
||||
}
|
||||
|
||||
func (m metadataDSF) Year() int {
|
||||
return m.id3.Year()
|
||||
}
|
||||
|
||||
func (m metadataDSF) Genre() string {
|
||||
return m.id3.Genre()
|
||||
}
|
||||
|
||||
func (m metadataDSF) Track() (int, int) {
|
||||
return m.id3.Track()
|
||||
}
|
||||
|
||||
func (m metadataDSF) Disc() (int, int) {
|
||||
return m.id3.Disc()
|
||||
}
|
||||
|
||||
func (m metadataDSF) Picture() *Picture {
|
||||
return m.id3.Picture()
|
||||
}
|
||||
|
||||
func (m metadataDSF) Lyrics() string {
|
||||
return m.id3.Lyrics()
|
||||
}
|
||||
|
||||
func (m metadataDSF) Comment() string {
|
||||
return m.id3.Comment()
|
||||
}
|
||||
|
||||
func (m metadataDSF) Raw() map[string]interface{} {
|
||||
return m.id3.Raw()
|
||||
}
|
4
tag.go
4
tag.go
@ -50,6 +50,9 @@ func ReadFrom(r io.ReadSeeker) (Metadata, error) {
|
||||
|
||||
case string(b[0:3]) == "ID3":
|
||||
return ReadID3v2Tags(r)
|
||||
|
||||
case string(b[0:4]) == "DSD ":
|
||||
return ReadDSFTags(r)
|
||||
}
|
||||
|
||||
m, err := ReadID3v1Tags(r)
|
||||
@ -91,6 +94,7 @@ const (
|
||||
ALAC FileType = "ALAC" // Apple Lossless file FIXME: actually detect this
|
||||
FLAC FileType = "FLAC" // FLAC file
|
||||
OGG FileType = "OGG" // OGG file
|
||||
DSF FileType = "DSF" // DSF file DSD Sony format see https://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf
|
||||
)
|
||||
|
||||
// Metadata is an interface which is used to describe metadata retrieved by this package.
|
||||
|
12
tag_test.go
12
tag_test.go
@ -62,12 +62,12 @@ func TestReadFrom(t *testing.T) {
|
||||
"with_tags/sample.m4a": fullMetadata,
|
||||
"with_tags/sample.mp4": fullMetadata,
|
||||
"with_tags/sample.ogg": fullMetadata,
|
||||
|
||||
"without_tags/sample.flac": emptyMetadata,
|
||||
"without_tags/sample.m4a": emptyMetadata,
|
||||
"without_tags/sample.mp3": emptyMetadata,
|
||||
"without_tags/sample.mp4": emptyMetadata,
|
||||
"without_tags/sample.ogg": emptyMetadata,
|
||||
"with_tags/sample.dsf": fullMetadata,
|
||||
"without_tags/sample.flac": emptyMetadata,
|
||||
"without_tags/sample.m4a": emptyMetadata,
|
||||
"without_tags/sample.mp3": emptyMetadata,
|
||||
"without_tags/sample.mp4": emptyMetadata,
|
||||
"without_tags/sample.ogg": emptyMetadata,
|
||||
}
|
||||
|
||||
for path, metadata := range testdata {
|
||||
|
BIN
testdata/with_tags/sample.dsf
vendored
Normal file
BIN
testdata/with_tags/sample.dsf
vendored
Normal file
Binary file not shown.
9
util.go
9
util.go
@ -32,6 +32,15 @@ func getInt(b []byte) int {
|
||||
return n
|
||||
}
|
||||
|
||||
func getIntLittleEndian(b []byte) int {
|
||||
var n int
|
||||
for i := len(b) - 1; i >= 0; i-- {
|
||||
n = n << 8
|
||||
n |= int(b[i])
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func readBytes(r io.Reader, n int) ([]byte, error) {
|
||||
b := make([]byte, n)
|
||||
_, err := io.ReadFull(r, b)
|
||||
|
35
util_test.go
35
util_test.go
@ -79,3 +79,38 @@ func TestGetInt(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetIntLittleEndian(t *testing.T) {
|
||||
tests := []struct {
|
||||
input []byte
|
||||
output int
|
||||
}{
|
||||
{
|
||||
[]byte{},
|
||||
0,
|
||||
},
|
||||
{
|
||||
[]byte{0x01},
|
||||
1,
|
||||
},
|
||||
{
|
||||
[]byte{0xF1, 0xF2},
|
||||
0xF2F1,
|
||||
},
|
||||
{
|
||||
[]byte{0xF1, 0xF2, 0xF3},
|
||||
0xF3F2F1,
|
||||
},
|
||||
{
|
||||
[]byte{0xF1, 0xF2, 0xF3, 0xF4},
|
||||
0xF4F3F2F1,
|
||||
},
|
||||
}
|
||||
|
||||
for ii, tt := range tests {
|
||||
got := getIntLittleEndian(tt.input)
|
||||
if got != tt.output {
|
||||
t.Errorf("[%d] getInt(%v) = %v, expected %v", ii, tt.input, got, tt.output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user