From 605a6b2b280c0b2d555f0e2fad56b289361ce29c Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Sun, 07 Jun 2026 11:28:52 +0800
Subject: [PATCH] Add X-ASR live captions

---
 privatevoice.src/internal/audio/recorder.go |   45 ++++++++++++++++++++++++++++++++++++---------
 1 files changed, 36 insertions(+), 9 deletions(-)

diff --git a/privatevoice.src/internal/audio/recorder.go b/privatevoice.src/internal/audio/recorder.go
index bef4072..5842248 100755
--- a/privatevoice.src/internal/audio/recorder.go
+++ b/privatevoice.src/internal/audio/recorder.go
@@ -165,16 +165,30 @@
 		return nil
 	}
 
-	// Convert 16-bit PCM to float32
-	numSamples := len(pcm) / 2
-	samples := make([]float32, numSamples)
-	for i := 0; i < numSamples; i++ {
-		sample := int16(pcm[i*2]) | int16(pcm[i*2+1])<<8
-		samples[i] = float32(sample) / 32768.0
-	}
-
-	logger.Info("Recording stopped, %d samples captured", numSamples)
+	samples := pcm16LEToFloat32(pcm)
+	logger.Info("Recording stopped, %d samples captured", len(samples))
 	return samples
+}
+
+// ReadSamplesSince returns newly captured samples after sampleOffset and the
+// next offset to pass on the following call. It is safe to call while recording.
+func (r *Recorder) ReadSamplesSince(sampleOffset int) ([]float32, int) {
+	r.mu.Lock()
+	totalSamples := len(r.pcmBuf) / 2
+	if sampleOffset < 0 {
+		sampleOffset = 0
+	}
+	if sampleOffset > totalSamples {
+		sampleOffset = totalSamples
+	}
+	startByte := sampleOffset * 2
+	pcm := append([]byte(nil), r.pcmBuf[startByte:]...)
+	r.mu.Unlock()
+
+	if len(pcm) < 2 {
+		return nil, totalSamples
+	}
+	return pcm16LEToFloat32(pcm), totalSamples
 }
 
 // HasVoiceActivity returns true if the max volume exceeded the silence threshold.
@@ -260,6 +274,19 @@
 	logger.Info("Audio device uninitialized")
 }
 
+func pcm16LEToFloat32(pcm []byte) []float32 {
+	numSamples := len(pcm) / 2
+	if numSamples == 0 {
+		return nil
+	}
+	samples := make([]float32, numSamples)
+	for i := 0; i < numSamples; i++ {
+		sample := int16(pcm[i*2]) | int16(pcm[i*2+1])<<8
+		samples[i] = float32(sample) / 32768.0
+	}
+	return samples
+}
+
 func (r *Recorder) onData(input []byte) {
 	r.mu.Lock()
 

--
Gitblit v1.9.3