flacgo/frame/frame.go

647 lines
21 KiB
Go
Raw Normal View History

// Package frame implements access to FLAC audio frames.
//
// A brief introduction of the FLAC audio format [1] follows. FLAC encoders
// divide the audio stream into blocks through a process called blocking [2]. A
// block contains the unencoded audio samples from all channels during a short
// period of time. Each audio block is divided into subblocks, one per channel.
//
// There is often a correlation between the left and right channel of stereo
// audio. Using inter-channel decorrelation [3] it is possible to store only one
// of the channels and the difference between the channels, or store the average
// of the channels and their difference. An encoder decorrelates audio samples
// as follows:
2022-07-16 22:07:10 +00:00
//
// mid = (left + right)/2 // average of the channels
// side = left - right // difference between the channels
//
// The blocks are encoded using a variety of prediction methods [4][5] and
// stored in frames. Blocks and subblocks contains unencoded audio samples while
// frames and subframes contain encoded audio samples. A FLAC stream contains
// one or more audio frames.
//
2022-07-16 22:07:10 +00:00
// [1]: https://www.xiph.org/flac/format.html#architecture
// [2]: https://www.xiph.org/flac/format.html#blocking
// [3]: https://www.xiph.org/flac/format.html#interchannel
// [4]: https://www.xiph.org/flac/format.html#prediction
// [5]: https://godoc.org/git.gammaspectra.live/S.O.N.G/flacgo/frame#Pred
package frame
import (
"encoding/binary"
"errors"
"fmt"
"hash"
"io"
"log"
2022-07-16 22:07:10 +00:00
"git.gammaspectra.live/S.O.N.G/flacgo/internal/bits"
"git.gammaspectra.live/S.O.N.G/flacgo/internal/hashutil"
"git.gammaspectra.live/S.O.N.G/flacgo/internal/hashutil/crc16"
"git.gammaspectra.live/S.O.N.G/flacgo/internal/hashutil/crc8"
"git.gammaspectra.live/S.O.N.G/flacgo/internal/utf8"
)
// A Frame contains the header and subframes of an audio frame. It holds the
// encoded samples from a block (a part) of the audio stream. Each subframe
// holding the samples from one of its channel.
//
// ref: https://www.xiph.org/flac/format.html#frame
type Frame struct {
// Audio frame header.
Header
// One subframe per channel, containing encoded audio samples.
Subframes []*Subframe
// CRC-16 hash sum, calculated by read operations on hr.
crc hashutil.Hash16
2014-08-07 00:18:12 +00:00
// A bit reader, wrapping read operations to hr.
2014-09-30 00:24:15 +00:00
br *bits.Reader
// A CRC-16 hash reader, wrapping read operations to r.
hr io.Reader
// Underlying io.Reader.
r io.Reader
}
// New creates a new Frame for accessing the audio samples of r. It reads and
// parses an audio frame header. It returns io.EOF to signal a graceful end of
// FLAC stream.
//
// Call Frame.Parse to parse the audio samples of its subframes.
func New(r io.Reader) (frame *Frame, err error) {
// Create a new CRC-16 hash reader which adds the data from all read
// operations to a running hash.
crc := crc16.NewIBM()
hr := io.TeeReader(r, crc)
// Parse frame header.
frame = &Frame{crc: crc, hr: hr, r: r}
err = frame.parseHeader()
return frame, err
}
// Parse reads and parses the header, and the audio samples from each subframe
// of a frame. If the samples are inter-channel decorrelated between the
// subframes, it correlates them. It returns io.EOF to signal a graceful end of
// FLAC stream.
//
// ref: https://www.xiph.org/flac/format.html#interchannel
func Parse(r io.Reader) (frame *Frame, err error) {
// Parse frame header.
frame, err = New(r)
if err != nil {
return frame, err
}
// Parse subframes.
err = frame.Parse()
return frame, err
}
// Parse reads and parses the audio samples from each subframe of the frame. If
// the samples are inter-channel decorrelated between the subframes, it
// correlates them.
//
// ref: https://www.xiph.org/flac/format.html#interchannel
func (frame *Frame) Parse() error {
// Parse subframes.
frame.Subframes = make([]*Subframe, frame.Channels.Count())
var err error
for channel := range frame.Subframes {
// The side channel requires an extra bit per sample when using
// inter-channel decorrelation.
bps := uint(frame.BitsPerSample)
switch frame.Channels {
case ChannelsSideRight:
// channel 0 is the side channel.
if channel == 0 {
bps++
}
case ChannelsLeftSide, ChannelsMidSide:
// channel 1 is the side channel.
if channel == 1 {
bps++
}
}
// Parse subframe.
frame.Subframes[channel], err = frame.parseSubframe(frame.br, bps)
if err != nil {
return err
}
}
// Inter-channel correlation of subframe samples.
frame.correlate()
// 2 bytes: CRC-16 checksum.
var want uint16
2016-07-21 22:56:37 +00:00
if err = binary.Read(frame.r, binary.BigEndian, &want); err != nil {
return unexpected(err)
}
got := frame.crc.Sum16()
if got != want {
flac: add Encoder API to encode audio samples and metadata blocks (#32) * flac: encode frame header * flac: calculate CRC-8 when encoding frame headers * flac: fix encoding of frame header * flac: add preliminary subframe encoder * flac: fix UTF-8 encoding of frame number * frame: add sanity check for sample count in decodeLPC Updates #31. * flac: update flac encoding API, depricate flac.Encode Encode has been removed in favour of using NewEncoder. The Encode function was temporarily added to support re-encoding FLAC streams to update the metadata, but it had no support for encoding audio samples. The added flac.Encoder has support for encoding both metadata and audio samples. It also does not require that you first decode a FLAC file to later re-encode it by calling Encode (as was the previous behaviour). * flac: add MD5 running hash of unencoded audio samples to StreamInfo * flac: remove unused encodePadding Reported by golangci * flac: fix golangci lint issues frame/utf8.go:57:6: `decodeUTF8Int` is unused (deadcode) func decodeUTF8Int(r io.Reader) (n uint64, err error) { ^ internal/utf8/encode.go:32:16: unnecessary conversion (unconvert) bits = uint64(t2 | (x>>6)&mask2) ^ internal/utf8/encode.go:37:16: unnecessary conversion (unconvert) bits = uint64(t3 | (x>>(6*2))&mask3) ^ internal/utf8/encode.go:42:16: unnecessary conversion (unconvert) bits = uint64(t4 | (x>>(6*3))&mask4) ^ * flac: fix golangci lint issues encode_frame.go:89:1: cyclomatic complexity 52 of func `(*Encoder).encodeFrameHeader` is high (> 30) (gocyclo) func (enc *Encoder) encodeFrameHeader(w io.Writer, hdr frame.Header) error { ^ internal/utf8/encode.go:66:17: unnecessary conversion (unconvert) bits := uint64(tx | (x>>uint(6*i))&maskx) ^ encode_subframe.go:105:46: unnecessary conversion (unconvert) if err := bw.WriteBits(uint64(sample), byte(hdr.BitsPerSample)); err != nil { ^ * flac: clarify that frame.Header.Num is calculated by the encoder * flac: minor re-phrasing
2018-08-18 18:18:12 +00:00
return fmt.Errorf("frame.Frame.Parse: CRC-16 checksum mismatch; expected 0x%04X, got 0x%04X", want, got)
}
return nil
}
// Hash adds the decoded audio samples of the frame to a running MD5 hash. It
// can be used in conjunction with StreamInfo.MD5sum to verify the integrity of
// the decoded audio samples.
//
// Note: The audio samples of the frame must be decoded before calling Hash.
func (frame *Frame) Hash(md5sum hash.Hash) {
// Write decoded samples to a running MD5 hash.
bps := frame.BitsPerSample
var buf [3]byte
for i := 0; i < int(frame.BlockSize); i++ {
for _, subframe := range frame.Subframes {
sample := subframe.Samples[i]
switch bps {
case 8:
buf[0] = uint8(sample)
md5sum.Write(buf[:1])
case 16:
buf[0] = uint8(sample)
buf[1] = uint8(sample >> 8)
md5sum.Write(buf[:2])
case 24:
buf[0] = uint8(sample)
buf[1] = uint8(sample >> 8)
buf[2] = uint8(sample >> 16)
md5sum.Write(buf[:])
default:
log.Printf("frame.Frame.Hash: support for %d-bit sample size not yet implemented", bps)
}
}
}
}
// A Header contains the basic properties of an audio frame, such as its sample
// rate and channel count. To facilitate random access decoding each frame
// header starts with a sync-code. This allows the decoder to synchronize and
// locate the start of a frame header.
//
// ref: https://www.xiph.org/flac/format.html#frame_header
type Header struct {
// Specifies if the block size is fixed or variable.
HasFixedBlockSize bool
// Block size in inter-channel samples, i.e. the number of audio samples in
// each subframe.
BlockSize uint16
// Sample rate in Hz; a 0 value implies unknown, get sample rate from
// StreamInfo.
SampleRate uint32
// Specifies the number of channels (subframes) that exist in the frame,
// their order and possible inter-channel decorrelation.
Channels Channels
// Sample size in bits-per-sample; a 0 value implies unknown, get sample size
// from StreamInfo.
BitsPerSample uint8
// Specifies the frame number if the block size is fixed, and the first
// sample number in the frame otherwise. When using fixed block size, the
// first sample number in the frame can be derived by multiplying the frame
// number with the block size (in samples).
Num uint64
}
// Errors returned by Frame.parseHeader.
var (
ErrInvalidSync = errors.New("frame.Frame.parseHeader: invalid sync-code")
)
// parseHeader reads and parses the header of an audio frame.
func (frame *Frame) parseHeader() error {
// Create a new CRC-8 hash reader which adds the data from all read
// operations to a running hash.
h := crc8.NewATM()
hr := io.TeeReader(frame.hr, h)
// Create bit reader.
2014-09-30 00:24:15 +00:00
br := bits.NewReader(hr)
2014-08-07 00:18:12 +00:00
frame.br = br
// 14 bits: sync-code (11111111111110)
x, err := br.Read(14)
if err != nil {
// This is the only place an audio frame may return io.EOF, which signals
// a graceful end of a FLAC stream.
return err
}
if x != 0x3FFE {
return ErrInvalidSync
}
// 1 bit: reserved.
x, err = br.Read(1)
if err != nil {
return unexpected(err)
}
if x != 0 {
return errors.New("frame.Frame.parseHeader: non-zero reserved value")
}
// 1 bit: HasFixedBlockSize.
x, err = br.Read(1)
if err != nil {
return unexpected(err)
}
2015-05-16 15:45:39 +00:00
if x == 0 {
frame.HasFixedBlockSize = true
}
// 4 bits: BlockSize. The block size parsing is simplified by deferring it to
// the end of the header.
blockSize, err := br.Read(4)
if err != nil {
return unexpected(err)
}
// 4 bits: SampleRate. The sample rate parsing is simplified by deferring it
// to the end of the header.
sampleRate, err := br.Read(4)
if err != nil {
return unexpected(err)
}
// Parse channels.
if err := frame.parseChannels(br); err != nil {
return err
}
// Parse bits per sample.
if err := frame.parseBitsPerSample(br); err != nil {
return err
}
// 1 bit: reserved.
x, err = br.Read(1)
if err != nil {
return unexpected(err)
}
if x != 0 {
return errors.New("frame.Frame.parseHeader: non-zero reserved value")
}
// if (fixed block size)
// 1-6 bytes: UTF-8 encoded frame number.
// else
// 1-7 bytes: UTF-8 encoded sample number.
flac: add Encoder API to encode audio samples and metadata blocks (#32) * flac: encode frame header * flac: calculate CRC-8 when encoding frame headers * flac: fix encoding of frame header * flac: add preliminary subframe encoder * flac: fix UTF-8 encoding of frame number * frame: add sanity check for sample count in decodeLPC Updates #31. * flac: update flac encoding API, depricate flac.Encode Encode has been removed in favour of using NewEncoder. The Encode function was temporarily added to support re-encoding FLAC streams to update the metadata, but it had no support for encoding audio samples. The added flac.Encoder has support for encoding both metadata and audio samples. It also does not require that you first decode a FLAC file to later re-encode it by calling Encode (as was the previous behaviour). * flac: add MD5 running hash of unencoded audio samples to StreamInfo * flac: remove unused encodePadding Reported by golangci * flac: fix golangci lint issues frame/utf8.go:57:6: `decodeUTF8Int` is unused (deadcode) func decodeUTF8Int(r io.Reader) (n uint64, err error) { ^ internal/utf8/encode.go:32:16: unnecessary conversion (unconvert) bits = uint64(t2 | (x>>6)&mask2) ^ internal/utf8/encode.go:37:16: unnecessary conversion (unconvert) bits = uint64(t3 | (x>>(6*2))&mask3) ^ internal/utf8/encode.go:42:16: unnecessary conversion (unconvert) bits = uint64(t4 | (x>>(6*3))&mask4) ^ * flac: fix golangci lint issues encode_frame.go:89:1: cyclomatic complexity 52 of func `(*Encoder).encodeFrameHeader` is high (> 30) (gocyclo) func (enc *Encoder) encodeFrameHeader(w io.Writer, hdr frame.Header) error { ^ internal/utf8/encode.go:66:17: unnecessary conversion (unconvert) bits := uint64(tx | (x>>uint(6*i))&maskx) ^ encode_subframe.go:105:46: unnecessary conversion (unconvert) if err := bw.WriteBits(uint64(sample), byte(hdr.BitsPerSample)); err != nil { ^ * flac: clarify that frame.Header.Num is calculated by the encoder * flac: minor re-phrasing
2018-08-18 18:18:12 +00:00
frame.Num, err = utf8.Decode(hr)
if err != nil {
return unexpected(err)
}
// Parse block size.
if err := frame.parseBlockSize(br, blockSize); err != nil {
return err
}
// Parse sample rate.
if err := frame.parseSampleRate(br, sampleRate); err != nil {
return err
}
// 1 byte: CRC-8 checksum.
var want uint8
if err = binary.Read(frame.hr, binary.BigEndian, &want); err != nil {
return unexpected(err)
}
got := h.Sum8()
if want != got {
flac: add Encoder API to encode audio samples and metadata blocks (#32) * flac: encode frame header * flac: calculate CRC-8 when encoding frame headers * flac: fix encoding of frame header * flac: add preliminary subframe encoder * flac: fix UTF-8 encoding of frame number * frame: add sanity check for sample count in decodeLPC Updates #31. * flac: update flac encoding API, depricate flac.Encode Encode has been removed in favour of using NewEncoder. The Encode function was temporarily added to support re-encoding FLAC streams to update the metadata, but it had no support for encoding audio samples. The added flac.Encoder has support for encoding both metadata and audio samples. It also does not require that you first decode a FLAC file to later re-encode it by calling Encode (as was the previous behaviour). * flac: add MD5 running hash of unencoded audio samples to StreamInfo * flac: remove unused encodePadding Reported by golangci * flac: fix golangci lint issues frame/utf8.go:57:6: `decodeUTF8Int` is unused (deadcode) func decodeUTF8Int(r io.Reader) (n uint64, err error) { ^ internal/utf8/encode.go:32:16: unnecessary conversion (unconvert) bits = uint64(t2 | (x>>6)&mask2) ^ internal/utf8/encode.go:37:16: unnecessary conversion (unconvert) bits = uint64(t3 | (x>>(6*2))&mask3) ^ internal/utf8/encode.go:42:16: unnecessary conversion (unconvert) bits = uint64(t4 | (x>>(6*3))&mask4) ^ * flac: fix golangci lint issues encode_frame.go:89:1: cyclomatic complexity 52 of func `(*Encoder).encodeFrameHeader` is high (> 30) (gocyclo) func (enc *Encoder) encodeFrameHeader(w io.Writer, hdr frame.Header) error { ^ internal/utf8/encode.go:66:17: unnecessary conversion (unconvert) bits := uint64(tx | (x>>uint(6*i))&maskx) ^ encode_subframe.go:105:46: unnecessary conversion (unconvert) if err := bw.WriteBits(uint64(sample), byte(hdr.BitsPerSample)); err != nil { ^ * flac: clarify that frame.Header.Num is calculated by the encoder * flac: minor re-phrasing
2018-08-18 18:18:12 +00:00
return fmt.Errorf("frame.Frame.parseHeader: CRC-8 checksum mismatch; expected 0x%02X, got 0x%02X", want, got)
}
return nil
}
// parseBitsPerSample parses the bits per sample of the header.
func (frame *Frame) parseBitsPerSample(br *bits.Reader) error {
// 3 bits: BitsPerSample.
x, err := br.Read(3)
if err != nil {
return unexpected(err)
}
// The 3 bits are used to specify the sample size as follows:
// 000: unknown sample size; get from StreamInfo.
// 001: 8 bits-per-sample.
// 010: 12 bits-per-sample.
// 011: reserved.
// 100: 16 bits-per-sample.
// 101: 20 bits-per-sample.
// 110: 24 bits-per-sample.
// 111: reserved.
switch x {
case 0x0:
// 000: unknown bits-per-sample; get from StreamInfo.
case 0x1:
// 001: 8 bits-per-sample.
frame.BitsPerSample = 8
case 0x2:
// 010: 12 bits-per-sample.
frame.BitsPerSample = 12
// TODO(u): Remove log message when the test cases have been extended.
log.Printf("frame.Frame.parseHeader: The flac library test cases do not yet include any audio files with %d bits-per-sample. If possible please consider contributing this audio sample to improve the reliability of the test cases.", frame.BitsPerSample)
case 0x4:
// 100: 16 bits-per-sample.
frame.BitsPerSample = 16
case 0x5:
// 101: 20 bits-per-sample.
frame.BitsPerSample = 20
// TODO(u): Remove log message when the test cases have been extended.
log.Printf("frame.Frame.parseHeader: The flac library test cases do not yet include any audio files with %d bits-per-sample. If possible please consider contributing this audio sample to improve the reliability of the test cases.", frame.BitsPerSample)
case 0x6:
// 110: 24 bits-per-sample.
frame.BitsPerSample = 24
default:
// 011: reserved.
// 111: reserved.
return fmt.Errorf("frame.Frame.parseHeader: reserved sample size bit pattern (%03b)", x)
}
return nil
}
// parseChannels parses the channels of the header.
func (frame *Frame) parseChannels(br *bits.Reader) error {
// 4 bits: Channels.
//
// The 4 bits are used to specify the channels as follows:
// 0000: (1 channel) mono.
// 0001: (2 channels) left, right.
// 0010: (3 channels) left, right, center.
// 0011: (4 channels) left, right, left surround, right surround.
// 0100: (5 channels) left, right, center, left surround, right surround.
// 0101: (6 channels) left, right, center, LFE, left surround, right surround.
// 0110: (7 channels) left, right, center, LFE, center surround, side left, side right.
// 0111: (8 channels) left, right, center, LFE, left surround, right surround, side left, side right.
// 1000: (2 channels) left, side; using inter-channel decorrelation.
// 1001: (2 channels) side, right; using inter-channel decorrelation.
// 1010: (2 channels) mid, side; using inter-channel decorrelation.
// 1011: reserved.
// 1100: reserved.
// 1101: reserved.
// 1111: reserved.
x, err := br.Read(4)
if err != nil {
return unexpected(err)
}
if x >= 0xB {
return fmt.Errorf("frame.Frame.parseHeader: reserved channels bit pattern (%04b)", x)
}
frame.Channels = Channels(x)
return nil
}
// parseBlockSize parses the block size of the header.
func (frame *Frame) parseBlockSize(br *bits.Reader, blockSize uint64) error {
// The 4 bits of n are used to specify the block size as follows:
// 0000: reserved.
// 0001: 192 samples.
// 0010-0101: 576 * 2^(n-2) samples.
// 0110: get 8 bit (block size)-1 from the end of the header.
// 0111: get 16 bit (block size)-1 from the end of the header.
// 1000-1111: 256 * 2^(n-8) samples.
n := blockSize
switch {
case n == 0x0:
// 0000: reserved.
return errors.New("frame.Frame.parseHeader: reserved block size bit pattern (0000)")
case n == 0x1:
// 0001: 192 samples.
frame.BlockSize = 192
// TODO(u): Remove log message when the test cases have been extended.
log.Printf("frame.Frame.parseHeader: The flac library test cases do not yet include any audio files with block size %d. If possible please consider contributing this audio sample to improve the reliability of the test cases.", frame.BlockSize)
case n >= 0x2 && n <= 0x5:
// 0010-0101: 576 * 2^(n-2) samples.
frame.BlockSize = 576 * (1 << (n - 2))
case n == 0x6:
// 0110: get 8 bit (block size)-1 from the end of the header.
x, err := br.Read(8)
if err != nil {
return unexpected(err)
}
frame.BlockSize = uint16(x + 1)
case n == 0x7:
// 0111: get 16 bit (block size)-1 from the end of the header.
x, err := br.Read(16)
if err != nil {
return unexpected(err)
}
frame.BlockSize = uint16(x + 1)
default:
// 1000-1111: 256 * 2^(n-8) samples.
frame.BlockSize = 256 * (1 << (n - 8))
}
return nil
}
// parseSampleRate parses the sample rate of the header.
func (frame *Frame) parseSampleRate(br *bits.Reader, sampleRate uint64) error {
// The 4 bits are used to specify the sample rate as follows:
// 0000: unknown sample rate; get from StreamInfo.
// 0001: 88.2 kHz.
// 0010: 176.4 kHz.
// 0011: 192 kHz.
// 0100: 8 kHz.
// 0101: 16 kHz.
// 0110: 22.05 kHz.
// 0111: 24 kHz.
// 1000: 32 kHz.
// 1001: 44.1 kHz.
// 1010: 48 kHz.
// 1011: 96 kHz.
// 1100: get 8 bit sample rate (in kHz) from the end of the header.
// 1101: get 16 bit sample rate (in Hz) from the end of the header.
// 1110: get 16 bit sample rate (in daHz) from the end of the header.
// 1111: invalid.
switch sampleRate {
case 0x0:
// 0000: unknown sample rate; get from StreamInfo.
case 0x1:
// 0001: 88.2 kHz.
frame.SampleRate = 88200
case 0x2:
// 0010: 176.4 kHz.
frame.SampleRate = 176400
// TODO(u): Remove log message when the test cases have been extended.
log.Printf("frame.Frame.parseHeader: The flac library test cases do not yet include any audio files with sample rate %d. If possible please consider contributing this audio sample to improve the reliability of the test cases.", frame.SampleRate)
case 0x3:
// 0011: 192 kHz.
frame.SampleRate = 192000
case 0x4:
// 0100: 8 kHz.
frame.SampleRate = 8000
case 0x5:
// 0101: 16 kHz.
frame.SampleRate = 16000
case 0x6:
// 0110: 22.05 kHz.
frame.SampleRate = 22050
case 0x7:
// 0111: 24 kHz.
frame.SampleRate = 24000
// TODO(u): Remove log message when the test cases have been extended.
log.Printf("frame.Frame.parseHeader: The flac library test cases do not yet include any audio files with sample rate %d. If possible please consider contributing this audio sample to improve the reliability of the test cases.", frame.SampleRate)
case 0x8:
// 1000: 32 kHz.
frame.SampleRate = 32000
case 0x9:
// 1001: 44.1 kHz.
frame.SampleRate = 44100
case 0xA:
// 1010: 48 kHz.
frame.SampleRate = 48000
case 0xB:
// 1011: 96 kHz.
frame.SampleRate = 96000
case 0xC:
// 1100: get 8 bit sample rate (in kHz) from the end of the header.
x, err := br.Read(8)
if err != nil {
return unexpected(err)
}
frame.SampleRate = uint32(x * 1000)
// TODO(u): Remove log message when the test cases have been extended.
log.Printf("frame.Frame.parseHeader: The flac library test cases do not yet include any audio files with sample rate %d. If possible please consider contributing this audio sample to improve the reliability of the test cases.", frame.SampleRate)
case 0xD:
// 1101: get 16 bit sample rate (in Hz) from the end of the header.
x, err := br.Read(16)
if err != nil {
return unexpected(err)
}
frame.SampleRate = uint32(x)
case 0xE:
// 1110: get 16 bit sample rate (in daHz) from the end of the header.
x, err := br.Read(16)
if err != nil {
return unexpected(err)
}
frame.SampleRate = uint32(x * 10)
// TODO(u): Remove log message when the test cases have been extended.
log.Printf("frame.Frame.parseHeader: The flac library test cases do not yet include any audio files with sample rate %d. If possible please consider contributing this audio sample to improve the reliability of the test cases.", frame.SampleRate)
default:
// 1111: invalid.
return errors.New("frame.Frame.parseHeader: invalid sample rate bit pattern (1111)")
}
return nil
}
// Channels specifies the number of channels (subframes) that exist in a frame,
// their order and possible inter-channel decorrelation.
type Channels uint8
// Channel assignments. The following abbreviations are used:
2022-07-16 22:07:10 +00:00
//
// C: center (directly in front)
// R: right (standard stereo)
// Sr: side right (directly to the right)
// Rs: right surround (back right)
// Cs: center surround (rear center)
// Ls: left surround (back left)
// Sl: side left (directly to the left)
// L: left (standard stereo)
// Lfe: low-frequency effect (placed according to room acoustics)
//
// The first 6 channel constants follow the SMPTE/ITU-R channel order:
2022-07-16 22:07:10 +00:00
//
// L R C Lfe Ls Rs
const (
ChannelsMono Channels = iota // 1 channel: mono.
ChannelsLR // 2 channels: left, right.
ChannelsLRC // 3 channels: left, right, center.
ChannelsLRLsRs // 4 channels: left, right, left surround, right surround.
ChannelsLRCLsRs // 5 channels: left, right, center, left surround, right surround.
ChannelsLRCLfeLsRs // 6 channels: left, right, center, LFE, left surround, right surround.
ChannelsLRCLfeCsSlSr // 7 channels: left, right, center, LFE, center surround, side left, side right.
ChannelsLRCLfeLsRsSlSr // 8 channels: left, right, center, LFE, left surround, right surround, side left, side right.
ChannelsLeftSide // 2 channels: left, side; using inter-channel decorrelation.
ChannelsSideRight // 2 channels: side, right; using inter-channel decorrelation.
ChannelsMidSide // 2 channels: mid, side; using inter-channel decorrelation.
)
// nChannels specifies the number of channels used by each channel assignment.
var nChannels = [...]int{
ChannelsMono: 1,
ChannelsLR: 2,
ChannelsLRC: 3,
ChannelsLRLsRs: 4,
ChannelsLRCLsRs: 5,
ChannelsLRCLfeLsRs: 6,
ChannelsLRCLfeCsSlSr: 7,
ChannelsLRCLfeLsRsSlSr: 8,
ChannelsLeftSide: 2,
ChannelsSideRight: 2,
ChannelsMidSide: 2,
}
2014-08-06 02:38:58 +00:00
// Count returns the number of channels (subframes) used by the provided channel
// assignment.
func (channels Channels) Count() int {
return nChannels[channels]
}
// correlate reverts any inter-channel decorrelation between the samples of the
// subframes.
//
// An encoder decorrelates audio samples as follows:
2022-07-16 22:07:10 +00:00
//
// mid = (left + right)/2
// side = left - right
func (frame *Frame) correlate() {
switch frame.Channels {
case ChannelsLeftSide:
// 2 channels: left, side; using inter-channel decorrelation.
left := frame.Subframes[0].Samples
side := frame.Subframes[1].Samples
for i := range side {
// right = left - side
side[i] = left[i] - side[i]
}
case ChannelsSideRight:
// 2 channels: side, right; using inter-channel decorrelation.
side := frame.Subframes[0].Samples
right := frame.Subframes[1].Samples
// left = right + side
for i := range side {
side[i] += right[i]
}
case ChannelsMidSide:
// 2 channels: mid, side; using inter-channel decorrelation.
mid := frame.Subframes[0].Samples
side := frame.Subframes[1].Samples
for i := range side {
// left = (2*mid + side)/2
// right = (2*mid - side)/2
m := mid[i]
s := side[i]
m *= 2
// Notice that the integer division in mid = (left + right)/2 discards
// the least significant bit. It can be reconstructed however, since a
2017-12-26 14:14:11 +00:00
// sum A+B and a difference A-B has the same least significant bit.
//
// ref: Data Compression: The Complete Reference (ch. 7, Decorrelation)
m |= s & 1
mid[i] = (m + s) / 2
side[i] = (m - s) / 2
}
}
}
// SampleNumber returns the first sample number contained within the frame.
func (frame *Frame) SampleNumber() uint64 {
if frame.HasFixedBlockSize {
return frame.Num * uint64(frame.BlockSize)
}
return frame.Num
}
// unexpected returns io.ErrUnexpectedEOF if err is io.EOF, and returns err
// otherwise.
func unexpected(err error) error {
if err == io.EOF {
return io.ErrUnexpectedEOF
}
return err
}