vail/message.go

82 lines
1.8 KiB
Go

package main
import (
"bytes"
"encoding/binary"
"time"
)
// VailMessage is a single Vail message.
type Message struct {
// Relative time in ms of this message.
// These timestamps need to be consistent, but the offset can be anything.
// ECMAScript `performance.now()` is ideal.
Timestamp int64
// Message timing in ms.
// Timings alternate between tone and silence.
// For example, `A` could be sent as [80, 80, 240]
Duration []uint8
}
func NewMessage(ts time.Time, durations []time.Duration) Message {
msg := Message{
Timestamp: ts.UnixNano() / time.Millisecond.Nanoseconds(),
Duration: make([]uint8, len(durations)),
}
for i, dns := range durations {
ms := dns.Milliseconds()
if (ms > 255) {
ms = 255
} else if (ms < 0) {
ms = 0
}
msg.Duration[i] = uint8(ms)
}
return msg
}
// Marshaling presumes something else is keeping track of lengths
func (m Message) MarshalBinary() ([]byte, error) {
var w bytes.Buffer
if err := binary.Write(&w, binary.BigEndian, m.Timestamp); err != nil {
return nil, err
}
if err := binary.Write(&w, binary.BigEndian, m.Duration); err != nil {
return nil, err
}
return w.Bytes(), nil
}
// Unmarshaling presumes something else is keeping track of lengths
func (m *Message) UnmarshalBinary(data []byte) error {
r := bytes.NewReader(data)
if err := binary.Read(r, binary.BigEndian, &m.Timestamp); err != nil {
return err
}
dlen := r.Len()
m.Duration = make([]uint8, dlen)
if err := binary.Read(r, binary.BigEndian, &m.Duration); err != nil {
return err
}
return nil
}
func (m Message) Equal(m2 Message) bool {
if m.Timestamp != m2.Timestamp {
return false
}
if len(m.Duration) != len(m2.Duration) {
return false
}
for i := range m.Duration {
if m.Duration[i] != m2.Duration[i] {
return false
}
}
return true
}