Files

58 lines
2.3 KiB
Go

package audio
import "fmt"
// Codec converts between the wire payload of a network audio stream (Icom 50003)
// and OpsLog's internal PCM (16 kHz mono 16-bit little-endian — the format the
// capture/render engine and the pcmRing use). It exists so the transport code
// (icomaudio.go) never hard-codes a format: today PCM is an identity passthrough;
// tomorrow an Opus codec implements the same two methods and drops in unchanged.
// This mirrors how civTransport abstracts the CAT byte stream from its transport.
type Codec interface {
// Name is a short label for logs/UI.
Name() string
// Decode turns one received audio payload into internal PCM. The returned
// slice is freshly allocated (the caller may retain it).
Decode(payload []byte) ([]byte, error)
// Encode turns internal PCM into a payload to transmit.
Encode(pcm []byte) ([]byte, error)
}
// pcm16Codec is the uncompressed 16-bit-PCM codec — the Icom "uncompressed"
// audio mode (rxcodec/txcodec in the conninfo). Icom sends little-endian 16-bit
// mono samples, which is byte-for-byte OpsLog's internal format, so decode/encode
// are copies. It is the Phase-4/5 default: zero-dependency, lossless, ideal on a
// LAN. Opus (for WAN/internet bandwidth) becomes another Codec later.
//
// NOTE: rate conversion is deliberately NOT this layer's job. The Icom RX audio
// is 16 kHz = our internal rate, so RX needs none. The rig's TX side may run at a
// different rate (the captured conninfo showed 8 kHz) — Phase 5 will resample in
// the TX path before Encode; keeping the codec rate-agnostic keeps that concern
// in one place.
type pcm16Codec struct{}
// NewPCM16Codec returns the uncompressed 16-bit PCM codec.
func NewPCM16Codec() Codec { return pcm16Codec{} }
func (pcm16Codec) Name() string { return "pcm16" }
func (pcm16Codec) Decode(payload []byte) ([]byte, error) {
// Icom PCM payloads are whole 16-bit samples; an odd length means a truncated
// packet — trim the stray byte rather than emit a half-sample click.
n := len(payload) &^ 1
if n != len(payload) {
if n == 0 {
return nil, fmt.Errorf("pcm16: payload too short (%d bytes)", len(payload))
}
}
out := make([]byte, n)
copy(out, payload[:n])
return out, nil
}
func (pcm16Codec) Encode(pcm []byte) ([]byte, error) {
out := make([]byte, len(pcm))
copy(out, pcm)
return out, nil
}