Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ There exists a protocol definition (below), a Go library, and Asterisk
application and channel interfaces.

**NOTE:** [@florentchauveau](https://github.com/florentchauveau) has contributed [DTMF support](https://github.com/asterisk/asterisk/pull/1138) to the AudioSocket protocol. The patch has been merged into `master` and will be included in Asterisk versions 20.X, 21.X, and 22.X.
**NOTE:** [@SvenKube](https://github.com/SvenKube) has contributed [slin16 support](https://github.com/asterisk/asterisk/pull/1492) to the AudioSocket protocol. The patch has been merged into `master` and will be included in Asterisk versions 20.X, 21.X, 22.X, and 23.X.

## Protocol definition

Expand All @@ -26,6 +27,7 @@ indication, for instance, is `0x00 0x00 0x00`.
- `0x01` - Payload will contain the UUID (16-byte binary representation) for the audio stream
- `0x03` - Payload is 1 byte (ascii) DTMF (dual-tone multi-frequency) digit
- `0x10` - Payload is signed linear, 16-bit, 8kHz, mono PCM (little-endian)
- `0x11` - Payload is signed linear, 16-bit, 16kHz, mono PCM (little-endian)
- `0xff` - An error has occurred; payload is the (optional)
application-specific error code. Asterisk-generated error codes are listed
below.
Expand Down
49 changes: 49 additions & 0 deletions audiosocket.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ const (
// KindSlin indicates the message contains signed-linear audio data
KindSlin = 0x10

// KindSlin indicates that the message contains 16-bit, 16 kbit/s signed-linear audio data
KindSlin16 = 0x11

// KindError indicates the message contains an error code
KindError = 0xff
)
Expand All @@ -54,6 +57,38 @@ const (
ErrUnknown = 0xff
)

// AudioFormat defines codec-specific parameters for audio transmission
type AudioFormat struct {
Kind Kind
ChunkSize int
}

var (
// FormatSlin represents 8kHz signed linear audio format
FormatSlin = AudioFormat{
Kind: KindSlin,
ChunkSize: 320, // 8000Hz * 20ms * 2 bytes
}

// FormatSlin16 represents 16kHz signed linear audio format
FormatSlin16 = AudioFormat{
Kind: KindSlin16,
ChunkSize: 640, // 16000Hz * 20ms * 2 bytes
}
)

// AudioFormat returns the AudioFormat for this Kind
func (k Kind) AudioFormat() (AudioFormat, error) {
switch k {
case KindSlin:
return FormatSlin, nil
case KindSlin16:
return FormatSlin16, nil
default:
return AudioFormat{}, fmt.Errorf("unsupported audio format: %d", k)
}
}

// ContentLength returns the length of the payload of the message
func (m Message) ContentLength() uint16 {
if len(m) < 3 {
Expand Down Expand Up @@ -171,3 +206,17 @@ func SlinMessage(in []byte) Message {
out = append(out, in...)
return out
}

// AudioMessage creates a new Message from audio data with the specified kind
// If the input is larger than 65535 bytes, this function will panic.
func AudioMessage(in []byte, kind Kind) Message {
if len(in) > 65535 {
panic("audiosocket: message too large")
}

out := make([]byte, 3, 3+len(in))
out[0] = byte(kind)
binary.BigEndian.PutUint16(out[1:], uint16(len(in)))
out = append(out, in...)
return out
}
19 changes: 12 additions & 7 deletions chunk.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,27 @@ const DefaultSlinChunkSize = 320 // 8000Hz * 20ms * 2 bytes

// SendSlinChunks takes signed linear data and sends it over an AudioSocket connection in chunks of the given size.
func SendSlinChunks(w io.Writer, chunkSize int, input []byte) error {
var chunks int
return SendAudioChunks(
w, AudioFormat{
Kind: KindSlin,
ChunkSize: chunkSize,
}, input)
}

if chunkSize < 1 {
chunkSize = DefaultSlinChunkSize
}
// SendAudioChunks takes audio data and sends it over an AudioSocket connection using the specified format
func SendAudioChunks(w io.Writer, format AudioFormat, input []byte) error {
var chunks int

t := time.NewTicker(20 * time.Millisecond)
defer t.Stop()

for i := 0; i < len(input); {
<-t.C
chunkLen := chunkSize
if i+chunkSize > len(input) {
chunkLen := format.ChunkSize
if i+format.ChunkSize > len(input) {
chunkLen = len(input) - i
}
if _, err := w.Write(SlinMessage(input[i : i+chunkLen])); err != nil {
if _, err := w.Write(AudioMessage(input[i:i+chunkLen], format.Kind)); err != nil {
return fmt.Errorf("failed to write chunk to AudioSocket: %w", err)
}
chunks++
Expand Down