-
Notifications
You must be signed in to change notification settings - Fork 44
/
encode_subframe.go
369 lines (331 loc) · 12.2 KB
/
encode_subframe.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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
package flac
import (
"fmt"
"github.com/icza/bitio"
"github.com/mewkiz/flac/frame"
iobits "github.com/mewkiz/flac/internal/bits"
"github.com/mewkiz/pkg/errutil"
)
// --- [ Subframe ] ------------------------------------------------------------
// encodeSubframe encodes the given subframe, writing to bw.
func encodeSubframe(bw *bitio.Writer, hdr frame.Header, subframe *frame.Subframe, bps uint) error {
// Encode subframe header.
if err := encodeSubframeHeader(bw, subframe.SubHeader); err != nil {
return errutil.Err(err)
}
// Adjust bps of subframe for wasted bits-per-sample.
bps -= subframe.Wasted
// Right shift to account for wasted bits-per-sample.
if subframe.Wasted > 0 {
for i, sample := range subframe.Samples {
subframe.Samples[i] = sample >> subframe.Wasted
}
// NOTE: use defer to restore original samples after encode.
defer func() {
for i, sample := range subframe.Samples {
subframe.Samples[i] = sample << subframe.Wasted
}
}()
}
// Encode audio samples.
switch subframe.Pred {
case frame.PredConstant:
if err := encodeConstantSamples(bw, hdr, subframe, bps); err != nil {
return errutil.Err(err)
}
case frame.PredVerbatim:
if err := encodeVerbatimSamples(bw, hdr, subframe, bps); err != nil {
return errutil.Err(err)
}
case frame.PredFixed:
if err := encodeFixedSamples(bw, hdr, subframe, bps); err != nil {
return errutil.Err(err)
}
case frame.PredFIR:
if err := encodeFIRSamples(bw, hdr, subframe, bps); err != nil {
return errutil.Err(err)
}
default:
return errutil.Newf("support for prediction method %v not yet implemented", subframe.Pred)
}
return nil
}
// --- [ Subframe header ] -----------------------------------------------------
// encodeSubframeHeader encodes the given subframe header, writing to bw.
func encodeSubframeHeader(bw *bitio.Writer, subHdr frame.SubHeader) error {
// Zero bit padding, to prevent sync-fooling string of 1s.
if err := bw.WriteBits(0x0, 1); err != nil {
return errutil.Err(err)
}
// Subframe type:
// 000000 : SUBFRAME_CONSTANT
// 000001 : SUBFRAME_VERBATIM
// 00001x : reserved
// 0001xx : reserved
// 001xxx : if(xxx <= 4) SUBFRAME_FIXED, xxx=order ; else reserved
// 01xxxx : reserved
// 1xxxxx : SUBFRAME_LPC, xxxxx=order-1
var bits uint64
switch subHdr.Pred {
case frame.PredConstant:
// 000000 : SUBFRAME_CONSTANT
bits = 0x00
case frame.PredVerbatim:
// 000001 : SUBFRAME_VERBATIM
bits = 0x01
case frame.PredFixed:
// 001xxx : if(xxx <= 4) SUBFRAME_FIXED, xxx=order ; else reserved
bits = 0x08 | uint64(subHdr.Order)
case frame.PredFIR:
// 1xxxxx : SUBFRAME_LPC, xxxxx=order-1
bits = 0x20 | uint64(subHdr.Order-1)
}
if err := bw.WriteBits(bits, 6); err != nil {
return errutil.Err(err)
}
// <1+k> 'Wasted bits-per-sample' flag:
//
// 0 : no wasted bits-per-sample in source subblock, k=0
// 1 : k wasted bits-per-sample in source subblock, k-1 follows, unary coded; e.g. k=3 => 001 follows, k=7 => 0000001 follows.
hasWastedBits := subHdr.Wasted > 0
if err := bw.WriteBool(hasWastedBits); err != nil {
return errutil.Err(err)
}
if hasWastedBits {
if err := iobits.WriteUnary(bw, uint64(subHdr.Wasted-1)); err != nil {
return errutil.Err(err)
}
}
return nil
}
// --- [ Constant samples ] ----------------------------------------------------
// encodeConstantSamples stores the given constant sample, writing to bw.
func encodeConstantSamples(bw *bitio.Writer, hdr frame.Header, subframe *frame.Subframe, bps uint) error {
samples := subframe.Samples
sample := samples[0]
for _, s := range samples[1:] {
if sample != s {
return errutil.Newf("constant sample mismatch; expected %v, got %v", sample, s)
}
}
// Unencoded constant value of the subblock, n = frame's bits-per-sample.
if err := bw.WriteBits(uint64(sample), uint8(bps)); err != nil {
return errutil.Err(err)
}
return nil
}
// --- [ Verbatim samples ] ----------------------------------------------------
// encodeVerbatimSamples stores the given samples verbatim (uncompressed),
// writing to bw.
func encodeVerbatimSamples(bw *bitio.Writer, hdr frame.Header, subframe *frame.Subframe, bps uint) error {
// Unencoded subblock; n = frame's bits-per-sample, i = frame's blocksize.
samples := subframe.Samples
if int(hdr.BlockSize) != len(samples) {
return errutil.Newf("block size and sample count mismatch; expected %d, got %d", hdr.BlockSize, len(samples))
}
for _, sample := range samples {
if err := bw.WriteBits(uint64(sample), uint8(bps)); err != nil {
return errutil.Err(err)
}
}
return nil
}
// --- [ Fixed samples ] -------------------------------------------------------
// encodeFixedSamples stores the given samples using linear prediction coding
// with a fixed set of predefined polynomial coefficients, writing to bw.
func encodeFixedSamples(bw *bitio.Writer, hdr frame.Header, subframe *frame.Subframe, bps uint) error {
// Encode unencoded warm-up samples.
samples := subframe.Samples
for i := 0; i < subframe.Order; i++ {
sample := samples[i]
if err := bw.WriteBits(uint64(sample), uint8(bps)); err != nil {
return errutil.Err(err)
}
}
// Compute residuals (signal errors of the prediction) between audio
// samples and LPC predicted audio samples.
const shift = 0
residuals, err := getLPCResiduals(subframe, frame.FixedCoeffs[subframe.Order], shift)
if err != nil {
return errutil.Err(err)
}
// Encode subframe residuals.
if err := encodeResiduals(bw, subframe, residuals); err != nil {
return errutil.Err(err)
}
return nil
}
// --- [ FIR samples ] -------------------------------------------------------
// encodeFIRSamples stores the given samples using linear prediction coding
// with a custom set of predefined polynomial coefficients, writing to bw.
func encodeFIRSamples(bw *bitio.Writer, hdr frame.Header, subframe *frame.Subframe, bps uint) error {
// Encode unencoded warm-up samples.
samples := subframe.Samples
for i := 0; i < subframe.Order; i++ {
sample := samples[i]
if err := bw.WriteBits(uint64(sample), uint8(bps)); err != nil {
return errutil.Err(err)
}
}
// 4 bits: (coefficients' precision in bits) - 1.
if err := bw.WriteBits(uint64(subframe.CoeffPrec-1), 4); err != nil {
return errutil.Err(err)
}
// 5 bits: predictor coefficient shift needed in bits.
if err := bw.WriteBits(uint64(subframe.CoeffShift), 5); err != nil {
return errutil.Err(err)
}
// Encode coefficients.
for _, coeff := range subframe.Coeffs {
// (prec) bits: Predictor coefficient.
if err := bw.WriteBits(uint64(coeff), uint8(subframe.CoeffPrec)); err != nil {
return errutil.Err(err)
}
}
// Compute residuals (signal errors of the prediction) between audio
// samples and LPC predicted audio samples.
residuals, err := getLPCResiduals(subframe, subframe.Coeffs, subframe.CoeffShift)
if err != nil {
return errutil.Err(err)
}
// Encode subframe residuals.
if err := encodeResiduals(bw, subframe, residuals); err != nil {
return errutil.Err(err)
}
return nil
}
// encodeResiduals encodes the residuals (prediction method error signals) of the
// subframe.
//
// ref: https://www.xiph.org/flac/format.html#residual
func encodeResiduals(bw *bitio.Writer, subframe *frame.Subframe, residuals []int32) error {
// 2 bits: Residual coding method.
if err := bw.WriteBits(uint64(subframe.ResidualCodingMethod), 2); err != nil {
return errutil.Err(err)
}
// The 2 bits are used to specify the residual coding method as follows:
// 00: Rice coding with a 4-bit Rice parameter.
// 01: Rice coding with a 5-bit Rice parameter.
// 10: reserved.
// 11: reserved.
switch subframe.ResidualCodingMethod {
case frame.ResidualCodingMethodRice1:
return encodeRicePart(bw, subframe, 4, residuals)
case frame.ResidualCodingMethodRice2:
return encodeRicePart(bw, subframe, 5, residuals)
default:
return fmt.Errorf("encodeResiduals: reserved residual coding method bit pattern (%02b)", uint8(subframe.ResidualCodingMethod))
}
}
// encodeRicePart encodes a Rice partition of residuals from the subframe, using
// a Rice parameter of the specified size in bits.
//
// ref: https://www.xiph.org/flac/format.html#partitioned_rice
// ref: https://www.xiph.org/flac/format.html#partitioned_rice2
func encodeRicePart(bw *bitio.Writer, subframe *frame.Subframe, paramSize uint, residuals []int32) error {
// 4 bits: Partition order.
riceSubframe := subframe.RiceSubframe
if err := bw.WriteBits(uint64(riceSubframe.PartOrder), 4); err != nil {
return errutil.Err(err)
}
// Parse Rice partitions; in total 2^partOrder partitions.
//
// ref: https://www.xiph.org/flac/format.html#rice_partition
// ref: https://www.xiph.org/flac/format.html#rice2_partition
partOrder := riceSubframe.PartOrder
nparts := 1 << partOrder
curResidualIndex := 0
for i := range riceSubframe.Partitions {
partition := &riceSubframe.Partitions[i]
// (4 or 5) bits: Rice parameter.
param := partition.Param
if err := bw.WriteBits(uint64(param), uint8(paramSize)); err != nil {
return errutil.Err(err)
}
// Determine the number of Rice encoded samples in the partition.
var nsamples int
if partOrder == 0 {
nsamples = subframe.NSamples - subframe.Order
} else if i != 0 {
nsamples = subframe.NSamples / nparts
} else {
nsamples = subframe.NSamples/nparts - subframe.Order
}
if paramSize == 4 && param == 0xF || paramSize == 5 && param == 0x1F {
// 1111 or 11111: Escape code, meaning the partition is in unencoded
// binary form using n bits per sample; n follows as a 5-bit number.
if err := bw.WriteBits(uint64(partition.EscapedBitsPerSample), 5); err != nil {
return errutil.Err(err)
}
for j := 0; j < nsamples; j++ {
// ref: https://datatracker.ietf.org/doc/draft-ietf-cellar-flac/
//
// From section 9.2.7.1. Escaped partition:
//
// The residual samples themselves are stored signed two's
// complement. For example, when a partition is escaped and each
// residual sample is stored with 3 bits, the number -1 is
// represented as 0b111.
residual := residuals[curResidualIndex]
curResidualIndex++
if err := bw.WriteBits(uint64(residual), uint8(partition.EscapedBitsPerSample)); err != nil {
return errutil.Err(err)
}
}
continue
}
// Encode the Rice residuals of the partition.
for j := 0; j < nsamples; j++ {
residual := residuals[curResidualIndex]
curResidualIndex++
if err := encodeRiceResidual(bw, param, residual); err != nil {
return errutil.Err(err)
}
}
}
return nil
}
// encodeRiceResidual encodes a Rice residual (error signal).
func encodeRiceResidual(bw *bitio.Writer, k uint, residual int32) error {
// ZigZag encode.
folded := iobits.EncodeZigZag(residual)
// unfold into low- and high.
lowMask := ^uint32(0) >> (32 - k) // lower k bits.
highMask := ^uint32(0) << k // upper bits.
high := (folded & highMask) >> k
low := folded & lowMask
// Write unary encoded most significant bits.
if err := iobits.WriteUnary(bw, uint64(high)); err != nil {
return errutil.Err(err)
}
// Write binary encoded least significant bits.
if err := bw.WriteBits(uint64(low), uint8(k)); err != nil {
return errutil.Err(err)
}
return nil
}
// getLPCResiduals returns the residuals (signal errors of the prediction)
// between the given audio samples and the LPC predicted audio samples, using
// the coefficients of a given polynomial, and a couple (order of polynomial;
// i.e. len(coeffs)) of unencoded warm-up samples.
func getLPCResiduals(subframe *frame.Subframe, coeffs []int32, shift int32) ([]int32, error) {
if len(coeffs) != subframe.Order {
return nil, fmt.Errorf("getLPCResiduals: prediction order (%d) differs from number of coefficients (%d)", subframe.Order, len(coeffs))
}
if shift < 0 {
return nil, fmt.Errorf("getLPCResiduals: invalid negative shift")
}
if subframe.NSamples != len(subframe.Samples) {
return nil, fmt.Errorf("getLPCResiduals: subframe sample count mismatch; expected %d, got %d", subframe.NSamples, len(subframe.Samples))
}
var residuals []int32
for i := subframe.Order; i < subframe.NSamples; i++ {
var sample int64
for j, c := range coeffs {
sample += int64(c) * int64(subframe.Samples[i-j-1])
}
residual := subframe.Samples[i] - int32(sample>>uint(shift))
residuals = append(residuals, residual)
}
return residuals, nil
}