riff

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 4, 2019 License: Apache-2.0 Imports: 7 Imported by: 7

README

Resource Interchange File Format parser

GoDoc

Build Status

The RIFF container format is used to store chunks of data in media files, especially in wav files hence this package inside under the go-audio organization.

Documentation

Overview

Package riff is package implementing a simple Resource Interchange File Format (RIFF) parser with basic support for sub formats such as WAV. The goal of this package is to give all the tools needed for a developer to implement parse any kind of file formats using the RIFF container format.

Support for PCM wav format was added so the headers are parsed, the duration and the raw sound data of a wav file can be easily accessed (See the examples below) .

For more information about RIFF: https://en.wikipedia.org/wiki/Resource_Interchange_File_Format

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	RiffID = [4]byte{'R', 'I', 'F', 'F'}
	FmtID  = [4]byte{'f', 'm', 't', ' '}

	WavFormatID = [4]byte{'W', 'A', 'V', 'E'}
	// DataFormatID is the Wave Data Chunk ID, it contains the digital audio sample data which can be decoded using the format
	// and compression method specified in the Wave Format Chunk. If the Compression Code is 1 (uncompressed PCM), then the Wave Data contains raw sample values.
	DataFormatID = [4]byte{'d', 'a', 't', 'a'}

	// ErrFmtNotSupported is a generic error reporting an unknown format.
	ErrFmtNotSupported = errors.New("format not supported")
	// ErrUnexpectedData is a generic error reporting that the parser encountered unexpected data.
	ErrUnexpectedData = errors.New("unexpected data content")
)

Functions

func Duration

func Duration(r io.Reader) (time.Duration, error)

Duration returns the time duration of the passed reader if the sub format is supported.

Example
path, _ := filepath.Abs("fixtures/sample.wav")
f, err := os.Open(path)
if err != nil {
	panic(err)
}
defer f.Close()
d, err := Duration(f)
if err != nil {
	panic(err)
}
fmt.Printf("File with a duration of %f seconds", d.Seconds())
Output:

File with a duration of 0.612177 seconds

Types

type Chunk

type Chunk struct {
	ID   [4]byte
	Size int
	Pos  int
	R    io.Reader

	Wg *sync.WaitGroup
	// contains filtered or unexported fields
}

Chunk represents the header and containt of a sub block See https://tech.ebu.ch/docs/tech/tech3285.pdf to see how audio content is stored in a BWF/WAVE file.

func (*Chunk) DecodeWavHeader

func (ch *Chunk) DecodeWavHeader(p *Parser) error

func (*Chunk) Done

func (ch *Chunk) Done()

Done signals the parent parser that we are done reading the chunk if the chunk isn't fully read, this code will do so before signaling.

func (*Chunk) Drain

func (ch *Chunk) Drain()

Drain discards the rest of the chunk

func (*Chunk) IsFullyRead

func (ch *Chunk) IsFullyRead() bool

IsFullyRead checks if we're finished reading the chunk

func (*Chunk) Read

func (ch *Chunk) Read(p []byte) (n int, err error)

Read implements the reader interface

func (*Chunk) ReadBE

func (ch *Chunk) ReadBE(dst interface{}) error

ReadBE reads the Big Endian chunk data into the passed struct

func (*Chunk) ReadByte

func (ch *Chunk) ReadByte() (byte, error)

ReadByte reads and returns a single byte

func (*Chunk) ReadLE

func (ch *Chunk) ReadLE(dst interface{}) error

ReadLE reads the Little Endian chunk data into the passed struct

type Parser

