-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsound.go
197 lines (159 loc) · 4.2 KB
/
sound.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
package main
import (
"encoding/binary"
"io"
"math/rand"
"os"
"time"
"github.com/bwmarrin/discordgo"
)
// Holding multiple sounds
type SoundCollection struct {
Name string `json:"name"`
Sounds []*Sound `json:"sounds,omitempty"`
}
// Holding the name, path and data of a sound
type Sound struct {
Name string `json:"name"`
File string `json:"path"`
buffer [][]byte `json:"-"`
}
// Holding information to where play which sound
type Play struct {
sound *Sound
channelID string
guildID string
}
/**
* Play
*/
// Creates a new Play
func NewPlay(sound *Sound, guildID, channelID string) *Play {
return &Play{sound: sound, guildID: guildID, channelID: channelID}
}
/**
* SoundCollection
*/
// Loads the sound data of all Sounds in the SoundCollection
func (sc *SoundCollection) Load() {
log.Infof("Loading collection %s", sc.Name)
for _, s := range sc.Sounds {
s.Load()
}
}
// Returns the Sound with the given name or nil, if no sound with
// the given name exists in this SoundCollection
func (sc *SoundCollection) GetSound(name string) *Sound {
for _, s := range sc.Sounds {
if s.Name == name {
return s
}
}
return nil
}
// Returns a random sound
func (sc *SoundCollection) GetRandomSound() *Sound {
index := rand.Intn(len(sc.Sounds))
return sc.Sounds[index]
}
/**
* Sound
*/
// Creats a new sound
func NewSound(name string, file string) *Sound {
return &Sound{Name: name, File: file}
}
// Loads the Sounddata of the sound into the memory
func (s *Sound) Load() (err error) {
log.Infof("Loading sound %s", s.Name)
file, err := os.Open(s.File)
if err != nil {
log.Errorf("Error opening dca file > %s", err)
return
}
var opuslen int16
for {
// Read opus frame length from dca file.
err = binary.Read(file, binary.LittleEndian, &opuslen)
// If this is the end of the file, just return.
if err == io.EOF || err == io.ErrUnexpectedEOF {
file.Close()
if err != nil {
return
}
return
}
if err != nil {
log.Errorf("Error reading from dca file > %s", err)
return
}
// Read encoded pcm from dca file.
InBuf := make([]byte, opuslen)
err = binary.Read(file, binary.LittleEndian, &InBuf)
// Should not be any end of file errors
if err != nil {
log.Errorf("Error reading from dca file > %s", err)
return
}
// Append encoded pcm data to the buffer.
s.buffer = append(s.buffer, InBuf)
}
}
/**
* Discordbot methodes
*/
// Creates a Play and puts it into the queue of the given guild if the maximum number of
// enqued Plays is not exeeded
func enqueueSound(s *discordgo.Session, guildID, channelID string, sound *Sound) {
play := NewPlay(sound, guildID, channelID)
queueLock.Lock()
defer queueLock.Unlock()
playChannel := playQueue[guildID]
if playChannel != nil {
if len(playChannel) < MAX_QUEUE_SIZE {
playChannel <- play
}
} else {
playQueue[guildID] = make(chan *Play, MAX_QUEUE_SIZE)
go playSound(s, play, nil)
}
}
// Plays a Play in its VoiceChannel using an existing VoiceConnection or, if vc is nil
// creates its own VoiceConnection. Furthermore, if there are Plays for the same VoiceChannel,
// a new go routine will be started to play the next song
// If the VoiceConnection is connected to a different channel, the channel will be changed.
func playSound(s *discordgo.Session, play *Play, vc *discordgo.VoiceConnection) (err error) {
if vc == nil {
// Join the provided voice channel.
vc, err = s.ChannelVoiceJoin(play.guildID, play.channelID, false, true)
if err != nil {
return err
}
time.Sleep(200 * time.Millisecond)
}
if vc.ChannelID != play.channelID {
vc.ChangeChannel(play.channelID, false, true)
time.Sleep(200 * time.Millisecond)
}
time.Sleep(50 * time.Millisecond)
_ = vc.Speaking(true)
// Send the buffer data.
for _, buff := range play.sound.buffer {
vc.OpusSend <- buff
}
_ = vc.Speaking(false)
queueLock.Lock()
if len(playQueue[play.guildID]) > 0 {
play = <-playQueue[play.guildID]
queueLock.Unlock()
go playSound(s, play, vc)
return nil
}
delete(playQueue, play.guildID)
queueLock.Unlock()
// Sleep for a specificed amount of time before ending.
time.Sleep(250 * time.Millisecond)
// Disconnect from the provided voice channel.
_ = vc.Disconnect()
return nil
}