58 lines
2.3 KiB
Go
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
|
|
}
|