Skip to content

Commit

Permalink
wav: accept wav file uses waveformatextensible at fmt header that spe…
Browse files Browse the repository at this point in the history
…cifies guid with KSDATAFORMAT_SUBTYPE_PCM. Maybe this also fixes issue faiface#2.
  • Loading branch information
wadasan committed Jul 28, 2018
1 parent 32585d5 commit bb05244
Showing 1 changed file with 123 additions and 4 deletions.
127 changes: 123 additions & 4 deletions wav/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package wav

import (
"encoding/binary"
"encoding/hex"
"fmt"
"io"
"time"
Expand All @@ -22,23 +23,118 @@ func Decode(rc io.ReadCloser) (s beep.StreamSeekCloser, format beep.Format, err
d.rc.Close()
}
}()
if err := binary.Read(rc, binary.LittleEndian, &d.h); err != nil {

// READ "RIFF" header
if err := binary.Read(rc, binary.LittleEndian, d.h.RiffMark[:]); err != nil {
return nil, beep.Format{}, errors.Wrap(err, "wav")
}

if string(d.h.RiffMark[:]) != "RIFF" {
return nil, beep.Format{}, errors.New("wav: missing RIFF at the beginning")
return nil, beep.Format{}, errors.New(fmt.Sprintf("wav: missing RIFF at the beginning > %s",string(d.h.RiffMark[:])))
}

// READ Total file size
if err := binary.Read(rc, binary.LittleEndian, &d.h.FileSize); err != nil {
return nil, beep.Format{}, errors.Wrap(err,"wav: missing RIFF file size")
}

if err := binary.Read(rc, binary.LittleEndian, d.h.WaveMark[:]); err != nil {
return nil, beep.Format{}, errors.Wrap(err,"wav: missing RIFF file type")
}

if string(d.h.WaveMark[:]) != "WAVE" {
return nil, beep.Format{}, errors.New("wav: unsupported file type")
}

//check each formtypes
ft := [4]byte {0,0,0,0}

var fs int32 = 0


for string(ft[:]) != "data" {

if err = binary.Read(rc,binary.LittleEndian,ft[:]); err != nil {
return nil, beep.Format{}, errors.Wrap(err, "wav: missing chunk type")
}

switch {
case string(ft[:]) == "fmt " : {
d.h.FmtMark = ft
if err := binary.Read(rc,binary.LittleEndian,&d.h.FormatSize); err != nil {
return nil, beep.Format{}, errors.New("wav: missing format chunk size")
}

if err := binary.Read(rc,binary.LittleEndian,&d.h.FormatType); err != nil {
return nil, beep.Format{}, errors.New("wav: missing format type")
}

//WAVEFORMATEXTENSIBLE
if d.h.FormatType == -2 {
fmtchunk := formatchunkextensible {0,0,0,0,0,0,0,0,[18]byte{
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}
if err := binary.Read(rc,binary.LittleEndian,&fmtchunk); err != nil {
return nil, beep.Format{}, errors.New("wav: missing format chunk body")
} else {
d.h.NumChans = fmtchunk.NumChans
d.h.SampleRate = fmtchunk.SampleRate
d.h.ByteRate = fmtchunk.ByteRate
d.h.BytesPerFrame = fmtchunk.BytesPerFrame
d.h.BitsPerSample = fmtchunk.BitsPerSample
}
if fmtchunk.SubFormat != [18]byte{0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71} {
return nil, beep.Format{}, errors.New(fmt.Sprintf("wav: unsupported sub format type - %s",hex.EncodeToString(fmtchunk.SubFormat[:])))
}
//WAVEFORMAT or WAVEFORMATEX
} else {
fmtchunk := formatchunk {0,0,0,0,0}
if err := binary.Read(rc,binary.LittleEndian,&fmtchunk); err != nil {
return nil, beep.Format{}, errors.New("wav: missing format chunk body")
} else {
d.h.NumChans = fmtchunk.NumChans
d.h.SampleRate = fmtchunk.SampleRate
d.h.ByteRate = fmtchunk.ByteRate
d.h.BytesPerFrame = fmtchunk.BytesPerFrame
d.h.BitsPerSample = fmtchunk.BitsPerSample
}
//it would be skipping cbSize (WAVEFORMATEX's last member).
if d.h.FormatSize > 16 {
trash := make([]byte,d.h.FormatSize - 16)
if err := binary.Read(rc,binary.LittleEndian,trash); err != nil {
return nil, beep.Format{}, errors.Wrap(err,"wav: missing extended format chunk body")
}
}
}

}

case string(ft[:]) == "data" : {
d.h.DataMark = ft
if err := binary.Read(rc,binary.LittleEndian,&d.h.DataSize); err != nil {
return nil, beep.Format{}, errors.Wrap(err,"wav: missing data chunk size")
}
}

default : {
if err := binary.Read(rc,binary.LittleEndian,&fs); err != nil {
return nil, beep.Format{}, errors.Wrap(err,"wav: missing unknown chunk size")
}
trash := make([]byte,fs)
if err := binary.Read(rc,binary.LittleEndian,trash); err != nil {
return nil, beep.Format{}, errors.Wrap(err,"wav: missing unknown chunk body")
}
}
}
}

if string(d.h.FmtMark[:]) != "fmt " {
return nil, beep.Format{}, errors.New("wav: missing format chunk marker")
}
if string(d.h.DataMark[:]) != "data" {
return nil, beep.Format{}, errors.New("wav: missing data chunk marker")
}
if d.h.FormatType != 1 {
return nil, beep.Format{}, errors.New("wav: unsupported format type")
if d.h.FormatType != 1 && d.h.FormatType != -2 {
return nil, beep.Format{}, errors.New(fmt.Sprintf("wav: unsupported format type - %d",d.h.FormatType))
}
if d.h.NumChans <= 0 {
return nil, beep.Format{}, errors.New("wav: invalid number of channels (less than 1)")
Expand All @@ -53,19 +149,42 @@ func Decode(rc io.ReadCloser) (s beep.StreamSeekCloser, format beep.Format, err
}
return &d, format, nil
}
type formatchunk struct {
NumChans int16
SampleRate int32
ByteRate int32
BytesPerFrame int16
BitsPerSample int16
}

type formatchunkextensible struct {
NumChans int16
SampleRate int32
ByteRate int32
BytesPerFrame int16
BitsPerSample int16
SubFormatSize int16
Samples int16
ChannelMask int16
SubFormat [18]byte
}

type header struct {
RiffMark [4]byte
FileSize int32

WaveMark [4]byte

FmtMark [4]byte
FormatSize int32
FormatType int16

NumChans int16
SampleRate int32
ByteRate int32
BytesPerFrame int16
BitsPerSample int16

DataMark [4]byte
DataSize int32
}
Expand Down

0 comments on commit bb05244

Please sign in to comment.