Repository navigation
Expand file tree
/
Copy pathdecoder_bitexact_test.go
More file actions
169 lines (160 loc) · 5.61 KB
/
Copy pathdecoder_bitexact_test.go
File metadata and controls
169 lines (160 loc) · 5.61 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
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
//nolint:cyclop,tagliatelle // Exact reference fixtures retain their canonical JSON field names.
package opus
import (
"encoding/hex"
"encoding/json"
"fmt"
"math"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// The reference generator verifies that float decoding followed by lrintf
// matches its int16 API for this unclipped first frame only.
func TestDecoderFirstFrameFloatReference(t *testing.T) {
corpus := loadPLCCorpus(t)
data, err := os.ReadFile("testdata/short-plc/first-float.json")
require.NoError(t, err)
var reference struct {
Pin string `json:"pin"`
Bits []uint32 `json:"bits"`
}
require.NoError(t, json.Unmarshal(data, &reference))
require.Equal(t, corpus.Pin, reference.Pin)
frame := corpus.Cases[0].Steps[0]
packet, err := hex.DecodeString(frame.Packet)
require.NoError(t, err)
decoder, err := NewDecoderWithOutput(8000, 1)
require.NoError(t, err)
output := make([]float32, frame.Samples)
count, err := decoder.DecodeToFloat32(packet, output)
require.NoError(t, err)
require.Equal(t, frame.Samples, count)
require.Len(t, reference.Bits, count)
first := -1
unequal := 0
for i, sample := range output {
if math.Float32bits(sample) != reference.Bits[i] {
unequal++
if first < 0 {
first = i
}
}
}
if first >= 0 {
assert.Failf(t, "float mismatch", "%d samples; first=%d got=%08x want=%08x; sample 28 scaled got=%.9g want=%.9g",
unequal, first, math.Float32bits(output[first]), reference.Bits[first],
output[28]*32768, math.Float32frombits(reference.Bits[28])*32768)
}
}
func TestDecoderNoLossFloatSequenceReference(t *testing.T) {
corpus := loadPLCCorpus(t)
data, err := os.ReadFile("testdata/short-plc/no-loss-float.json")
require.NoError(t, err)
var reference struct {
Pin string
Cases []struct {
Signal int
Channels int
OutputChannels int `json:"output_channels"`
Rate int
Mode int
Sequence int
Frames [][]uint32
}
}
require.NoError(t, json.Unmarshal(data, &reference))
require.Equal(t, corpus.Pin, reference.Pin)
require.Len(t, reference.Cases, 4)
for _, referenceCase := range reference.Cases {
name := fmt.Sprintf("%dto%d/%d/sequence%d", referenceCase.Channels,
referenceCase.OutputChannels, referenceCase.Rate, referenceCase.Sequence)
t.Run(name, func(t *testing.T) {
scenarioIndex := -1
for i := range corpus.Cases {
candidate := corpus.Cases[i]
if candidate.Mode == referenceCase.Mode && candidate.Signal == referenceCase.Signal &&
candidate.Channels == referenceCase.Channels &&
candidate.OutputChannels == referenceCase.OutputChannels && candidate.Rate == referenceCase.Rate &&
candidate.Sequence == referenceCase.Sequence {
scenarioIndex = i
break
}
}
require.NotEqual(t, -1, scenarioIndex)
scenario := corpus.Cases[scenarioIndex]
require.NotEmpty(t, referenceCase.Frames)
require.LessOrEqual(t, len(referenceCase.Frames), len(scenario.Steps))
decoder, decoderErr := NewDecoderWithOutput(scenario.Rate, scenario.OutputChannels)
require.NoError(t, decoderErr)
for step, expectedFrame := range referenceCase.Frames {
frame := scenario.Steps[step]
packet, decodeErr := hex.DecodeString(frame.Packet)
require.NoError(t, decodeErr)
output := make([]float32, frame.Samples*scenario.OutputChannels)
count, decodeErr := decoder.DecodeToFloat32(packet, output)
require.NoError(t, decodeErr)
require.Equal(t, frame.Samples, count)
require.Len(t, expectedFrame, len(output))
for sample, actual := range output {
expected := expectedFrame[sample]
if math.Float32bits(actual) != expected {
assert.Failf(t, "float mismatch", "step=%d channel=%d sample=%d got=%08x want=%08x",
step, sample%scenario.OutputChannels, sample/scenario.OutputChannels,
math.Float32bits(actual), expected)
return
}
}
}
})
}
}
func TestDecoderReferenceInt16Rounding(t *testing.T) {
for _, test := range []struct {
name string
scaled float32
expected int16
}{
{"positive even tie", 1014.5, 1014},
{"positive odd tie", 1015.5, 1016},
{"negative even tie", -734.5, -734},
{"negative odd tie", -733.5, -734},
{"positive saturation", 32768, 32767},
{"negative saturation", -32769, -32768},
} {
t.Run(test.name, func(t *testing.T) {
require.Equal(t, test.expected, decoderFloat32ToSigned16(test.scaled/32768))
})
}
}
func TestSILKFirstFrameStagesReference(t *testing.T) {
corpus := loadPLCCorpus(t)
data, err := os.ReadFile("testdata/short-plc/silk-stage.json")
require.NoError(t, err)
var reference struct {
Pin string
PreResample []int16 `json:"pre_resample"`
PostResample []int16 `json:"post_resample"`
}
require.NoError(t, json.Unmarshal(data, &reference))
require.Equal(t, corpus.Pin, reference.Pin)
scenario := corpus.Cases[490]
packet, err := hex.DecodeString(scenario.Steps[0].Packet)
require.NoError(t, err)
decoder, err := NewDecoderWithOutput(16000, 1)
require.NoError(t, err)
output := make([]float32, scenario.Steps[0].Samples)
count, err := decoder.DecodeToFloat32(packet, output)
require.NoError(t, err)
require.Equal(t, scenario.Steps[0].Samples, count)
require.Len(t, reference.PreResample, len(decoder.silkBuffer))
for i, sample := range decoder.silkBuffer {
require.Equal(t, reference.PreResample[i], decoderFloat32ToSigned16(sample), "pre-resample index %d", i)
}
for i, sample := range output {
require.Equal(t, reference.PostResample[i], decoderFloat32ToSigned16(sample), "post-resample index %d", i)
}
}