Go-based FM stereo transmitter with RDS, Windows-first and cross-platform
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

82 linhas
2.2KB

  1. package aoiprxkit
  2. import (
  3. "encoding/binary"
  4. "fmt"
  5. "io"
  6. )
  7. const (
  8. streamMagic = "ARX1"
  9. StreamCodecPCM_S32LE uint8 = 1
  10. StreamCodecOpus uint8 = 2 // reserved for later phases
  11. )
  12. type StreamHeader struct {
  13. Codec uint8
  14. Channels uint8
  15. SampleRateHz uint32
  16. FrameSamples uint32
  17. Sequence uint32
  18. Timestamp uint64
  19. PayloadBytes uint32
  20. }
  21. func ReadStreamHeader(r io.Reader) (StreamHeader, error) {
  22. var raw [26]byte
  23. if _, err := io.ReadFull(r, raw[:]); err != nil {
  24. return StreamHeader{}, err
  25. }
  26. if string(raw[0:4]) != streamMagic {
  27. return StreamHeader{}, fmt.Errorf("invalid stream magic %q", string(raw[0:4]))
  28. }
  29. h := StreamHeader{
  30. Codec: raw[4],
  31. Channels: raw[5],
  32. SampleRateHz: binary.BigEndian.Uint32(raw[6:10]),
  33. FrameSamples: binary.BigEndian.Uint32(raw[10:14]),
  34. Sequence: binary.BigEndian.Uint32(raw[14:18]),
  35. Timestamp: binary.BigEndian.Uint64(raw[18:26]),
  36. }
  37. var payloadLenRaw [4]byte
  38. if _, err := io.ReadFull(r, payloadLenRaw[:]); err != nil {
  39. return StreamHeader{}, err
  40. }
  41. h.PayloadBytes = binary.BigEndian.Uint32(payloadLenRaw[:])
  42. return h, nil
  43. }
  44. func WritePCM32Packet(w io.Writer, channels int, sampleRateHz int, frameSamples int, sequence uint32, timestamp uint64, samples []int32) error {
  45. if channels < 1 || channels > 2 {
  46. return fmt.Errorf("channels must be 1 or 2")
  47. }
  48. if frameSamples < 0 {
  49. return fmt.Errorf("frameSamples must be >= 0")
  50. }
  51. if len(samples) != frameSamples*channels {
  52. return fmt.Errorf("sample length mismatch: got=%d want=%d", len(samples), frameSamples*channels)
  53. }
  54. payloadBytes := len(samples) * 4
  55. var hdr [30]byte
  56. copy(hdr[0:4], []byte(streamMagic))
  57. hdr[4] = StreamCodecPCM_S32LE
  58. hdr[5] = byte(channels)
  59. binary.BigEndian.PutUint32(hdr[6:10], uint32(sampleRateHz))
  60. binary.BigEndian.PutUint32(hdr[10:14], uint32(frameSamples))
  61. binary.BigEndian.PutUint32(hdr[14:18], sequence)
  62. binary.BigEndian.PutUint64(hdr[18:26], timestamp)
  63. binary.BigEndian.PutUint32(hdr[26:30], uint32(payloadBytes))
  64. if _, err := w.Write(hdr[:]); err != nil {
  65. return err
  66. }
  67. payload := make([]byte, payloadBytes)
  68. for i, s := range samples {
  69. binary.LittleEndian.PutUint32(payload[i*4:i*4+4], uint32(s))
  70. }
  71. _, err := w.Write(payload)
  72. return err
  73. }