You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

AudioService.cs 6.2 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. using Nito.AsyncEx;
  2. using System;
  3. using System.Collections.Concurrent;
  4. using System.Linq;
  5. using System.Threading.Tasks;
  6. namespace Discord.Audio
  7. {
  8. public class AudioService : IService
  9. {
  10. private readonly AsyncLock _asyncLock;
  11. private AudioClient _defaultClient; //Only used for single server
  12. private VirtualClient _currentClient; //Only used for single server
  13. private ConcurrentDictionary<ulong, AudioClient> _voiceClients;
  14. private ConcurrentDictionary<User, bool> _talkingUsers;
  15. private int _nextClientId;
  16. internal DiscordClient Client { get; private set; }
  17. public AudioServiceConfig Config { get; }
  18. public event EventHandler Connected = delegate { };
  19. public event EventHandler<VoiceDisconnectedEventArgs> Disconnected = delegate { };
  20. public event EventHandler<UserIsSpeakingEventArgs> UserIsSpeakingUpdated = delegate { };
  21. private void OnConnected()
  22. => Connected(this, EventArgs.Empty);
  23. private void OnDisconnected(ulong serverId, bool wasUnexpected, Exception ex)
  24. => Disconnected(this, new VoiceDisconnectedEventArgs(serverId, wasUnexpected, ex));
  25. private void OnUserIsSpeakingUpdated(User user, bool isSpeaking)
  26. => UserIsSpeakingUpdated(this, new UserIsSpeakingEventArgs(user, isSpeaking));
  27. public AudioService(AudioServiceConfig config)
  28. {
  29. Config = config;
  30. _asyncLock = new AsyncLock();
  31. }
  32. void IService.Install(DiscordClient client)
  33. {
  34. Client = client;
  35. Config.Lock();
  36. if (Config.EnableMultiserver)
  37. _voiceClients = new ConcurrentDictionary<ulong, AudioClient>();
  38. else
  39. {
  40. var logger = Client.Log.CreateLogger("Voice");
  41. _defaultClient = new AudioClient(Client, null, 0);
  42. }
  43. _talkingUsers = new ConcurrentDictionary<User, bool>();
  44. client.Disconnected += async (s, e) =>
  45. {
  46. if (Config.EnableMultiserver)
  47. {
  48. var tasks = _voiceClients
  49. .Select(x =>
  50. {
  51. var val = x.Value;
  52. if (val != null)
  53. return x.Value.Disconnect();
  54. else
  55. return TaskHelper.CompletedTask;
  56. })
  57. .ToArray();
  58. await Task.WhenAll(tasks).ConfigureAwait(false);
  59. _voiceClients.Clear();
  60. }
  61. foreach (var member in _talkingUsers)
  62. {
  63. bool ignored;
  64. if (_talkingUsers.TryRemove(member.Key, out ignored))
  65. OnUserIsSpeakingUpdated(member.Key, false);
  66. }
  67. };
  68. }
  69. public IAudioClient GetClient(Server server)
  70. {
  71. if (server == null) throw new ArgumentNullException(nameof(server));
  72. if (Config.EnableMultiserver)
  73. {
  74. AudioClient client;
  75. if (_voiceClients.TryGetValue(server.Id, out client))
  76. return client;
  77. else
  78. return null;
  79. }
  80. else
  81. {
  82. if (server == _currentClient.Server)
  83. return _currentClient;
  84. else
  85. return null;
  86. }
  87. }
  88. //Called from AudioClient.Disconnect
  89. internal async Task RemoveClient(Server server, AudioClient client)
  90. {
  91. using (await _asyncLock.LockAsync().ConfigureAwait(false))
  92. {
  93. if (_voiceClients.TryUpdate(server.Id, null, client))
  94. _voiceClients.TryRemove(server.Id, out client);
  95. }
  96. }
  97. public async Task<IAudioClient> Join(Channel channel)
  98. {
  99. if (channel == null) throw new ArgumentNullException(nameof(channel));
  100. var server = channel.Server;
  101. using (await _asyncLock.LockAsync().ConfigureAwait(false))
  102. {
  103. if (Config.EnableMultiserver)
  104. {
  105. AudioClient client;
  106. if (!_voiceClients.TryGetValue(server.Id, out client))
  107. {
  108. client = new AudioClient(Client, server, unchecked(++_nextClientId));
  109. _voiceClients[server.Id] = client;
  110. await client.Connect().ConfigureAwait(false);
  111. /*voiceClient.VoiceSocket.FrameReceived += (s, e) =>
  112. {
  113. OnFrameReceieved(e);
  114. };
  115. voiceClient.VoiceSocket.UserIsSpeaking += (s, e) =>
  116. {
  117. var user = server.GetUser(e.UserId);
  118. OnUserIsSpeakingUpdated(user, e.IsSpeaking);
  119. };*/
  120. }
  121. await client.Join(channel).ConfigureAwait(false);
  122. return client;
  123. }
  124. else
  125. {
  126. if (_defaultClient.Server != server)
  127. {
  128. await _defaultClient.Disconnect();
  129. _defaultClient.VoiceSocket.Server = server;
  130. await _defaultClient.Connect().ConfigureAwait(false);
  131. }
  132. var client = new VirtualClient(_defaultClient, server);
  133. _currentClient = client;
  134. await client.Join(channel).ConfigureAwait(false);
  135. return client;
  136. }
  137. }
  138. }
  139. public async Task Leave(Server server)
  140. {
  141. if (server == null) throw new ArgumentNullException(nameof(server));
  142. if (Config.EnableMultiserver)
  143. {
  144. AudioClient client;
  145. if (_voiceClients.TryRemove(server.Id, out client))
  146. await client.Disconnect().ConfigureAwait(false);
  147. }
  148. else
  149. {
  150. using (await _asyncLock.LockAsync().ConfigureAwait(false))
  151. {
  152. var client = GetClient(server) as VirtualClient;
  153. if (client != null)
  154. await _defaultClient.Disconnect().ConfigureAwait(false);
  155. }
  156. }
  157. }
  158. }
  159. }