185 lines
4.1 KiB
Go
185 lines
4.1 KiB
Go
package metadata
|
|
|
|
import (
|
|
"crypto/sha1"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const TocPregap = 150
|
|
const SectorsPerSecond = 75
|
|
const DataTrackGap = 11400
|
|
const BytesPerSector = 2352
|
|
const CDChannels = 2
|
|
const Int16SamplesPerSector = BytesPerSector / (2 * CDChannels)
|
|
const CDSampleRate = Int16SamplesPerSector * SectorsPerSecond
|
|
|
|
//TOC includes a list, index 0 being total sectors/end, then start times follow, with TocPregap added
|
|
type TOC []int
|
|
|
|
var specialBase64Encoding = base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._").WithPadding('-')
|
|
|
|
func NewTOCFromString(toc string, split ...string) (r TOC) {
|
|
sep := " "
|
|
if len(split) > 0 {
|
|
sep = split[0]
|
|
}
|
|
for _, i := range strings.Split(toc, sep) {
|
|
item, err := strconv.Atoi(i)
|
|
if err == nil {
|
|
r = append(r, item)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
func NewTOCFromCTDBString(toc string) (r TOC) {
|
|
t := NewTOCFromString(toc)
|
|
|
|
r = append(r, t[len(t)-1]+TocPregap)
|
|
for i := 0; i < len(t)-1; i++ {
|
|
r = append(r, t[i]+TocPregap)
|
|
}
|
|
return
|
|
}
|
|
func NewTOCFromCTDB2String(toc string) (r TOC) {
|
|
t := NewTOCFromString(toc, ":")
|
|
|
|
r = append(r, t[len(t)-1]+TocPregap)
|
|
for i := 0; i < len(t)-1; i++ {
|
|
r = append(r, t[i]+TocPregap)
|
|
}
|
|
return
|
|
}
|
|
|
|
func (t TOC) GetTrackNumber() int {
|
|
return len(t) - 1
|
|
}
|
|
|
|
func (t TOC) GetDuration() time.Duration {
|
|
return (time.Second * time.Duration(t[0]-t[1])) / SectorsPerSecond
|
|
}
|
|
|
|
func (t TOC) GetTrackDuration(index int) time.Duration {
|
|
if index < 0 || index > len(t)-2 {
|
|
return 0
|
|
} else if index == len(t)-2 {
|
|
return (time.Second * time.Duration(t[0]-t[index+1])) / SectorsPerSecond
|
|
} else {
|
|
return (time.Second * time.Duration(t[index+2]-t[index+1])) / SectorsPerSecond
|
|
}
|
|
}
|
|
|
|
func (t TOC) CTDBString() string {
|
|
toc := make([]string, 0, len(t))
|
|
for _, o := range t[1:] {
|
|
toc = append(toc, fmt.Sprintf("%d", o-TocPregap))
|
|
}
|
|
toc = append(toc, fmt.Sprintf("%d", t[0]-TocPregap))
|
|
|
|
return strings.Join(toc, ":")
|
|
}
|
|
|
|
func (t TOC) CDDBString() string {
|
|
return fmt.Sprintf("%s %d %s %d", t.GetCDDB1(), t.GetTrackNumber(), t[1:].String(), int(t.GetDuration().Seconds()))
|
|
}
|
|
|
|
func (t TOC) MusicBrainzString() string {
|
|
return fmt.Sprintf("1 %d %s", t.GetTrackNumber(), t.String())
|
|
}
|
|
|
|
func (t TOC) String() string {
|
|
toc := make([]string, 0, len(t))
|
|
for _, o := range t {
|
|
toc = append(toc, fmt.Sprintf("%d", o))
|
|
}
|
|
return strings.Join(toc, " ")
|
|
}
|
|
|
|
func (t TOC) GetCDDB1() CDDB1 {
|
|
|
|
length := uint32(t.GetDuration().Seconds())
|
|
checksum := uint32(0)
|
|
for i := 1; i < len(t); i++ {
|
|
n := uint32(t[i] / SectorsPerSecond)
|
|
for n > 0 {
|
|
checksum += n % 10
|
|
n /= 10
|
|
}
|
|
}
|
|
|
|
return CDDB1(uint32((len(t)-1)&0xFF) | ((length & 0xFFFF) << 8) | ((checksum % 255) << 24))
|
|
}
|
|
|
|
func (t TOC) GetDiscID() DiscID {
|
|
|
|
hasher := sha1.New()
|
|
|
|
hasher.Write([]byte(fmt.Sprintf("%02X", 1)))
|
|
hasher.Write([]byte(fmt.Sprintf("%02X", len(t)-1)))
|
|
|
|
for i := 0; i < 100; i++ {
|
|
if i < len(t) { //tracks+lead-out
|
|
hasher.Write([]byte(fmt.Sprintf("%08X", t[i])))
|
|
} else {
|
|
hasher.Write([]byte(fmt.Sprintf("%08X", 0)))
|
|
}
|
|
}
|
|
|
|
result := hasher.Sum([]byte{})
|
|
return DiscID(specialBase64Encoding.EncodeToString(result))
|
|
}
|
|
|
|
func (t TOC) GetTocID() TocID {
|
|
|
|
hasher := sha1.New()
|
|
|
|
for i := 2; i < len(t); i++ {
|
|
hasher.Write([]byte(fmt.Sprintf("%08X", t[i]-t[1])))
|
|
}
|
|
hasher.Write([]byte(fmt.Sprintf("%08X", t[0]-t[1])))
|
|
for i := len(t) - 1; i < 100; i++ {
|
|
hasher.Write([]byte(fmt.Sprintf("%08X", 0)))
|
|
}
|
|
|
|
result := hasher.Sum([]byte{})
|
|
return TocID(specialBase64Encoding.EncodeToString(result))
|
|
}
|
|
|
|
func (t TOC) GetAccurateRipData() (byte, uint32, uint32, CDDB1) {
|
|
|
|
var TrackOffsetsAdded uint32
|
|
var TrackOffsetsMultiplied uint32
|
|
var num uint32
|
|
for i := 1; i < len(t); i++ {
|
|
start := uint32(t[i] - TocPregap)
|
|
TrackOffsetsAdded += start
|
|
if start < 1 {
|
|
start = 1
|
|
}
|
|
num++
|
|
TrackOffsetsMultiplied += start * num
|
|
}
|
|
TrackOffsetsAdded += uint32(t[0] - TocPregap)
|
|
num++
|
|
TrackOffsetsMultiplied += uint32(t[0]-TocPregap) * num
|
|
|
|
return byte(t.GetTrackNumber()), TrackOffsetsAdded, TrackOffsetsMultiplied, t.GetCDDB1()
|
|
}
|
|
|
|
func (t TOC) Equals(o TOC) bool {
|
|
if len(t) != len(o) {
|
|
return false
|
|
}
|
|
|
|
for i, d := range t {
|
|
if o[i] != d {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|