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 }