-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdecode.go
150 lines (121 loc) · 4.02 KB
/
decode.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package qoa
import (
"encoding/binary"
"errors"
)
// DecodeHeader decodes the QOA header and initializes the QOA struct with header information.
func DecodeHeader(bytes []byte) (*QOA, error) {
size := len(bytes)
if size < QOAMinFilesize {
return nil, errors.New("qoa: file too small")
}
// Read the file header, verify the magic number ('qoaf') and read the total number of samples.
fileHeader := binary.BigEndian.Uint64(bytes)
if (fileHeader >> 32) != QOAMagic {
return nil, errors.New("qoa: invalid magic number")
}
Samples := uint32(fileHeader & 0xffffffff)
if Samples == 0 {
return nil, errors.New("qoa: no samples found")
}
// Peek into the first frame header to get the number of channels and the SampleRate.
frameHeader := binary.BigEndian.Uint64(bytes[8:])
Channels := uint32(frameHeader>>56) & 0xff
SampleRate := uint32(frameHeader>>32) & 0xffffff
if Channels == 0 || SampleRate == 0 {
return nil, errors.New("qoa: first frame header is invalid")
}
return &QOA{
Samples: Samples,
Channels: Channels,
SampleRate: SampleRate,
}, nil
}
// decodeFrame decodes a QOA frame and returns the size of the decoded frame.
func (q *QOA) decodeFrame(bytes []byte, size uint, sampleData []int16, frameLen *uint32) (uint, error) {
if size < 8+QOALMSLen*4*uint(q.Channels) {
return 0, errors.New("decodeFrame: too small")
}
p := uint(0)
*frameLen = 0
// Read and verify the frame header
frameHeader := binary.BigEndian.Uint64(bytes[:8])
p += 8
channels := uint32((frameHeader >> 56) & 0x000000FF)
sampleRate := uint32((frameHeader >> 32) & 0x00FFFFFF)
samples := uint32((frameHeader >> 16) & 0x0000FFFF)
frameSize := uint(frameHeader & 0x0000FFFF)
dataSize := int(frameSize) - 8 - QOALMSLen*4*int(channels)
numSlices := dataSize / 8
maxTotalSamples := numSlices * QOASliceLen
if channels != q.Channels ||
sampleRate != q.SampleRate ||
frameSize > size ||
int(samples*channels) > maxTotalSamples {
return 0, errors.New("decodeFrame: invalid header")
}
// Read the LMS state: 4 x 2 bytes history and 4 x 2 bytes weights per channel
for c := uint32(0); c < channels; c++ {
history := binary.BigEndian.Uint64(bytes[p:])
weights := binary.BigEndian.Uint64(bytes[p+8:])
p += 16
for i := 0; i < QOALMSLen; i++ {
q.lms[c].History[i] = int16(history >> 48)
history <<= 16
q.lms[c].Weights[i] = int16(weights >> 48)
weights <<= 16
}
}
// Decode all slices for all channels in this frame
for sampleIndex := uint32(0); sampleIndex < samples; sampleIndex += QOASliceLen {
for c := uint32(0); c < channels; c++ {
slice := binary.BigEndian.Uint64(bytes[p:])
p += 8
scaleFactor := (slice >> 60) & 0xF
slice <<= 4
sliceStart := sampleIndex*channels + c
sliceEnd := uint32(clamp(int(sampleIndex)+QOASliceLen, 0, int(samples)))*channels + c
for si := sliceStart; si < sliceEnd; si += channels {
predicted := q.lms[c].predict()
quantized := int((slice >> 61) & 0x7)
dequantized := qoaDequantTable[scaleFactor][quantized]
reconstructed := clampS16(predicted + int(dequantized))
sampleData[si] = reconstructed
slice <<= 3
q.lms[c].update(reconstructed, dequantized)
}
}
}
*frameLen = samples
return p, nil
}
// Decode decodes the provided QOA encoded bytes and returns the QOA struct and the decoded audio sample data.
func Decode(bytes []byte) (*QOA, []int16, error) {
q, err := DecodeHeader(bytes)
if err != nil {
return nil, nil, err
}
size := len(bytes)
p := 8
// Calculate the required size of the sample buffer and allocate
totalSamples := q.Samples * q.Channels
sampleData := make([]int16, totalSamples)
sampleIndex := uint32(0)
frameLen := uint32(0)
frameSize := uint(0)
// Decode all frames
for {
samplePtr := sampleData[sampleIndex*q.Channels:]
frameSize, err = q.decodeFrame(bytes[p:], uint(size-p), samplePtr, &frameLen)
if err != nil {
return nil, nil, err
}
p += int(frameSize)
sampleIndex += frameLen
if !(frameSize > 0 && sampleIndex < q.Samples) {
break
}
}
q.Samples = sampleIndex
return q, sampleData, nil
}