Skip to content

Add support for WAV files with appended metadata #113

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions id.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import (

// Identify identifies the format and file type of the data in the ReadSeeker.
func Identify(r io.ReadSeeker) (format Format, fileType FileType, err error) {
b, err := readBytes(r, 11)
b, err := readBytes(r, 12)
if err != nil {
return
}

_, err = r.Seek(-11, io.SeekCurrent)
_, err = r.Seek(-12, io.SeekCurrent)
if err != nil {
err = fmt.Errorf("could not seek back to original position: %v", err)
return
Expand Down Expand Up @@ -56,6 +56,16 @@ func Identify(r io.ReadSeeker) (format Format, fileType FileType, err error) {
return
}
return format, MP3, nil

case string(b[0:4]) == "RIFF" && string(b[8:12]) == "WAVE":
format = UnknownFormat
err = setWavOffset(r)
if err != nil {
return format, WAV, err
}
// call Identify() again, replacing whatever fileType it finds with WAV
format, _, err = Identify(r)
return format, WAV, err
}

n, err := r.Seek(-128, io.SeekEnd)
Expand Down
22 changes: 16 additions & 6 deletions tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
// parsing and artwork extraction.
//
// Detect and parse tag metadata from an io.ReadSeeker (i.e. an *os.File):
// m, err := tag.ReadFrom(f)
// if err != nil {
// log.Fatal(err)
// }
// log.Print(m.Format()) // The detected format.
// log.Print(m.Title()) // The title of the track (see Metadata interface for more details).
//
// m, err := tag.ReadFrom(f)
// if err != nil {
// log.Fatal(err)
// }
// log.Print(m.Format()) // The detected format.
// log.Print(m.Title()) // The title of the track (see Metadata interface for more details).
package tag

import (
Expand Down Expand Up @@ -53,6 +54,14 @@ func ReadFrom(r io.ReadSeeker) (Metadata, error) {

case string(b[0:4]) == "DSD ":
return ReadDSFTags(r)

case string(b[0:4]) == "RIFF":
err := setWavOffset(r)
if err != nil {
return nil, err
}
// call ReadFrom() again at the new offset
return ReadFrom(r)
}

m, err := ReadID3v1Tags(r)
Expand Down Expand Up @@ -95,6 +104,7 @@ const (
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
WAV FileType = "WAV" // WAVE file
)

// Metadata is an interface which is used to describe metadata retrieved by this package.
Expand Down
62 changes: 62 additions & 0 deletions wav.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package tag

import (
"fmt"
"io"
)

func setWavOffset(r io.ReadSeeker) error {
// verify RIFF chunk
str, err := readString(r, 4)
if err != nil {
return err
}
if str != "RIFF" {
return fmt.Errorf("chunk header %v does not match expected 'RIFF'", str)
}

// verify WAVE filetype
_, err = r.Seek(4, io.SeekCurrent)
if err != nil {
return err
}
str, err = readString(r, 4)
if err != nil {
return err
}
if str != "WAVE" {
return fmt.Errorf("filetype %v does not match exptected 'WAVE'", str)
}

// identify chunk length
_, err = r.Seek(24, io.SeekCurrent) // 24-byte data format chunk is unneeded
if err != nil {
return err
}
str, err = readString(r, 4)
if err != nil {
return err
}
if str != "data" {
return fmt.Errorf("identifier %v does not match expected 'data'", err)
}
dataSize, err := readUint32LittleEndian(r)
if err != nil {
return err
}

_, err = r.Seek(int64(dataSize), io.SeekCurrent)
if err != nil {
return err
}

// skip unneeded 8-byte RIFF chunk header (4-byte ASCII identifier
// and 4-byte little-endian uint32 chunk size), more info:
// https://en.wikipedia.org/wiki/Resource_Interchange_File_Format#Explanation
_, err = r.Seek(8, io.SeekCurrent)
if err != nil {
return err
}

return nil
}