Fix ESP32 audio dropouts and improve streaming reliability
FLAC encoder now tracks frame numbers and reuses per-channel sample buffers to reduce allocations. Client dialer always frees the active slot on release so mDNS re-emissions can reconnect cleanly. Server send buffer uses drop-oldest instead of hard failure, with lateness tracking. Added engine stats logging (chunks/sec, kbps) and writer diagnostics. UI simplified to a single play/pause toggle that auto-starts playback on preset selection. Added --preset CLI flag for headless startup. Removed stale systemd service files. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,8 @@ type FLACEncoder struct {
|
||||
channels int
|
||||
bitDepth int
|
||||
blockSize int
|
||||
frameNum uint64
|
||||
chanBufs [][]int32 // reused per-channel sample buffers
|
||||
}
|
||||
|
||||
// NewFLACEncoder creates a FLAC encoder with prediction analysis enabled.
|
||||
@@ -54,6 +56,11 @@ func NewFLACEncoder(sampleRate, channels, bitDepth, blockSize int) (*FLACEncoder
|
||||
copy(codecHeader, buf.Bytes())
|
||||
buf.Reset()
|
||||
|
||||
chanBufs := make([][]int32, channels)
|
||||
for ch := range chanBufs {
|
||||
chanBufs[ch] = make([]int32, blockSize)
|
||||
}
|
||||
|
||||
return &FLACEncoder{
|
||||
encoder: enc,
|
||||
buf: buf,
|
||||
@@ -62,6 +69,7 @@ func NewFLACEncoder(sampleRate, channels, bitDepth, blockSize int) (*FLACEncoder
|
||||
channels: channels,
|
||||
bitDepth: bitDepth,
|
||||
blockSize: blockSize,
|
||||
chanBufs: chanBufs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -92,20 +100,18 @@ func (e *FLACEncoder) Encode(samples []int32) ([]byte, error) {
|
||||
shift = uint(24 - e.bitDepth)
|
||||
}
|
||||
|
||||
// De-interleave into per-channel slices.
|
||||
// De-interleave into pre-allocated per-channel buffers.
|
||||
subframes := make([]*frame.Subframe, e.channels)
|
||||
for ch := 0; ch < e.channels; ch++ {
|
||||
channelSamples := make([]int32, e.blockSize)
|
||||
buf := e.chanBufs[ch]
|
||||
for i := 0; i < e.blockSize; i++ {
|
||||
channelSamples[i] = samples[i*e.channels+ch] >> shift
|
||||
buf[i] = samples[i*e.channels+ch] >> shift
|
||||
}
|
||||
subframes[ch] = &frame.Subframe{
|
||||
SubHeader: frame.SubHeader{
|
||||
// PredVerbatim signals the encoder to run prediction analysis
|
||||
// and pick the optimal method (Constant, Fixed, or Verbatim).
|
||||
Pred: frame.PredVerbatim,
|
||||
},
|
||||
Samples: channelSamples,
|
||||
Samples: buf,
|
||||
NSamples: e.blockSize,
|
||||
}
|
||||
}
|
||||
@@ -127,9 +133,11 @@ func (e *FLACEncoder) Encode(samples []int32) ([]byte, error) {
|
||||
SampleRate: uint32(e.sampleRate),
|
||||
Channels: channelAssign,
|
||||
BitsPerSample: uint8(e.bitDepth),
|
||||
Num: e.frameNum,
|
||||
},
|
||||
Subframes: subframes,
|
||||
}
|
||||
e.frameNum++
|
||||
|
||||
e.buf.Reset()
|
||||
if err := e.encoder.WriteFrame(f); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user