// Package protocol implements the obs-teleport wire format. // // This file builds the audio ("WAVE") side of the protocol: Header + // WaveHeader + raw interleaved PCM, byte-for-byte compatible with the // reference implementation (GPL-2.0). package protocol // BuildWavePacket assembles a complete ""WAVE"" wire packet: // Header + WaveHeader + interleaved PCM payload. // // pcm is already interleaved (L,R,L,R,… for stereo). format must be one of // the AudioFormat* constants. frames is the PCM frame count (samples per // channel). The returned slice is a freshly allocated buffer safe to hand to // the network. func BuildWavePacket(timestamp uint64, format int32, sampleRate, speakers, frames int32, pcm []byte) ([]byte, error) { expected := int64(speakers) * int64(frames) * int64(bytesPerSample(format)) if int64(len(pcm)) != expected { return nil, errBadPCMLength{got: len(pcm), want: int(expected)} } h := Header{ Type: AudioType, Timestamp: timestamp, Size: int32(len(pcm)), } wave := WaveHeader{ Format: format, SampleRate: sampleRate, Speakers: speakers, Frames: frames, } return WritePacket(h, nil, &wave, pcm) } // bytesPerSample maps an AudioFormat* value to its size in bytes. func bytesPerSample(format int32) int { switch format { case AudioFormatU8: return 1 case AudioFormatS16: return 2 case AudioFormatS32, AudioFormatF32: return 4 default: return 0 } } // errBadPCMLength is returned when pcm length does not match the declared // format/speakers/frames. type errBadPCMLength struct{ got, want int } func (e errBadPCMLength) Error() string { return "protocol: pcm length mismatch" + ": got " + itoa(e.got) + " bytes, want " + itoa(e.want) } // itoa is a tiny local int→string helper to avoid importing strconv in the // hot path callers. func itoa(v int) string { if v == 0 { return "0" } neg := v < 0 if neg { v = -v } var buf [20]byte i := len(buf) for v > 0 { i-- buf[i] = byte('0' + v%10) v /= 10 } if neg { i-- buf[i] = '-' } return string(buf[i:]) }