Skip to content

Commit 380f7f3

Browse files
xaionaro@dx.centerxaionaro@dx.center
authored andcommitted
Add device-side E2E audio recording test with Goertzel
Records 1s of 48kHz audio via AAudio input stream and verifies the captured signal contains a dominant 440Hz tone.
1 parent 43a3990 commit 380f7f3

1 file changed

Lines changed: 121 additions & 0 deletions

File tree

  • tests/e2e/audio-recording-e2e
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// E2E test: records audio from AAudio input stream at 48 kHz and verifies
2+
// the captured signal contains a 440 Hz tone using the Goertzel algorithm.
3+
//
4+
// Exit 0 = PASS (440 Hz detected), exit 1 = FAIL.
5+
package main
6+
7+
import (
8+
"fmt"
9+
"math"
10+
"os"
11+
"unsafe"
12+
13+
"github.com/xaionaro-go/ndk/audio"
14+
)
15+
16+
// goertzelMagnitude computes the magnitude of a specific frequency in
17+
// a buffer of int16 PCM samples using the Goertzel algorithm.
18+
func goertzelMagnitude(samples []int16, targetFreq float64, sampleRate float64) float64 {
19+
n := len(samples)
20+
k := int(0.5 + float64(n)*targetFreq/sampleRate)
21+
w := 2.0 * math.Pi * float64(k) / float64(n)
22+
coeff := 2.0 * math.Cos(w)
23+
24+
var s0, s1, s2 float64
25+
for _, sample := range samples {
26+
s0 = coeff*s1 - s2 + float64(sample)
27+
s2 = s1
28+
s1 = s0
29+
}
30+
31+
return s1*s1 + s2*s2 - coeff*s1*s2
32+
}
33+
34+
func main() {
35+
builder, err := audio.NewStreamBuilder()
36+
if err != nil {
37+
fmt.Fprintf(os.Stderr, "FAIL: create builder: %v\n", err)
38+
os.Exit(1)
39+
}
40+
defer builder.Close()
41+
42+
builder.
43+
SetDirection(audio.Input).
44+
SetSampleRate(48000).
45+
SetChannelCount(1).
46+
SetFormat(audio.PcmI16).
47+
SetPerformanceMode(audio.LowLatency).
48+
SetSharingMode(audio.Shared)
49+
50+
stream, err := builder.Open()
51+
if err != nil {
52+
fmt.Fprintf(os.Stderr, "FAIL: open stream: %v\n", err)
53+
os.Exit(1)
54+
}
55+
defer stream.Close()
56+
57+
actualRate := float64(stream.SampleRate())
58+
fmt.Printf("stream opened: rate=%.0f Hz, channels=%d\n", actualRate, stream.ChannelCount())
59+
60+
if err := stream.Start(); err != nil {
61+
fmt.Fprintf(os.Stderr, "FAIL: start stream: %v\n", err)
62+
os.Exit(1)
63+
}
64+
65+
// Read ~1 second of audio.
66+
totalFrames := int32(actualRate)
67+
buf := make([]int16, 1024)
68+
var captured []int16
69+
70+
for int32(len(captured)) < totalFrames {
71+
framesToRead := int32(len(buf))
72+
if remaining := totalFrames - int32(len(captured)); remaining < framesToRead {
73+
framesToRead = remaining
74+
}
75+
n, err := stream.Read(unsafe.Pointer(&buf[0]), framesToRead, 1_000_000_000)
76+
if err != nil {
77+
fmt.Fprintf(os.Stderr, "FAIL: read: %v\n", err)
78+
os.Exit(1)
79+
}
80+
if n == 0 {
81+
fmt.Fprintf(os.Stderr, "FAIL: read returned 0 frames\n")
82+
os.Exit(1)
83+
}
84+
captured = append(captured, buf[:n]...)
85+
}
86+
87+
stream.Stop()
88+
89+
fmt.Printf("captured %d frames\n", len(captured))
90+
91+
// Goertzel: check 440 Hz vs a few other frequencies.
92+
targetFreq := 440.0
93+
targetMag := goertzelMagnitude(captured, targetFreq, actualRate)
94+
95+
// Check energy at non-target frequencies for comparison.
96+
otherFreqs := []float64{200, 600, 1000, 2000, 5000}
97+
var maxOtherMag float64
98+
for _, f := range otherFreqs {
99+
mag := goertzelMagnitude(captured, f, actualRate)
100+
fmt.Printf(" %.0f Hz magnitude: %.2e\n", f, mag)
101+
if mag > maxOtherMag {
102+
maxOtherMag = mag
103+
}
104+
}
105+
fmt.Printf(" 440 Hz magnitude: %.2e\n", targetMag)
106+
fmt.Printf(" max other magnitude: %.2e\n", maxOtherMag)
107+
108+
// Pass criteria:
109+
// 1. 440 Hz energy must be significantly above noise floor.
110+
// 2. 440 Hz must be at least 10x stronger than any other checked frequency.
111+
if targetMag < 1e10 {
112+
fmt.Fprintf(os.Stderr, "FAIL: 440 Hz energy too low (%.2e < 1e10)\n", targetMag)
113+
os.Exit(1)
114+
}
115+
if maxOtherMag > 0 && targetMag/maxOtherMag < 10 {
116+
fmt.Fprintf(os.Stderr, "FAIL: 440 Hz not dominant (ratio=%.1f, need >=10)\n", targetMag/maxOtherMag)
117+
os.Exit(1)
118+
}
119+
120+
fmt.Println("PASS: 440 Hz tone detected")
121+
}

0 commit comments

Comments
 (0)