272 lines
7.3 KiB
Go
272 lines
7.3 KiB
Go
// Copyright 2017 Hajime Hoshi
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package frameheader
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/hajimehoshi/go-mp3/internal/consts"
|
|
)
|
|
|
|
// A mepg1FrameHeader is MPEG1 Layer 1-3 frame header
|
|
type FrameHeader uint32
|
|
|
|
// ID returns this header's ID stored in position 20,19
|
|
func (f FrameHeader) ID() consts.Version {
|
|
return consts.Version((f & 0x00180000) >> 19)
|
|
}
|
|
|
|
// Layer returns the mpeg layer of this frame stored in position 18,17
|
|
func (f FrameHeader) Layer() consts.Layer {
|
|
return consts.Layer((f & 0x00060000) >> 17)
|
|
}
|
|
|
|
// ProtectionBit returns the protection bit stored in position 16
|
|
func (f FrameHeader) ProtectionBit() int {
|
|
return int(f&0x00010000) >> 16
|
|
}
|
|
|
|
// BirateIndex returns the bitrate index stored in position 15,12
|
|
func (f FrameHeader) BitrateIndex() int {
|
|
return int(f&0x0000f000) >> 12
|
|
}
|
|
|
|
// SamplingFrequency returns the SamplingFrequency in Hz stored in position 11,10
|
|
func (f FrameHeader) SamplingFrequency() consts.SamplingFrequency {
|
|
return consts.SamplingFrequency(int(f&0x00000c00) >> 10)
|
|
}
|
|
|
|
func (f FrameHeader) SamplingFrequencyValue() (int, error) {
|
|
switch f.SamplingFrequency() {
|
|
case 0:
|
|
return 44100 >> uint(f.LowSamplingFrequency()), nil
|
|
case 1:
|
|
return 48000 >> uint(f.LowSamplingFrequency()), nil
|
|
case 2:
|
|
return 32000 >> uint(f.LowSamplingFrequency()), nil
|
|
}
|
|
return 0, errors.New("mp3: frame header has invalid sample frequency")
|
|
}
|
|
|
|
// PaddingBit returns the padding bit stored in position 9
|
|
func (f FrameHeader) PaddingBit() int {
|
|
return int(f&0x00000200) >> 9
|
|
}
|
|
|
|
// PrivateBit returns the private bit stored in position 8 - this bit may be used to store arbitrary data to be used
|
|
// by an application
|
|
func (f FrameHeader) PrivateBit() int {
|
|
return int(f&0x00000100) >> 8
|
|
}
|
|
|
|
// Mode returns the channel mode, stored in position 7,6
|
|
func (f FrameHeader) Mode() consts.Mode {
|
|
return consts.Mode((f & 0x000000c0) >> 6)
|
|
}
|
|
|
|
// modeExtension returns the mode_extension - for use with Joint Stereo - stored in position 4,5
|
|
func (f FrameHeader) modeExtension() int {
|
|
return int(f&0x00000030) >> 4
|
|
}
|
|
|
|
// UseMSStereo returns a boolean value indicating whether the frame uses middle/side stereo.
|
|
func (f FrameHeader) UseMSStereo() bool {
|
|
if f.Mode() != consts.ModeJointStereo {
|
|
return false
|
|
}
|
|
return f.modeExtension()&0x2 != 0
|
|
}
|
|
|
|
// UseIntensityStereo returns a boolean value indicating whether the frame uses intensity stereo.
|
|
func (f FrameHeader) UseIntensityStereo() bool {
|
|
if f.Mode() != consts.ModeJointStereo {
|
|
return false
|
|
}
|
|
return f.modeExtension()&0x1 != 0
|
|
}
|
|
|
|
// Copyright returns whether or not this recording is copywritten - stored in position 3
|
|
func (f FrameHeader) Copyright() int {
|
|
return int(f&0x00000008) >> 3
|
|
}
|
|
|
|
// OriginalOrCopy returns whether or not this is an Original recording or a copy of one - stored in position 2
|
|
func (f FrameHeader) OriginalOrCopy() int {
|
|
return int(f&0x00000004) >> 2
|
|
}
|
|
|
|
// Emphasis returns emphasis - the emphasis indication is here to tell the decoder that the file must be de-emphasized - stored in position 0,1
|
|
func (f FrameHeader) Emphasis() int {
|
|
return int(f&0x00000003) >> 0
|
|
}
|
|
|
|
// LowSamplingFrequency returns whether the frame is encoded in a low sampling frequency => 0 = MPEG-1, 1 = MPEG-2/2.5
|
|
func (f FrameHeader) LowSamplingFrequency() int {
|
|
if f.ID() == consts.Version1 {
|
|
return 0
|
|
}
|
|
return 1
|
|
}
|
|
|
|
func (f FrameHeader) BytesPerFrame() int {
|
|
return consts.SamplesPerGr * f.Granules() * 4
|
|
}
|
|
|
|
func (f FrameHeader) Granules() int {
|
|
return consts.GranulesMpeg1 >> uint(f.LowSamplingFrequency()) // MPEG2 uses only 1 granule
|
|
}
|
|
|
|
// IsValid returns a boolean value indicating whether the header is valid or not.
|
|
func (f FrameHeader) IsValid() bool {
|
|
const sync = 0xffe00000
|
|
if (f & sync) != sync {
|
|
return false
|
|
}
|
|
if f.ID() == consts.VersionReserved {
|
|
return false
|
|
}
|
|
if f.BitrateIndex() == 15 {
|
|
return false
|
|
}
|
|
if f.SamplingFrequency() == consts.SamplingFrequencyReserved {
|
|
return false
|
|
}
|
|
if f.Layer() == consts.LayerReserved {
|
|
return false
|
|
}
|
|
if f.Emphasis() == 2 {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (f FrameHeader) Bitrate() int {
|
|
bitrates := [2][3][16]int{
|
|
{
|
|
// MPEG 1 Layer 3
|
|
{0, 32000, 40000, 48000, 56000, 64000, 80000, 96000,
|
|
112000, 128000, 160000, 192000, 224000, 256000, 320000},
|
|
|
|
// MPEG 1 Layer 2
|
|
{0, 32000, 48000, 56000, 64000, 80000, 96000, 112000,
|
|
128000, 160000, 192000, 224000, 256000, 320000, 384000},
|
|
|
|
// MPEG 1 Layer 1
|
|
{0, 32000, 64000, 96000, 128000, 160000, 192000, 224000,
|
|
256000, 288000, 320000, 352000, 384000, 416000, 448000},
|
|
},
|
|
{
|
|
// MPEG2 2 Layer 3
|
|
{0, 8000, 16000, 24000, 32000, 40000, 48000, 56000,
|
|
64000, 80000, 96000, 112000, 128000, 144000, 160000},
|
|
|
|
// MPEG 2 Layer 2
|
|
{0, 8000, 16000, 24000, 32000, 40000, 48000, 56000,
|
|
64000, 80000, 96000, 112000, 128000, 144000, 160000},
|
|
|
|
// MPEG 2 Layer 1
|
|
{0, 32000, 48000, 56000, 64000, 80000, 96000, 112000,
|
|
128000, 144000, 160000, 176000, 192000, 224000, 256000},
|
|
},
|
|
}
|
|
return bitrates[f.LowSamplingFrequency()][f.Layer()-1][f.BitrateIndex()]
|
|
}
|
|
|
|
func (f FrameHeader) FrameSize() (int, error) {
|
|
freq, err := f.SamplingFrequencyValue()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
size := ((144*f.Bitrate())/freq +
|
|
int(f.PaddingBit())) >> uint(f.LowSamplingFrequency())
|
|
return size, nil
|
|
}
|
|
|
|
func (f FrameHeader) SideInfoSize() int {
|
|
mono := f.Mode() == consts.ModeSingleChannel
|
|
var sideinfo_size int
|
|
if f.LowSamplingFrequency() == 1 {
|
|
if mono {
|
|
sideinfo_size = 9
|
|
} else {
|
|
sideinfo_size = 17
|
|
}
|
|
} else {
|
|
if mono {
|
|
sideinfo_size = 17
|
|
} else {
|
|
sideinfo_size = 32
|
|
}
|
|
}
|
|
return sideinfo_size
|
|
}
|
|
|
|
func (f FrameHeader) NumberOfChannels() int {
|
|
if f.Mode() == consts.ModeSingleChannel {
|
|
return 1
|
|
}
|
|
return 2
|
|
}
|
|
|
|
type FullReader interface {
|
|
ReadFull([]byte) (int, error)
|
|
}
|
|
|
|
func Read(source FullReader, position int64) (h FrameHeader, startPosition int64, err error) {
|
|
buf := make([]byte, 4)
|
|
if n, err := source.ReadFull(buf); n < 4 {
|
|
if err == io.EOF {
|
|
if n == 0 {
|
|
// Expected EOF
|
|
return 0, 0, io.EOF
|
|
}
|
|
return 0, 0, &consts.UnexpectedEOF{"readHeader (1)"}
|
|
}
|
|
return 0, 0, err
|
|
}
|
|
|
|
b1 := uint32(buf[0])
|
|
b2 := uint32(buf[1])
|
|
b3 := uint32(buf[2])
|
|
b4 := uint32(buf[3])
|
|
header := FrameHeader((b1 << 24) | (b2 << 16) | (b3 << 8) | (b4 << 0))
|
|
for !header.IsValid() {
|
|
b1 = b2
|
|
b2 = b3
|
|
b3 = b4
|
|
|
|
buf := make([]byte, 1)
|
|
if _, err := source.ReadFull(buf); err != nil {
|
|
if err == io.EOF {
|
|
return 0, 0, &consts.UnexpectedEOF{"readHeader (2)"}
|
|
}
|
|
return 0, 0, err
|
|
}
|
|
b4 = uint32(buf[0])
|
|
header = FrameHeader((b1 << 24) | (b2 << 16) | (b3 << 8) | (b4 << 0))
|
|
position++
|
|
}
|
|
|
|
// If we get here we've found the sync word, and can decode the header
|
|
// which is in the low 20 bits of the 32-bit sync+header word.
|
|
|
|
if header.BitrateIndex() == 0 {
|
|
return 0, 0, fmt.Errorf("mp3: free bitrate format is not supported. Header word is 0x%08x at position %d",
|
|
header, position)
|
|
}
|
|
return header, position, nil
|
|
}
|