Skip to content

Commit 78f916d

Browse files
committed
Merge pull-request #28
2 parents fd30637 + 8de7746 commit 78f916d

File tree

3 files changed

+148
-8
lines changed

3 files changed

+148
-8
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ There exists a protocol definition (below), a Go library, and Asterisk
99
application and channel interfaces.
1010

1111
**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.
12+
**NOTE:** [@SvenKube](https://github.com/SvenKube) has contributed [support for higher sample rates](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.
1213

1314
## Protocol definition
1415

@@ -26,6 +27,14 @@ indication, for instance, is `0x00 0x00 0x00`.
2627
- `0x01` - Payload will contain the UUID (16-byte binary representation) for the audio stream
2728
- `0x03` - Payload is 1 byte (ascii) DTMF (dual-tone multi-frequency) digit
2829
- `0x10` - Payload is signed linear, 16-bit, 8kHz, mono PCM (little-endian)
30+
- `0x11` - Payload is signed linear, 16-bit, 12kHz, mono PCM (little-endian)
31+
- `0x12` - Payload is signed linear, 16-bit, 16kHz, mono PCM (little-endian)
32+
- `0x13` - Payload is signed linear, 16-bit, 24kHz, mono PCM (little-endian)
33+
- `0x14` - Payload is signed linear, 16-bit, 32kHz, mono PCM (little-endian)
34+
- `0x15` - Payload is signed linear, 16-bit, 44.1kHz, mono PCM (little-endian)
35+
- `0x16` - Payload is signed linear, 16-bit, 48kHz, mono PCM (little-endian)
36+
- `0x17` - Payload is signed linear, 16-bit, 96kHz, mono PCM (little-endian)
37+
- `0x18` - Payload is signed linear, 16-bit, 192kHz, mono PCM (little-endian)
2938
- `0xff` - An error has occurred; payload is the (optional)
3039
application-specific error code. Asterisk-generated error codes are listed
3140
below.

audiosocket.go

Lines changed: 127 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,33 @@ const (
2727
// KindDTMF indicates the message contains DTMF data
2828
KindDTMF = 0x03
2929

30-
// KindSlin indicates the message contains signed-linear audio data
30+
// KindSlin indicates that the message contains 16-bit, 8 kbit/s signed-linear audio data
3131
KindSlin = 0x10
3232

33+
// KindSlin indicates that the message contains 16-bit, 12 kbit/s signed-linear audio data
34+
KindSlin12 = 0x11
35+
36+
// KindSlin indicates that the message contains 16-bit, 16 kbit/s signed-linear audio data
37+
KindSlin16 = 0x12
38+
39+
// KindSlin indicates that the message contains 16-bit, 24 kbit/s signed-linear audio data
40+
KindSlin24 = 0x13
41+
42+
// KindSlin indicates that the message contains 16-bit, 32 kbit/s signed-linear audio data
43+
KindSlin32 = 0x14
44+
45+
// KindSlin indicates that the message contains 16-bit, 44.1 kbit/s signed-linear audio data
46+
KindSlin44 = 0x15
47+
48+
// KindSlin indicates that the message contains 16-bit, 48 kbit/s signed-linear audio data
49+
KindSlin48 = 0x16
50+
51+
// KindSlin indicates that the message contains 16-bit, 96 kbit/s signed-linear audio data
52+
KindSlin96 = 0x17
53+
54+
// KindSlin indicates that the message contains 16-bit, 192 kbit/s signed-linear audio data
55+
KindSlin192 = 0x18
56+
3357
// KindError indicates the message contains an error code
3458
KindError = 0xff
3559
)
@@ -54,6 +78,94 @@ const (
5478
ErrUnknown = 0xff
5579
)
5680

81+
// AudioFormat defines codec-specific parameters for audio transmission
82+
type AudioFormat struct {
83+
Kind Kind
84+
ChunkSize int
85+
}
86+
87+
var (
88+
// FormatSlin represents 8kHz signed linear audio format
89+
FormatSlin = AudioFormat{
90+
Kind: KindSlin,
91+
ChunkSize: 320, // 8000Hz * 20ms * 2 bytes
92+
}
93+
94+
// FormatSlin12 represents 12kHz signed linear audio format
95+
FormatSlin12 = AudioFormat{
96+
Kind: KindSlin12,
97+
ChunkSize: 480, // 12000Hz * 20ms * 2 bytes
98+
}
99+
100+
// FormatSlin16 represents 16kHz signed linear audio format
101+
FormatSlin16 = AudioFormat{
102+
Kind: KindSlin16,
103+
ChunkSize: 640, // 16000Hz * 20ms * 2 bytes
104+
}
105+
106+
// FormatSlin24 represents 24kHz signed linear audio format
107+
FormatSlin24 = AudioFormat{
108+
Kind: KindSlin24,
109+
ChunkSize: 960, // 24000Hz * 20ms * 2 bytes
110+
}
111+
112+
// FormatSlin32 represents 32kHz signed linear audio format
113+
FormatSlin32 = AudioFormat{
114+
Kind: KindSlin32,
115+
ChunkSize: 1280, // 32000Hz * 20ms * 2 bytes
116+
}
117+
118+
// FormatSlin44 represents 44kHz signed linear audio format
119+
FormatSlin44 = AudioFormat{
120+
Kind: KindSlin44,
121+
ChunkSize: 1764, // 44100Hz * 20ms * 2 bytes
122+
}
123+
124+
// FormatSlin48 represents 48kHz signed linear audio format
125+
FormatSlin48 = AudioFormat{
126+
Kind: KindSlin48,
127+
ChunkSize: 1920, // 48000Hz * 20ms * 2 bytes
128+
}
129+
130+
// FormatSlin96 represents 96kHz signed linear audio format
131+
FormatSlin96 = AudioFormat{
132+
Kind: KindSlin96,
133+
ChunkSize: 3840, // 96000Hz * 20ms * 2 bytes
134+
}
135+
136+
// FormatSlin192 represents 192kHz signed linear audio format
137+
FormatSlin192 = AudioFormat{
138+
Kind: KindSlin192,
139+
ChunkSize: 7680, // 192000Hz * 20ms * 2 bytes
140+
}
141+
)
142+
143+
// AudioFormat returns the AudioFormat for this Kind
144+
func (k Kind) AudioFormat() (AudioFormat, error) {
145+
switch k {
146+
case KindSlin:
147+
return FormatSlin, nil
148+
case KindSlin12:
149+
return FormatSlin12, nil
150+
case KindSlin16:
151+
return FormatSlin16, nil
152+
case KindSlin24:
153+
return FormatSlin24, nil
154+
case KindSlin32:
155+
return FormatSlin32, nil
156+
case KindSlin44:
157+
return FormatSlin44, nil
158+
case KindSlin48:
159+
return FormatSlin48, nil
160+
case KindSlin96:
161+
return FormatSlin96, nil
162+
case KindSlin192:
163+
return FormatSlin192, nil
164+
default:
165+
return AudioFormat{}, fmt.Errorf("unsupported audio format: %d", k)
166+
}
167+
}
168+
57169
// ContentLength returns the length of the payload of the message
58170
func (m Message) ContentLength() uint16 {
59171
if len(m) < 3 {
@@ -171,3 +283,17 @@ func SlinMessage(in []byte) Message {
171283
out = append(out, in...)
172284
return out
173285
}
286+
287+
// AudioMessage creates a new Message from audio data with the specified kind
288+
// If the input is larger than 65535 bytes, this function will panic.
289+
func AudioMessage(in []byte, kind Kind) Message {
290+
if len(in) > 65535 {
291+
panic("audiosocket: message too large")
292+
}
293+
294+
out := make([]byte, 3, 3+len(in))
295+
out[0] = byte(kind)
296+
binary.BigEndian.PutUint16(out[1:], uint16(len(in)))
297+
out = append(out, in...)
298+
return out
299+
}

chunk.go

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,27 @@ const DefaultSlinChunkSize = 320 // 8000Hz * 20ms * 2 bytes
1313

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

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

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

2530
for i := 0; i < len(input); {
2631
<-t.C
27-
chunkLen := chunkSize
28-
if i+chunkSize > len(input) {
32+
chunkLen := format.ChunkSize
33+
if i+format.ChunkSize > len(input) {
2934
chunkLen = len(input) - i
3035
}
31-
if _, err := w.Write(SlinMessage(input[i : i+chunkLen])); err != nil {
36+
if _, err := w.Write(AudioMessage(input[i:i+chunkLen], format.Kind)); err != nil {
3237
return fmt.Errorf("failed to write chunk to AudioSocket: %w", err)
3338
}
3439
chunks++

0 commit comments

Comments
 (0)