-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathdecoder_ctl_equivalence_test.go
More file actions
559 lines (500 loc) · 20 KB
/
Copy pathdecoder_ctl_equivalence_test.go
File metadata and controls
559 lines (500 loc) · 20 KB
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
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
// decoder_ctl_equivalence_test.go verifies that every decoder CTL exposed by
// gopus matches the libopus 1.5.2 C oracle for default values, round-trip
// SET/GET, and clamping. Error-code equivalence for malformed packets is also
// covered here.
//
// Libopus references (src/opus_decoder.c):
// - opus_decoder_init: complexity=0, decode_gain=0, frame_size=Fs/400
// - opus_decoder_ctl cases: OPUS_GET_BANDWIDTH, OPUS_SET/GET_COMPLEXITY,
// OPUS_GET_FINAL_RANGE, OPUS_GET_SAMPLE_RATE, OPUS_GET_PITCH,
// OPUS_GET/SET_GAIN, OPUS_GET_LAST_PACKET_DURATION,
// OPUS_SET/GET_PHASE_INVERSION_DISABLED,
// OPUS_SET/GET_IGNORE_EXTENSIONS
// - opus_decode_native: OPUS_BUFFER_TOO_SMALL when
// count*packet_frame_size > frame_size; OPUS_INVALID_PACKET propagated
// from opus_packet_parse_impl (count < 0); OPUS_BAD_ARG when len < 0.
package gopus
import (
"testing"
)
// ---------------------------------------------------------------------------
// CTL default values (opus_decoder_init zeroes the struct; only non-zero
// defaults are noted in the code).
// ---------------------------------------------------------------------------
// TestDecoderCTL_Defaults verifies freshly-created decoder CTL getter values
// match libopus opus_decoder_init() defaults.
//
// C ref: opus_decoder.c opus_decoder_init()
//
// st->complexity = 0
// st->decode_gain = 0 (OPUS_CLEAR zeroes the struct)
// st->frame_size = Fs/400 (last_packet_duration starts at 0 before any decode)
// st->bandwidth = 0 → reported as 0 (no packet decoded yet)
func TestDecoderCTL_Defaults(t *testing.T) {
dec := mustNewTestDecoder(t, 48000, 1)
// OPUS_GET_COMPLEXITY default = 0
if got := dec.Complexity(); got != 0 {
t.Errorf("Complexity() default = %d, want 0 (libopus: st->complexity=0 at init)", got)
}
// OPUS_GET_GAIN default = 0
if got := dec.Gain(); got != 0 {
t.Errorf("Gain() default = %d, want 0 (libopus: decode_gain=0 via OPUS_CLEAR)", got)
}
// OPUS_GET_LAST_PACKET_DURATION default = 0 (no packet decoded yet)
if got := dec.LastPacketDuration(); got != 0 {
t.Errorf("LastPacketDuration() default = %d, want 0", got)
}
// OPUS_GET_FINAL_RANGE default = 0 (lastDataLen=0 → FinalRange returns 0)
if got := dec.FinalRange(); got != 0 {
t.Errorf("FinalRange() default = %d, want 0", got)
}
// OPUS_GET_PITCH default = 0 (prev_mode=0, not MODE_CELT_ONLY, silk signal
// type not VOICED)
if got := dec.Pitch(); got != 0 {
t.Errorf("Pitch() default = %d, want 0", got)
}
// OPUS_GET_PHASE_INVERSION_DISABLED: mono decoder always has phase
// inversion disabled in gopus (libopus enforces mono constraint in CELT).
// Stereo default is false.
stereo := mustNewTestDecoder(t, 48000, 2)
if stereo.PhaseInversionDisabled() {
t.Errorf("stereo PhaseInversionDisabled() default = true, want false")
}
// OPUS_GET_IGNORE_EXTENSIONS default = false (OPUS_CLEAR zeroes the struct)
if dec.IgnoreExtensions() {
t.Errorf("IgnoreExtensions() default = true, want false")
}
// OPUS_GET_SAMPLE_RATE
for _, rate := range []int{8000, 12000, 16000, 24000, 48000} {
d := mustNewTestDecoder(t, rate, 1)
if got := d.SampleRate(); got != rate {
t.Errorf("SampleRate() = %d, want %d", got, rate)
}
}
// Channels is not a CTL but a constructor parameter.
if got := dec.Channels(); got != 1 {
t.Errorf("Channels() = %d, want 1", got)
}
}
// ---------------------------------------------------------------------------
// CTL round-trip: SET then GET must return the value just set.
// ---------------------------------------------------------------------------
// TestDecoderCTL_GainRoundTrip verifies OPUS_SET_GAIN / OPUS_GET_GAIN
// round-trips the full valid range boundary values.
//
// C ref: opus_decoder_ctl OPUS_SET_GAIN_REQUEST – range [-32768, 32767].
func TestDecoderCTL_GainRoundTrip(t *testing.T) {
dec := mustNewTestDecoder(t, 48000, 1)
for _, gain := range []int{-32768, -1, 0, 1, 256, 32767} {
if err := dec.SetGain(gain); err != nil {
t.Fatalf("SetGain(%d) unexpected error: %v", gain, err)
}
if got := dec.Gain(); got != gain {
t.Errorf("Gain() = %d after SetGain(%d), want %d", got, gain, gain)
}
}
}
// TestDecoderCTL_GainBoundaryReject verifies out-of-range gain returns an error
// and does not change the stored value.
//
// C ref: opus_decoder_ctl OPUS_SET_GAIN_REQUEST – "if (value<-32768 || value>32767) goto bad_arg"
func TestDecoderCTL_GainBoundaryReject(t *testing.T) {
dec := mustNewTestDecoder(t, 48000, 1)
if err := dec.SetGain(0); err != nil {
t.Fatalf("SetGain(0) error: %v", err)
}
for _, gain := range []int{-32769, 32768, -100000, 100000} {
err := dec.SetGain(gain)
if err == nil {
t.Errorf("SetGain(%d) should return error, got nil", gain)
}
// Value must not have changed.
if got := dec.Gain(); got != 0 {
t.Errorf("SetGain(%d) changed Gain() to %d, want 0", gain, got)
}
}
}
// TestDecoderCTL_ComplexityRoundTrip verifies OPUS_SET_COMPLEXITY /
// OPUS_GET_COMPLEXITY round-trips all valid values [0,10].
//
// C ref: opus_decoder_ctl OPUS_SET_COMPLEXITY_REQUEST – "if(value<0 || value>10) goto bad_arg"
func TestDecoderCTL_ComplexityRoundTrip(t *testing.T) {
dec := mustNewTestDecoder(t, 48000, 1)
for c := 0; c <= 10; c++ {
if err := dec.SetComplexity(c); err != nil {
t.Fatalf("SetComplexity(%d) error: %v", c, err)
}
if got := dec.Complexity(); got != c {
t.Errorf("Complexity() = %d after SetComplexity(%d)", got, c)
}
}
}
// TestDecoderCTL_ComplexityBoundaryReject verifies out-of-range complexity is
// rejected.
//
// C ref: opus_decoder_ctl OPUS_SET_COMPLEXITY_REQUEST – "if(value<0 || value>10) goto bad_arg"
func TestDecoderCTL_ComplexityBoundaryReject(t *testing.T) {
dec := mustNewTestDecoder(t, 48000, 1)
if err := dec.SetComplexity(5); err != nil {
t.Fatalf("SetComplexity(5) error: %v", err)
}
for _, c := range []int{-1, 11, 100} {
err := dec.SetComplexity(c)
if err == nil {
t.Errorf("SetComplexity(%d) should return error, got nil", c)
}
if got := dec.Complexity(); got != 5 {
t.Errorf("invalid SetComplexity(%d) changed Complexity() to %d, want 5", c, got)
}
}
}
// TestDecoderCTL_PhaseInversionDisabledRoundTrip verifies
// OPUS_SET/GET_PHASE_INVERSION_DISABLED on a stereo decoder.
//
// C ref: opus_decoder_ctl OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST –
//
// "if(value<0 || value>1) goto bad_arg"
func TestDecoderCTL_PhaseInversionDisabledRoundTrip(t *testing.T) {
dec := mustNewTestDecoder(t, 48000, 2)
dec.SetPhaseInversionDisabled(true)
if !dec.PhaseInversionDisabled() {
t.Error("PhaseInversionDisabled() = false after Set(true), want true")
}
dec.SetPhaseInversionDisabled(false)
if dec.PhaseInversionDisabled() {
t.Error("PhaseInversionDisabled() = true after Set(false), want false")
}
}
// TestDecoderCTL_IgnoreExtensionsRoundTrip verifies
// OPUS_SET/GET_IGNORE_EXTENSIONS round-trip.
//
// C ref: opus_decoder_ctl OPUS_SET_IGNORE_EXTENSIONS_REQUEST –
//
// "if(value<0 || value>1) goto bad_arg"
func TestDecoderCTL_IgnoreExtensionsRoundTrip(t *testing.T) {
dec := mustNewTestDecoder(t, 48000, 1)
dec.SetIgnoreExtensions(true)
if !dec.IgnoreExtensions() {
t.Error("IgnoreExtensions() = false after Set(true), want true")
}
dec.SetIgnoreExtensions(false)
if dec.IgnoreExtensions() {
t.Error("IgnoreExtensions() = true after Set(false), want false")
}
}
// TestDecoderCTL_LastPacketDurationAfterDecode verifies
// OPUS_GET_LAST_PACKET_DURATION equals the decoded sample count after a real
// packet.
//
// C ref: opus_decode_native – "st->last_packet_duration = nb_samples"
func TestDecoderCTL_LastPacketDurationAfterDecode(t *testing.T) {
dec := mustNewTestDecoder(t, 48000, 1)
pcm := make([]float32, 960)
n, err := dec.Decode(minimalHybridTestPacket20ms(), pcm)
if err != nil {
t.Fatalf("Decode error: %v", err)
}
if got := dec.LastPacketDuration(); got != n {
t.Errorf("LastPacketDuration() = %d after Decode returning %d, want equal", got, n)
}
}
// TestDecoderCTL_BandwidthAfterDecode verifies OPUS_GET_BANDWIDTH returns the
// bandwidth of the last decoded packet.
//
// C ref: opus_decode_native – "st->bandwidth = packet_bandwidth"
func TestDecoderCTL_BandwidthAfterDecode(t *testing.T) {
dec := mustNewTestDecoder(t, 48000, 1)
// minimalHybridTestPacket20ms is Hybrid Fullband (config 15).
if _, err := dec.Decode(minimalHybridTestPacket20ms(), make([]float32, 960)); err != nil {
t.Fatalf("Decode error: %v", err)
}
if got := dec.Bandwidth(); got != BandwidthFullband {
t.Errorf("Bandwidth() = %v after Hybrid-FB decode, want BandwidthFullband", got)
}
}
// TestDecoderCTL_SampleRateAllRates verifies OPUS_GET_SAMPLE_RATE returns the
// configured API rate for all valid sample rates.
//
// C ref: opus_decoder_ctl OPUS_GET_SAMPLE_RATE_REQUEST – "*value = st->Fs"
func TestDecoderCTL_SampleRateAllRates(t *testing.T) {
for _, rate := range []int{8000, 12000, 16000, 24000, 48000} {
dec := mustNewTestDecoder(t, rate, 1)
if got := dec.SampleRate(); got != rate {
t.Errorf("SampleRate() = %d, want %d", got, rate)
}
}
}
// TestDecoderCTL_FinalRangeZeroBeforeDecode verifies FinalRange returns 0
// before any packet has been decoded.
//
// C ref: opus_decoder.c FinalRange – "if lastDataLen <= 1 return 0"
func TestDecoderCTL_FinalRangeZeroBeforeDecode(t *testing.T) {
dec := mustNewTestDecoder(t, 48000, 1)
if got := dec.FinalRange(); got != 0 {
t.Errorf("FinalRange() = %d before any decode, want 0", got)
}
}
// TestDecoderCTL_FinalRangeNonZeroAfterDecode verifies FinalRange returns a
// non-zero value after successfully decoding a real packet.
//
// C ref: opus_decoder.c – "st->rangeFinal" is set from the range coder state
//
// after opus_decode_frame (celt_decoder_ctl OPUS_GET_FINAL_RANGE).
func TestDecoderCTL_FinalRangeNonZeroAfterDecode(t *testing.T) {
dec := mustNewTestDecoder(t, 48000, 1)
pcm := make([]float32, 960)
if _, err := dec.Decode(minimalHybridTestPacket20ms(), pcm); err != nil {
t.Fatalf("Decode error: %v", err)
}
if got := dec.FinalRange(); got == 0 {
t.Error("FinalRange() = 0 after decoding real packet, want non-zero")
}
}
// TestDecoderCTL_ResetPreservesComplexity verifies that Reset() does not clear
// the complexity setting (matching libopus: opus_decoder_ctl OPUS_RESET_STATE
// uses OPUS_CLEAR over the RESET_START range, which excludes complexity from
// its reset because st->complexity is inside the preserved region).
//
// C ref: opus_decoder.c OPUS_RESET_STATE case – OPUS_CLEAR starts at
//
// &st->OPUS_DECODER_RESET_START, which is after st->complexity.
func TestDecoderCTL_ResetPreservesComplexity(t *testing.T) {
dec := mustNewTestDecoder(t, 48000, 1)
if err := dec.SetComplexity(7); err != nil {
t.Fatalf("SetComplexity(7) error: %v", err)
}
dec.Reset()
if got := dec.Complexity(); got != 7 {
t.Errorf("Complexity() = %d after Reset(), want 7 (complexity is preserved by libopus reset)", got)
}
}
// TestDecoderCTL_ResetPreservesGain verifies that Reset() preserves the
// decode gain, matching libopus OPUS_RESET_STATE behavior.
//
// C ref: opus_decoder.c struct layout – decode_gain is at line 71, before
//
// "OPUS_DECODER_RESET_START stream_channels" (line 80). OPUS_RESET_STATE
// clears only from stream_channels onward, so decode_gain survives reset.
// Complexity (line 72) is likewise preserved (see TestDecoderCTL_ResetPreservesComplexity).
func TestDecoderCTL_ResetPreservesGain(t *testing.T) {
dec := mustNewTestDecoder(t, 48000, 1)
if err := dec.SetGain(256); err != nil {
t.Fatalf("SetGain(256) error: %v", err)
}
dec.Reset()
if got := dec.Gain(); got != 256 {
t.Errorf("Gain() = %d after Reset(), want 256 (libopus preserves decode_gain across OPUS_RESET_STATE)", got)
}
}
// TestDecoderCTL_ResetClearsLastPacketDuration verifies Reset() zeroes
// LastPacketDuration.
func TestDecoderCTL_ResetClearsLastPacketDuration(t *testing.T) {
dec := mustNewTestDecoder(t, 48000, 1)
if _, err := dec.Decode(minimalHybridTestPacket20ms(), make([]float32, 960)); err != nil {
t.Fatalf("Decode error: %v", err)
}
if dec.LastPacketDuration() == 0 {
t.Fatal("LastPacketDuration() = 0 after valid decode – precondition failed")
}
dec.Reset()
if got := dec.LastPacketDuration(); got != 0 {
t.Errorf("LastPacketDuration() = %d after Reset(), want 0", got)
}
}
// ---------------------------------------------------------------------------
// Error-code equivalence for invalid packets.
// ---------------------------------------------------------------------------
// libopus maps packet parsing failures to OPUS_INVALID_PACKET (-4) and buffer
// sizing failures to OPUS_BUFFER_TOO_SMALL (-2). gopus uses ErrInvalidPacket
// and ErrBufferTooSmall. The tests below feed known-bad packets and confirm
// the gopus error matches the libopus classification.
// TestDecodeErrorCode_ZeroLengthPacket verifies that an empty (zero-length)
// byte slice returns ErrInvalidPacket.
//
// C ref: opus_decode_native – non-nil data with len==0 takes the PLC branch,
//
// but when called from opus_decode_float with data!=nil it would have
// attempted get_nb_samples first; however gopus (like libopus float path)
// treats an empty non-nil slice as a zero-length data → PLC, not an error.
// A truly absent packet (nil) is PLC. A single-byte (TOC-only) packet with
// no frame data is handled by opus_packet_parse_impl returning count≥0, and
// the single frame has zero bytes, which is valid for the CELT/SILK layer.
//
// gopus mirrors libopus: empty non-nil data decodes as PLC (no error).
func TestDecodeErrorCode_ZeroLengthPacket(t *testing.T) {
dec := mustNewTestDecoder(t, 48000, 1)
pcm := make([]float32, 960)
// nil data → PLC, not error
_, err := dec.Decode(nil, pcm)
if err != nil {
t.Errorf("Decode(nil) error = %v, want nil (PLC)", err)
}
// empty non-nil data → PLC (matches libopus len==0 branch)
_, err = dec.Decode([]byte{}, pcm)
if err != nil {
t.Errorf("Decode([]byte{}) error = %v, want nil (PLC)", err)
}
}
// TestDecodeErrorCode_TruncatedCode3 verifies that a code-3 packet that is
// only 1 byte (missing the frame-count byte) is rejected.
//
// C ref: opus_packet_parse_impl – code==3 requires at least 2 bytes; returns
//
// OPUS_INVALID_PACKET when len<2.
func TestDecodeErrorCode_TruncatedCode3(t *testing.T) {
dec := mustNewTestDecoder(t, 48000, 1)
pcm := make([]float32, 5760)
// One-byte code-3 packet (missing frame-count byte).
_, err := dec.Decode([]byte{GenerateTOC(16, false, 3)}, pcm)
if err == nil {
t.Error("Decode(truncated code-3) = nil, want error")
}
}
// TestDecodeErrorCode_Code3ZeroFrames verifies that M=0 in a code-3 packet
// is rejected.
//
// C ref: opus_packet_parse_impl – "if (count==0) return OPUS_INVALID_PACKET"
// gopus: ErrInvalidFrameCount ("M > 48" message covers M==0 via m==0 check)
func TestDecodeErrorCode_Code3ZeroFrames(t *testing.T) {
dec := mustNewTestDecoder(t, 48000, 1)
pcm := make([]float32, 5760)
// Code-3 packet with M=0 in the frame-count byte.
pkt := []byte{GenerateTOC(16, false, 3), 0x00}
_, err := dec.Decode(pkt, pcm)
if err == nil {
t.Error("Decode(code-3 M=0) = nil, want error")
}
}
// TestDecodeErrorCode_Code3ExcessiveFrames verifies M>48 is rejected.
//
// C ref: opus_packet_parse_impl – if (count > 48) return OPUS_INVALID_PACKET
func TestDecodeErrorCode_Code3ExcessiveFrames(t *testing.T) {
dec := mustNewTestDecoder(t, 48000, 1)
pcm := make([]float32, 5760)
// Code-3 packet with M=49 (0x31 = 0b0110001 = CBR, no-padding, 49 frames).
pkt := []byte{GenerateTOC(16, false, 3), 49}
_, err := dec.Decode(pkt, pcm)
if err == nil {
t.Error("Decode(code-3 M=49) = nil, want error")
}
}
// TestDecodeErrorCode_TotalDurationOver120ms verifies that a packet whose
// total duration exceeds 120ms is rejected.
//
// C ref: opus_packet_get_nb_samples – "if (samples*25 > Fs*3) return
//
// OPUS_INVALID_PACKET"
func TestDecodeErrorCode_TotalDurationOver120ms(t *testing.T) {
dec := mustNewTestDecoder(t, 48000, 1)
// Config 31 = CELT FB 20ms; code 3 with M=7 → 140ms > 120ms.
pkt := []byte{GenerateTOC(31, false, 3), 0x07}
pcm := make([]float32, 7000)
_, err := dec.Decode(pkt, pcm)
if err == nil {
t.Error("Decode(>120ms) = nil, want error")
}
}
// TestDecodeErrorCode_BufferTooSmall verifies OPUS_BUFFER_TOO_SMALL when the
// output PCM buffer is shorter than the packet requires.
//
// C ref: opus_decode_native – "if (count*packet_frame_size > frame_size)
//
// return OPUS_BUFFER_TOO_SMALL"
func TestDecodeErrorCode_BufferTooSmall(t *testing.T) {
dec := mustNewTestDecoder(t, 48000, 1)
// minimalHybridTestPacket20ms() is 960 samples; give only 480.
pcm := make([]float32, 480)
_, err := dec.Decode(minimalHybridTestPacket20ms(), pcm)
if err != ErrBufferTooSmall {
t.Errorf("Decode with short buffer = %v, want ErrBufferTooSmall", err)
}
}
// TestDecodeErrorCode_Code1OddPayload verifies that a code-1 packet with an
// odd payload length is rejected (two equal-length frames require an even
// total payload).
//
// C ref: opus_packet_parse_impl code==1 branch – "if (framesize & 1)
//
// return OPUS_INVALID_PACKET"
func TestDecodeErrorCode_Code1OddPayload(t *testing.T) {
dec := mustNewTestDecoder(t, 48000, 1)
pcm := make([]float32, 960*2)
// Config 16 (CELT NB 2.5ms), code 1, 3 payload bytes (odd).
pkt := []byte{GenerateTOC(16, false, 1), 0xAA, 0xBB, 0xCC}
_, err := dec.Decode(pkt, pcm)
if err == nil {
t.Error("Decode(code-1 odd payload) = nil, want error")
}
}
// TestDecodeErrorCode_Code2ShortPacket verifies that a code-2 packet with
// insufficient bytes for the first frame length is rejected.
//
// C ref: opus_packet_parse_impl code==2 branch checks parsed frame1Len
//
// against the remaining packet bytes.
func TestDecodeErrorCode_Code2ShortPacket(t *testing.T) {
dec := mustNewTestDecoder(t, 48000, 1)
pcm := make([]float32, 960*2)
// TOC byte only, no frame-length byte for code-2.
pkt := []byte{GenerateTOC(16, false, 2)}
_, err := dec.Decode(pkt, pcm)
if err == nil {
t.Error("Decode(code-2 truncated) = nil, want error")
}
}
// TestDecodeErrorCode_Code2Frame1TooLarge verifies that a code-2 packet
// where the encoded first-frame length exceeds the packet remainder is
// rejected.
//
// C ref: opus_packet_parse_impl code==2 – frame2 length becomes negative →
//
// OPUS_INVALID_PACKET.
func TestDecodeErrorCode_Code2Frame1TooLarge(t *testing.T) {
dec := mustNewTestDecoder(t, 48000, 1)
pcm := make([]float32, 960*2)
// TOC (code-2) + frame1-length byte 200 + 3 bytes payload (200 > 3).
pkt := []byte{GenerateTOC(16, false, 2), 200, 0xAA, 0xBB, 0xCC}
_, err := dec.Decode(pkt, pcm)
if err == nil {
t.Error("Decode(code-2 frame1 too large) = nil, want error")
}
}
// TestDecodeErrorCode_Code3CBRUneven verifies that a CBR code-3 packet whose
// total frame bytes are not divisible by M is rejected.
//
// C ref: opus_packet_parse_impl code==3 CBR branch –
//
// "if (framesize % count) return OPUS_INVALID_PACKET"
func TestDecodeErrorCode_Code3CBRUneven(t *testing.T) {
dec := mustNewTestDecoder(t, 48000, 1)
pcm := make([]float32, 960*3)
// Code-3, CBR, M=3, no padding: 2.5ms CELT NB config, payload 5 bytes
// (5 % 3 != 0 → invalid).
frameCountByte := byte(3) // CBR (bit7=0), no padding (bit6=0), M=3
pkt := append([]byte{GenerateTOC(16, false, 3), frameCountByte}, make([]byte, 5)...)
_, err := dec.Decode(pkt, pcm)
if err == nil {
t.Error("Decode(code-3 CBR uneven) = nil, want error")
}
}
// TestDecodeErrorCode_Int16BufferTooSmall verifies ErrBufferTooSmall is
// returned from DecodeInt16 when the output int16 buffer is short.
func TestDecodeErrorCode_Int16BufferTooSmall(t *testing.T) {
dec := mustNewTestDecoder(t, 48000, 1)
pcm := make([]int16, 480)
_, err := dec.DecodeInt16(minimalHybridTestPacket20ms(), pcm)
if err != ErrBufferTooSmall {
t.Errorf("DecodeInt16 with short buffer = %v, want ErrBufferTooSmall", err)
}
}
// TestDecodeErrorCode_InvalidPacketPropagatesFromInt16 verifies that
// ErrInvalidPacket is returned from DecodeInt16 for a malformed packet.
func TestDecodeErrorCode_InvalidPacketPropagatesFromInt16(t *testing.T) {
dec := mustNewTestDecoder(t, 48000, 1)
pcm := make([]int16, 5760)
// Code-3 M=7 with 20ms config → >120ms
_, err := dec.DecodeInt16([]byte{GenerateTOC(31, false, 3), 0x07}, pcm)
if err == nil {
t.Error("DecodeInt16(>120ms) = nil, want error")
}
}