diff --git a/id.go b/id.go index 2410356..f3efa45 100644 --- a/id.go +++ b/id.go @@ -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 @@ -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) diff --git a/tag.go b/tag.go index 306f1d7..7fc6a63 100644 --- a/tag.go +++ b/tag.go @@ -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 ( @@ -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) @@ -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. diff --git a/wav.go b/wav.go new file mode 100644 index 0000000..16cc609 --- /dev/null +++ b/wav.go @@ -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 +}