type Parser struct {

	// Chan is an Optional channel of chunks that is used to parse chunks
	Chan chan *Chunk
	// ChunkParserTimeout is the duration after which the main parser keeps going
	// if the dev hasn't reported the chunk parsing to be done.
	// By default: 2s
	ChunkParserTimeout time.Duration
	// The waitgroup is used to let the parser that it's ok to continue
	// after a chunk was passed to the optional parser channel.
	Wg *sync.WaitGroup

	// Must match RIFF
	ID [4]byte
	// This size is the size of the block
	// controlled by the RIFF header. Normally this equals the file size.
	Size uint32
	// Format name.
	// The representation of data in <wave-data>, and the content of the <format-specific-fields>
	// of the ‘fmt’ chunk, depend on the format category.
	// 0001h => Microsoft Pulse Code Modulation (PCM) format
	// 0050h => MPEG-1 Audio (audio only)
	Format [4]byte

	// A number indicating the WAVE format category of the file. The content of the
	// <format-specific-fields> portion of the ‘fmt’ chunk, and the interpretation of
	// the waveform data, depend on this value.
	// PCM = 1 (i.e. Linear quantization) Values other than 1 indicate some form of compression.
	WavAudioFormat uint16
	// The number of channels represented in the waveform data: 1 for mono or 2 for stereo.
	// Audio: Mono = 1, Stereo = 2, etc.
	// The EBU has defined the Multi-channel Broadcast Wave
	// Format [4] where more than two channels of audio are required.
	NumChannels uint16
	// The sampling rate (in sample per second) at which each channel should be played.
	// 8000, 44100, etc.
	SampleRate uint32
	// The average number of bytes per second at which the waveform data should be
	// transferred. Playback software can estimate the buffer size using this value.
	// SampleRate * NumChannels * BitsPerSample/8
	AvgBytesPerSec uint32
	// BlockAlign = SignificantBitsPerSample / 8 * NumChannels
	// It is the number of bytes per sample slice. This value is not affected by the number of channels and can be calculated with the formula:
	// NumChannels * BitsPerSample/8 The number of bytes for one sample including
	// all channels.
	// The block alignment (in bytes) of the waveform data. Playback software needs
	// to process a multiple of <nBlockAlign> bytes of data at a time, so the value of
	// <BlockAlign> can be used for buffer alignment.
	BlockAlign uint16
	// BitsPerSample 8, 16, 24...
	// Only available for PCM
	// This value specifies the number of bits used to define each sample. This value is usually 8, 16, 24 or 32.
	// If the number of bits is not byte aligned (a multiple of 8) then the number of bytes used per sample is
	// rounded up to the nearest byte size and the unused bytes are set to 0 and ignored.
	// The <nBitsPerSample> field specifies the number of bits of data used to represent each sample of
	// each channel. If there are multiple channels, the sample size is the same for each channel.
	BitsPerSample uint16
	// contains filtered or unexported fields
}

Parser is a struct containing the overall container information.

func New

func New(r io.Reader) *Parser

New creates a parser wrapper for a reader. Note that the reader doesn't get rewinded as the container is processed.

func (*Parser) Duration

func (c *Parser) Duration() (time.Duration, error)

Duration returns the time duration for the current RIFF container based on the sub format (wav etc...)

func (*Parser) IDnSize

func (c *Parser) IDnSize() ([4]byte, uint32, error)

IDnSize returns the next ID + block size

func (*Parser) NextChunk

func (c *Parser) NextChunk() (*Chunk, error)

NextChunk returns a convenient structure to parse the next chunk. If the container is fully read, io.EOF is returned as an error.

Example
// Example showing how to access the sound data
path, _ := filepath.Abs("fixtures/sample.wav")
f, err := os.Open(path)
if err != nil {
	panic(err)
}
defer f.Close()
c := New(f)
if err := c.ParseHeaders(); err != nil {
	panic(err)
}

var chunk *Chunk
for err == nil {
	chunk, err = c.NextChunk()
	if err != nil {
		panic(err)
	}
	if chunk.ID == FmtID {
		chunk.DecodeWavHeader(c)
	} else if chunk.ID == DataFormatID {
		break
	}
	chunk.Done()
}
soundData := chunk

nextSample := func() []byte {
	s := make([]byte, c.BlockAlign)
	if err := soundData.ReadLE(&s); err != nil {
		panic(err)
	}
	return s
}

// jump to a specific sample since first samples are blank
desideredPos := 1541
bytePos := desideredPos * 2
for i := 0; soundData.Pos < bytePos; i++ {
	nextSample()
	if i > soundData.Size {
		panic(fmt.Errorf("%+v read way too many bytes, we're out of bounds", soundData))
	}
}

sample := nextSample()
fmt.Printf("1542nd sample: %#X %#X\n", sample[0], sample[1])
Output:

1542nd sample: 0XFE 0XFF

func (*Parser) Parse

func (p *Parser) Parse() error

Parse parses the content of the file and populate the useful fields. If the parser has a chan set, chunks are sent to the channel.

func (*Parser) ParseHeaders

func (c *Parser) ParseHeaders() error

ParseHeaders reads the header of the passed container and populat the container with parsed info. Note that this code advances the container reader.

func (*Parser) String

func (c *Parser) String() string

String implements the Stringer interface.

Directories

Path Synopsis
riffinfo is a command line tool used to gather information about riff files.
riffinfo is a command line tool used to gather information about riff files.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL