From 25b19d729898fc13f3803aadebec847ee005351f Mon Sep 17 00:00:00 2001 From: Splamy Date: Thu, 22 Feb 2018 05:10:38 +0100 Subject: [PATCH] Small performance improvements - tiny performance improvement in the encoder - added early chain abort check - readded stall checker --- TS3AudioBot/Audio/CustomTargetPipe.cs | 25 ++++++-- TS3AudioBot/Audio/FfmpegProducer.cs | 2 +- TS3AudioBot/Audio/StallCheckPipe.cs | 64 +++++++++++++++++++ TS3AudioBot/Bot.cs | 1 - TS3AudioBot/Commands.cs | 2 +- TS3AudioBot/Core.cs | 3 +- TS3AudioBot/Dependency/DependencyRealm.cs | 26 ++++++-- TS3AudioBot/ITargetManager.cs | 2 +- TS3AudioBot/Plugins/Plugin.cs | 2 +- TS3AudioBot/TS3AudioBot.csproj | 1 + TS3AudioBot/Ts3Full.cs | 56 ++-------------- TS3Client/Audio/ActiveCheckPipe.cs | 27 ++++++++ TS3Client/{Full => }/Audio/AudioInterfaces.cs | 6 +- TS3Client/{Full => }/Audio/AudioMeta.cs | 2 +- .../{Full => }/Audio/AudioPacketReader.cs | 4 +- .../{Full => }/Audio/AudioPipeExtensions.cs | 2 +- TS3Client/{Full => }/Audio/EncoderPipe.cs | 43 +++++++------ TS3Client/{Full => }/Audio/Opus/LICENSE | 0 .../{Full => }/Audio/Opus/NativeMethods.cs | 2 +- TS3Client/{Full => }/Audio/Opus/OPUS_LICENSE | 0 .../{Full => }/Audio/Opus/OpusDecoder.cs | 2 +- .../{Full => }/Audio/Opus/OpusEncoder.cs | 2 +- TS3Client/{Full => }/Audio/Opus/README | 0 .../{Full => }/Audio/PreciseAudioTimer.cs | 2 +- .../{Full => }/Audio/PreciseTimedPipe.cs | 2 +- TS3Client/{Full => }/Audio/SplitterPipe.cs | 6 +- TS3Client/{Full => }/Audio/StaticMetaPipe.cs | 3 +- .../{Full => }/Audio/StreamAudioProducer.cs | 2 +- TS3Client/{Full => }/Audio/VolumePipe.cs | 3 +- TS3Client/Full/Ts3FullClient.cs | 6 +- TS3Client/Generated/Permissions.cs | 4 +- TS3Client/Generated/Permissions.tt | 4 +- TS3Client/Generated/Versions.cs | 2 + TS3Client/Generated/Versions.tt | 2 + TS3Client/TS3Client.csproj | 35 +++++----- 35 files changed, 218 insertions(+), 127 deletions(-) create mode 100644 TS3AudioBot/Audio/StallCheckPipe.cs create mode 100644 TS3Client/Audio/ActiveCheckPipe.cs rename TS3Client/{Full => }/Audio/AudioInterfaces.cs (87%) rename TS3Client/{Full => }/Audio/AudioMeta.cs (97%) rename TS3Client/{Full => }/Audio/AudioPacketReader.cs (90%) rename TS3Client/{Full => }/Audio/AudioPipeExtensions.cs (97%) rename TS3Client/{Full => }/Audio/EncoderPipe.cs (71%) rename TS3Client/{Full => }/Audio/Opus/LICENSE (100%) rename TS3Client/{Full => }/Audio/Opus/NativeMethods.cs (99%) rename TS3Client/{Full => }/Audio/Opus/OPUS_LICENSE (100%) rename TS3Client/{Full => }/Audio/Opus/OpusDecoder.cs (99%) rename TS3Client/{Full => }/Audio/Opus/OpusEncoder.cs (99%) rename TS3Client/{Full => }/Audio/Opus/README (100%) rename TS3Client/{Full => }/Audio/PreciseAudioTimer.cs (98%) rename TS3Client/{Full => }/Audio/PreciseTimedPipe.cs (98%) rename TS3Client/{Full => }/Audio/SplitterPipe.cs (92%) rename TS3Client/{Full => }/Audio/StaticMetaPipe.cs (96%) rename TS3Client/{Full => }/Audio/StreamAudioProducer.cs (95%) rename TS3Client/{Full => }/Audio/VolumePipe.cs (94%) diff --git a/TS3AudioBot/Audio/CustomTargetPipe.cs b/TS3AudioBot/Audio/CustomTargetPipe.cs index 18e7affc..d0fae04b 100644 --- a/TS3AudioBot/Audio/CustomTargetPipe.cs +++ b/TS3AudioBot/Audio/CustomTargetPipe.cs @@ -14,8 +14,8 @@ namespace TS3AudioBot.Audio using System.Collections.Generic; using System.Linq; using TS3Client; + using TS3Client.Audio; using TS3Client.Full; - using TS3Client.Full.Audio; internal class CustomTargetPipe : ITargetManager, IAudioPassiveConsumer { @@ -24,6 +24,23 @@ internal class CustomTargetPipe : ITargetManager, IAudioPassiveConsumer public GroupWhisperType GroupWhisperType { get; set; } public GroupWhisperTarget GroupWhisperTarget { get; set; } + public bool Active + { + get + { + switch (SendMode) + { + case TargetSendMode.None: + return false; + case TargetSendMode.Whisper: + UpdatedSubscriptionCache(); + return channelSubscriptionsCache.Length > 0 || clientSubscriptionsCache.Length > 0; + default: + return true; + } + } + } + private readonly Dictionary channelSubscriptionsSetup; private readonly List clientSubscriptionsSetup; private ulong[] channelSubscriptionsCache; @@ -64,10 +81,6 @@ public void Write(Span data, Meta meta) } } - // TODO get this somehow to the front of the pipechain, to prevent further chain execution - // if (SendMode == TargetSendMode.Whisper) - // doSend &= channelSubscriptionsCache.Length > 0 || clientSubscriptionsCache.Length > 0; - #region ITargetManager public void SetGroupWhisper(GroupWhisperType type, GroupWhisperTarget target, ulong targetId = 0) @@ -79,8 +92,6 @@ public void SetGroupWhisper(GroupWhisperType type, GroupWhisperTarget target, ul public void WhisperChannelSubscribe(ulong channel, bool temp) { - // TODO move to requested channel - // TODO spawn new client lock (subscriptionLockObj) { if (channelSubscriptionsSetup.TryGetValue(channel, out var subscriptionTemp)) diff --git a/TS3AudioBot/Audio/FfmpegProducer.cs b/TS3AudioBot/Audio/FfmpegProducer.cs index 5977be48..f0ec08d6 100644 --- a/TS3AudioBot/Audio/FfmpegProducer.cs +++ b/TS3AudioBot/Audio/FfmpegProducer.cs @@ -15,7 +15,7 @@ namespace TS3AudioBot.Audio using System.Diagnostics; using System.Globalization; using System.Text.RegularExpressions; - using TS3Client.Full.Audio; + using TS3Client.Audio; public class FfmpegProducer : IAudioPassiveProducer, ISampleInfo, IDisposable { diff --git a/TS3AudioBot/Audio/StallCheckPipe.cs b/TS3AudioBot/Audio/StallCheckPipe.cs new file mode 100644 index 00000000..b47aafe1 --- /dev/null +++ b/TS3AudioBot/Audio/StallCheckPipe.cs @@ -0,0 +1,64 @@ +// TS3AudioBot - An advanced Musicbot for Teamspeak 3 +// Copyright (C) 2017 TS3AudioBot contributors +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the Open Software License v. 3.0 +// +// You should have received a copy of the Open Software License along with this +// program. If not, see . + +namespace TS3AudioBot.Audio +{ + using System; + using TS3Client.Audio; + + public class StallCheckPipe : IAudioPipe + { + private const uint StallCountInterval = 10; + private const uint StallNoErrorCountMax = 5; + + public bool Active => OutStream?.Active ?? false; + public IAudioPassiveConsumer OutStream { get; set; } + + private bool isStall; + private uint stallCount; + private uint stallNoErrorCount; + + public StallCheckPipe() + { + isStall = false; + stallCount = 0; + } + + public void Write(Span data, Meta meta) + { + if (OutStream == null) return; + + if (isStall) + { + // TODO maybe do time-cooldown instead of call-count-cooldown + if (++stallCount % StallCountInterval == 0) + { + stallNoErrorCount++; + if (stallNoErrorCount > StallNoErrorCountMax) + { + stallCount = 0; + isStall = false; + } + } + else + { + return; + } + } + + OutStream?.Write(data, meta); + } + + public void SetStall() + { + stallNoErrorCount = 0; + isStall = true; + } + } +} diff --git a/TS3AudioBot/Bot.cs b/TS3AudioBot/Bot.cs index 5943ff6e..0db2f6f2 100644 --- a/TS3AudioBot/Bot.cs +++ b/TS3AudioBot/Bot.cs @@ -94,7 +94,6 @@ public R InitializeBot() if (!Injector.AllResolved()) { - // TODO detailed log + for inner if Log.Warn("Cyclic bot module dependency"); Injector.ForceCyclicResolve(); if (!Injector.AllResolved()) diff --git a/TS3AudioBot/Commands.cs b/TS3AudioBot/Commands.cs index 2862c47b..090a45b9 100644 --- a/TS3AudioBot/Commands.cs +++ b/TS3AudioBot/Commands.cs @@ -24,7 +24,7 @@ namespace TS3AudioBot using System.Linq; using System.Text; using TS3Client; - using TS3Client.Full.Audio; + using TS3Client.Audio; using TS3Client.Messages; using Web.Api; diff --git a/TS3AudioBot/Core.cs b/TS3AudioBot/Core.cs index 82103a88..5156dc55 100644 --- a/TS3AudioBot/Core.cs +++ b/TS3AudioBot/Core.cs @@ -158,7 +158,7 @@ private R InitializeCore() Log.Info("[=== Version: {0}", SystemData.AssemblyData); Log.Info("[=== Platform: {0}", SystemData.PlattformData); Log.Info("[=== Runtime: {0}", SystemData.RuntimeData.FullName); - Log.Info("[=== Opus: {0}", TS3Client.Full.Audio.Opus.NativeMethods.Info); + Log.Info("[=== Opus: {0}", TS3Client.Audio.Opus.NativeMethods.Info); Log.Info("[==============================================]"); if (SystemData.RuntimeData.Runtime == Runtime.Mono) { @@ -207,7 +207,6 @@ private R InitializeCore() if (!Injector.AllResolved()) { - // TODO detailed log + for inner if Log.Debug("Cyclic core module dependency"); Injector.ForceCyclicResolve(); if (!Injector.AllResolved()) diff --git a/TS3AudioBot/Dependency/DependencyRealm.cs b/TS3AudioBot/Dependency/DependencyRealm.cs index c67e933a..e9fa8fbf 100644 --- a/TS3AudioBot/Dependency/DependencyRealm.cs +++ b/TS3AudioBot/Dependency/DependencyRealm.cs @@ -31,7 +31,8 @@ public DependencyRealm() Util.Init(out modules); } - // TODO doc + /// Will add the type to pool of registered types which can be injected into other dependencies. + /// The type to add public void RegisterType() => RegisterType(typeof(TModule)); private void RegisterType(Type modType) @@ -41,7 +42,13 @@ private void RegisterType(Type modType) registeredTypes.Add(modType); } - // TODO doc + /// Adds an object as a new module to the module pool. + /// The realm will inject all registered types into public propeties as soon as available. + /// The type of the module to add + /// The object to add as a new module. + /// An initialize method that gets called when all dependencies are inected into this module. + /// Note that declaring this param will force the realm to be more strict with this modul + /// and make cyclic dependencies harder to resolve. public void RegisterModule(TMod module, Action onInit = null) where TMod : class { var onInitObject = onInit != null ? new Action(x => onInit((TMod)x)) : null; @@ -54,7 +61,9 @@ private void RegisterModule(object module, Action onInit = null) DoQueueInitialize(false); } - // TODO doc + /// Injects all dependencies into the passe object without registering it as a new module. + /// The object to fill. + /// True if all registered types were available and could be injected, false otherwise. public bool TryInject(object obj) => TryResolve(obj, InitState.SetOnly, false); // Maybe in future update child realm when parent gets updated @@ -66,7 +75,7 @@ private void RegisterModule(object module, Action onInit = null) return child; } - // TODO doc + /// Tries to initialize all modules while allowing undefined behaviour when resolving cyclic dependecies. public void ForceCyclicResolve() { DoQueueInitialize(true); @@ -144,15 +153,18 @@ private bool TryResolve(object obj, InitState state, bool force) return true; } - // TODO doc - public object GetModule() where TModule : class => GetModule(typeof(TModule)); + /// Gets a module assignable to the requested type. + /// The type to get. + /// The object if found, null otherwiese. + public TModule GetModule() where TModule : class => (TModule)GetModule(typeof(TModule)); public object GetModule(Type type) => FindInjectableModule(type, InitState.Done, false)?.Obj; void IInjector.AddModule(object obj) => RegisterModule(obj); public IEnumerable GetAllModules() => modules.Select(x => x.Obj); - // TODO doc + /// Checks if all module could get initialized. + /// True if all are initialized, false otherwise. public bool AllResolved() => modules.All(x => x.Status == InitState.Done); public void Unregister(Type type) diff --git a/TS3AudioBot/ITargetManager.cs b/TS3AudioBot/ITargetManager.cs index ecdc65d4..acb45454 100644 --- a/TS3AudioBot/ITargetManager.cs +++ b/TS3AudioBot/ITargetManager.cs @@ -10,7 +10,7 @@ namespace TS3AudioBot { using TS3Client; - using TS3Client.Full.Audio; + using TS3Client.Audio; /// Used to specify playing mode and active targets to send to. public interface ITargetManager diff --git a/TS3AudioBot/Plugins/Plugin.cs b/TS3AudioBot/Plugins/Plugin.cs index 06104d9e..e3a8df04 100644 --- a/TS3AudioBot/Plugins/Plugin.cs +++ b/TS3AudioBot/Plugins/Plugin.cs @@ -336,7 +336,7 @@ public PluginResponse Start(Bot bot) return PluginResponse.UnknownError; case PluginStatus.NotAvailable: - return PluginResponse.MissingContext; // TODO + return PluginResponse.MissingContext; default: throw new ArgumentOutOfRangeException(); diff --git a/TS3AudioBot/TS3AudioBot.csproj b/TS3AudioBot/TS3AudioBot.csproj index a45d9bd0..483b228b 100644 --- a/TS3AudioBot/TS3AudioBot.csproj +++ b/TS3AudioBot/TS3AudioBot.csproj @@ -91,6 +91,7 @@ + diff --git a/TS3AudioBot/Ts3Full.cs b/TS3AudioBot/Ts3Full.cs index abcf2ce9..d76281be 100644 --- a/TS3AudioBot/Ts3Full.cs +++ b/TS3AudioBot/Ts3Full.cs @@ -16,8 +16,8 @@ namespace TS3AudioBot using System.Linq; using System.Reflection; using TS3Client; + using TS3Client.Audio; using TS3Client.Full; - using TS3Client.Full.Audio; using TS3Client.Helper; using TS3Client.Messages; @@ -28,8 +28,6 @@ internal sealed class Ts3Full : TeamspeakControl, IPlayerConnection private ClientData self; private const Codec SendCodec = Codec.OpusMusic; - private const uint StallCountInterval = 10; - private const uint StallNoErrorCountMax = 5; private static readonly string[] QuitMessages = { "I'm outta here", "You're boring", "Have a nice day", "Bye", "Good night", "Nothing to do here", "Taking a break", "Lorem ipsum dolor sit amet…", @@ -45,11 +43,10 @@ internal sealed class Ts3Full : TeamspeakControl, IPlayerConnection private readonly Ts3FullClientData ts3FullClientData; - private bool isStall; - private uint stallCount; - private uint stallNoErrorCount; private IdentityData identity; + private readonly StallCheckPipe stallCheckPipe; + private readonly ActiveCheckPipe activeCheckPipe; private readonly VolumePipe volumePipe; private readonly FfmpegProducer ffmpegProducer; private readonly PreciseTimedPipe timePipe; @@ -64,6 +61,8 @@ public Ts3Full(Ts3FullClientData tfcd) : base(ClientType.Full) tfcd.PropertyChanged += Tfcd_PropertyChanged; ffmpegProducer = new FfmpegProducer(tfcd); + stallCheckPipe = new StallCheckPipe(); + activeCheckPipe = new ActiveCheckPipe(); volumePipe = new VolumePipe(); encoderPipe = new EncoderPipe(SendCodec) { Bitrate = ts3FullClientData.AudioBitrate * 1000 }; timePipe = new PreciseTimedPipe { ReadBufferSize = encoderPipe.PacketSize }; @@ -71,10 +70,8 @@ public Ts3Full(Ts3FullClientData tfcd) : base(ClientType.Full) TargetPipe = new CustomTargetPipe(tsFullClient); timePipe.InStream = ffmpegProducer; - timePipe.Chain(volumePipe).Chain(encoderPipe).Chain(TargetPipe); + timePipe.Chain(activeCheckPipe).Chain(stallCheckPipe).Chain(volumePipe).Chain(encoderPipe).Chain(TargetPipe); - isStall = false; - stallCount = 0; identity = null; } @@ -188,8 +185,7 @@ private void TsFullClient_OnErrorEvent(object sender, CommandError error) switch (error.Id) { case Ts3ErrorCode.whisper_no_targets: - stallNoErrorCount = 0; - isStall = true; + stallCheckPipe.SetStall(); break; default: @@ -264,44 +260,6 @@ public override R GetSelf() return cd; } - private void AudioSend() - { - // TODO Make a pipe for this - // Save cpu when we know there is noone to send to - bool doSend = true; - - var SendMode = TargetSendMode.None; - switch (SendMode) - { - case TargetSendMode.None: - doSend = false; - break; - case TargetSendMode.Voice: - break; - case TargetSendMode.Whisper: - case TargetSendMode.WhisperGroup: - if (isStall) - { - if (++stallCount % StallCountInterval == 0) - { - stallNoErrorCount++; - if (stallNoErrorCount > StallNoErrorCountMax) - { - stallCount = 0; - isStall = false; - } - } - else - { - doSend = false; - } - } - break; - default: - throw new InvalidOperationException(); - } - } - #region IPlayerConnection public event EventHandler OnSongEnd diff --git a/TS3Client/Audio/ActiveCheckPipe.cs b/TS3Client/Audio/ActiveCheckPipe.cs new file mode 100644 index 00000000..c7beaeba --- /dev/null +++ b/TS3Client/Audio/ActiveCheckPipe.cs @@ -0,0 +1,27 @@ +// TS3Client - A free TeamSpeak3 client implementation +// Copyright (C) 2017 TS3Client contributors +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the Open Software License v. 3.0 +// +// You should have received a copy of the Open Software License along with this +// program. If not, see . + +namespace TS3Client.Audio +{ + using System; + + public class ActiveCheckPipe : IAudioPipe + { + public bool Active => OutStream?.Active ?? false; + public IAudioPassiveConsumer OutStream { get; set; } + + public void Write(Span data, Meta meta) + { + if (OutStream == null || !Active) + return; + + OutStream?.Write(data, meta); + } + } +} diff --git a/TS3Client/Full/Audio/AudioInterfaces.cs b/TS3Client/Audio/AudioInterfaces.cs similarity index 87% rename from TS3Client/Full/Audio/AudioInterfaces.cs rename to TS3Client/Audio/AudioInterfaces.cs index fa7285e8..c9d2a2ec 100644 --- a/TS3Client/Full/Audio/AudioInterfaces.cs +++ b/TS3Client/Audio/AudioInterfaces.cs @@ -7,7 +7,7 @@ // You should have received a copy of the Open Software License along with this // program. If not, see . -namespace TS3Client.Full.Audio +namespace TS3Client.Audio { using System; @@ -28,6 +28,7 @@ public interface IAudioActiveProducer : IAudioStream /// Passive consumer will wait for manually passed audio data. public interface IAudioPassiveConsumer : IAudioStream { + bool Active { get; } void Write(Span data, Meta meta); } /// Active consumer will pull audio data as soon as available. @@ -36,6 +37,9 @@ public interface IAudioActiveConsumer : IAudioStream IAudioPassiveProducer InStream { get; set; } } + // Best practices for pipes: + // - Use Active-Propagiation: `Active => OutStream?.Active ?? false` + // - Alway check `OutStream != null` at begin of Write(...) public interface IAudioPipe : IAudioPassiveConsumer, IAudioActiveProducer { } public interface ISampleInfo diff --git a/TS3Client/Full/Audio/AudioMeta.cs b/TS3Client/Audio/AudioMeta.cs similarity index 97% rename from TS3Client/Full/Audio/AudioMeta.cs rename to TS3Client/Audio/AudioMeta.cs index c88aed91..b9bca565 100644 --- a/TS3Client/Full/Audio/AudioMeta.cs +++ b/TS3Client/Audio/AudioMeta.cs @@ -7,7 +7,7 @@ // You should have received a copy of the Open Software License along with this // program. If not, see . -namespace TS3Client.Full.Audio +namespace TS3Client.Audio { using System.Collections.Generic; using ClientIdT = System.UInt16; diff --git a/TS3Client/Full/Audio/AudioPacketReader.cs b/TS3Client/Audio/AudioPacketReader.cs similarity index 90% rename from TS3Client/Full/Audio/AudioPacketReader.cs rename to TS3Client/Audio/AudioPacketReader.cs index 5346315b..ae39058d 100644 --- a/TS3Client/Full/Audio/AudioPacketReader.cs +++ b/TS3Client/Audio/AudioPacketReader.cs @@ -7,12 +7,14 @@ // You should have received a copy of the Open Software License along with this // program. If not, see . -namespace TS3Client.Full.Audio +namespace TS3Client.Audio { + using Full; using System; public class AudioPacketReader : IAudioPipe { + public bool Active => OutStream?.Active ?? false; public IAudioPassiveConsumer OutStream { get; set; } public void Write(Span data, Meta meta) diff --git a/TS3Client/Full/Audio/AudioPipeExtensions.cs b/TS3Client/Audio/AudioPipeExtensions.cs similarity index 97% rename from TS3Client/Full/Audio/AudioPipeExtensions.cs rename to TS3Client/Audio/AudioPipeExtensions.cs index 0bdd4d14..74b9f549 100644 --- a/TS3Client/Full/Audio/AudioPipeExtensions.cs +++ b/TS3Client/Audio/AudioPipeExtensions.cs @@ -7,7 +7,7 @@ // You should have received a copy of the Open Software License along with this // program. If not, see . -namespace TS3Client.Full.Audio +namespace TS3Client.Audio { using System; diff --git a/TS3Client/Full/Audio/EncoderPipe.cs b/TS3Client/Audio/EncoderPipe.cs similarity index 71% rename from TS3Client/Full/Audio/EncoderPipe.cs rename to TS3Client/Audio/EncoderPipe.cs index af3df79e..6dad0e8b 100644 --- a/TS3Client/Full/Audio/EncoderPipe.cs +++ b/TS3Client/Audio/EncoderPipe.cs @@ -7,13 +7,14 @@ // You should have received a copy of the Open Software License along with this // program. If not, see . -namespace TS3Client.Full.Audio +namespace TS3Client.Audio { using Opus; using System; public class EncoderPipe : IAudioPipe, IDisposable, ISampleInfo { + public bool Active => OutStream?.Active ?? false; public IAudioPassiveConsumer OutStream { get; set; } public Codec Codec { get; } @@ -29,9 +30,8 @@ public class EncoderPipe : IAudioPipe, IDisposable, ISampleInfo private const int SegmentFrames = 960; // todo add upper limit to buffer size and drop everying over - private byte[] soundBuffer = new byte[0]; private byte[] notEncodedBuffer = new byte[0]; - private int notEncodedBufferLength; + private int notEncodedLength; private readonly byte[] encodedBuffer; public EncoderPipe(Codec codec) @@ -79,30 +79,30 @@ public void Write(Span data, Meta meta) if (OutStream == null) return; - int newSoundBufferLength = data.Length + notEncodedBufferLength; - if (newSoundBufferLength > soundBuffer.Length) - soundBuffer = new byte[newSoundBufferLength]; - int soundBufferLength = newSoundBufferLength; - - // TODO use buffer swap to save one copy call - Array.Copy(notEncodedBuffer, 0, soundBuffer, 0, notEncodedBufferLength); - data.CopyTo(new Span(soundBuffer, notEncodedBufferLength)); + int newSoundBufferLength = data.Length + notEncodedLength; + if (newSoundBufferLength >= notEncodedBuffer.Length) + { + var tmpSoundBuffer = new byte[newSoundBufferLength]; + Array.Copy(notEncodedBuffer, 0, tmpSoundBuffer, 0, notEncodedLength); + notEncodedBuffer = tmpSoundBuffer; + } + + var soundBuffer = new Span(notEncodedBuffer); + data.CopyTo(soundBuffer.Slice(notEncodedLength)); - int segmentCount = soundBufferLength / PacketSize; + int segmentCount = newSoundBufferLength / PacketSize; int segmentsEnd = segmentCount * PacketSize; - int newNotEncodedBufferLength = soundBufferLength - segmentsEnd; - if (newNotEncodedBufferLength > notEncodedBuffer.Length) - notEncodedBuffer = new byte[newNotEncodedBufferLength]; - notEncodedBufferLength = newNotEncodedBufferLength; - Array.Copy(soundBuffer, segmentsEnd, notEncodedBuffer, 0, notEncodedBufferLength); + notEncodedLength = newSoundBufferLength - segmentsEnd; for (int i = 0; i < segmentCount; i++) { - var encodedData = opusEncoder.Encode(soundBuffer.AsSpan().Slice(i * PacketSize, PacketSize), PacketSize, encodedBuffer); + var encodedData = opusEncoder.Encode(soundBuffer.Slice(i * PacketSize, PacketSize), PacketSize, encodedBuffer); if (meta != null) meta.Codec = Codec; // TODO copy ? OutStream?.Write(encodedData, meta); } + + soundBuffer.Slice(segmentsEnd, notEncodedLength).CopyTo(soundBuffer); } public TimeSpan GetPlayLength(int bytes) @@ -114,5 +114,12 @@ public void Dispose() { opusEncoder?.Dispose(); } + + private void Swap(ref T a, ref T b) + { + var tmp = a; + a = b; + b = tmp; + } } } diff --git a/TS3Client/Full/Audio/Opus/LICENSE b/TS3Client/Audio/Opus/LICENSE similarity index 100% rename from TS3Client/Full/Audio/Opus/LICENSE rename to TS3Client/Audio/Opus/LICENSE diff --git a/TS3Client/Full/Audio/Opus/NativeMethods.cs b/TS3Client/Audio/Opus/NativeMethods.cs similarity index 99% rename from TS3Client/Full/Audio/Opus/NativeMethods.cs rename to TS3Client/Audio/Opus/NativeMethods.cs index 4e4dff74..e97610b0 100644 --- a/TS3Client/Full/Audio/Opus/NativeMethods.cs +++ b/TS3Client/Audio/Opus/NativeMethods.cs @@ -19,7 +19,7 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -namespace TS3Client.Full.Audio.Opus +namespace TS3Client.Audio.Opus { using Helper; using System; diff --git a/TS3Client/Full/Audio/Opus/OPUS_LICENSE b/TS3Client/Audio/Opus/OPUS_LICENSE similarity index 100% rename from TS3Client/Full/Audio/Opus/OPUS_LICENSE rename to TS3Client/Audio/Opus/OPUS_LICENSE diff --git a/TS3Client/Full/Audio/Opus/OpusDecoder.cs b/TS3Client/Audio/Opus/OpusDecoder.cs similarity index 99% rename from TS3Client/Full/Audio/Opus/OpusDecoder.cs rename to TS3Client/Audio/Opus/OpusDecoder.cs index c9989d72..45c5734a 100644 --- a/TS3Client/Full/Audio/Opus/OpusDecoder.cs +++ b/TS3Client/Audio/Opus/OpusDecoder.cs @@ -19,7 +19,7 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -namespace TS3Client.Full.Audio.Opus +namespace TS3Client.Audio.Opus { using System; diff --git a/TS3Client/Full/Audio/Opus/OpusEncoder.cs b/TS3Client/Audio/Opus/OpusEncoder.cs similarity index 99% rename from TS3Client/Full/Audio/Opus/OpusEncoder.cs rename to TS3Client/Audio/Opus/OpusEncoder.cs index aa6a8f60..470c0b30 100644 --- a/TS3Client/Full/Audio/Opus/OpusEncoder.cs +++ b/TS3Client/Audio/Opus/OpusEncoder.cs @@ -19,7 +19,7 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -namespace TS3Client.Full.Audio.Opus +namespace TS3Client.Audio.Opus { using System; diff --git a/TS3Client/Full/Audio/Opus/README b/TS3Client/Audio/Opus/README similarity index 100% rename from TS3Client/Full/Audio/Opus/README rename to TS3Client/Audio/Opus/README diff --git a/TS3Client/Full/Audio/PreciseAudioTimer.cs b/TS3Client/Audio/PreciseAudioTimer.cs similarity index 98% rename from TS3Client/Full/Audio/PreciseAudioTimer.cs rename to TS3Client/Audio/PreciseAudioTimer.cs index 223f0f05..f5dc248a 100644 --- a/TS3Client/Full/Audio/PreciseAudioTimer.cs +++ b/TS3Client/Audio/PreciseAudioTimer.cs @@ -7,7 +7,7 @@ // You should have received a copy of the Open Software License along with this // program. If not, see . -namespace TS3Client.Full.Audio +namespace TS3Client.Audio { using System; using System.Diagnostics; diff --git a/TS3Client/Full/Audio/PreciseTimedPipe.cs b/TS3Client/Audio/PreciseTimedPipe.cs similarity index 98% rename from TS3Client/Full/Audio/PreciseTimedPipe.cs rename to TS3Client/Audio/PreciseTimedPipe.cs index 2f7a1b49..c04dd787 100644 --- a/TS3Client/Full/Audio/PreciseTimedPipe.cs +++ b/TS3Client/Audio/PreciseTimedPipe.cs @@ -7,7 +7,7 @@ // You should have received a copy of the Open Software License along with this // program. If not, see . -namespace TS3Client.Full.Audio +namespace TS3Client.Audio { using System; using System.Threading; diff --git a/TS3Client/Full/Audio/SplitterPipe.cs b/TS3Client/Audio/SplitterPipe.cs similarity index 92% rename from TS3Client/Full/Audio/SplitterPipe.cs rename to TS3Client/Audio/SplitterPipe.cs index 8a5499bd..1dcf1b02 100644 --- a/TS3Client/Full/Audio/SplitterPipe.cs +++ b/TS3Client/Audio/SplitterPipe.cs @@ -7,13 +7,15 @@ // You should have received a copy of the Open Software License along with this // program. If not, see . -namespace TS3Client.Full.Audio +namespace TS3Client.Audio { using System; using System.Collections.Generic; + using System.Linq; public class SplitterPipe : IAudioPassiveConsumer { + public bool Active => consumerList.Count > 0 && consumerList.Any(x => x.Active); private readonly List safeConsumerList = new List(); private readonly List consumerList = new List(); private bool changed; @@ -64,7 +66,7 @@ public void Write(Span data, Meta meta) return; } - if(buffer.Length < data.Length) + if (buffer.Length < data.Length) buffer = new byte[data.Length]; var bufSpan = new Span(buffer, 0, data.Length); diff --git a/TS3Client/Full/Audio/StaticMetaPipe.cs b/TS3Client/Audio/StaticMetaPipe.cs similarity index 96% rename from TS3Client/Full/Audio/StaticMetaPipe.cs rename to TS3Client/Audio/StaticMetaPipe.cs index b063bc36..d520b90c 100644 --- a/TS3Client/Full/Audio/StaticMetaPipe.cs +++ b/TS3Client/Audio/StaticMetaPipe.cs @@ -7,7 +7,7 @@ // You should have received a copy of the Open Software License along with this // program. If not, see . -namespace TS3Client.Full.Audio +namespace TS3Client.Audio { using System; using System.Collections.Generic; @@ -16,6 +16,7 @@ namespace TS3Client.Full.Audio public class StaticMetaPipe : IAudioPipe { + public bool Active => OutStream?.Active ?? false; public IAudioPassiveConsumer OutStream { get; set; } private MetaOut setMeta = new MetaOut(); diff --git a/TS3Client/Full/Audio/StreamAudioProducer.cs b/TS3Client/Audio/StreamAudioProducer.cs similarity index 95% rename from TS3Client/Full/Audio/StreamAudioProducer.cs rename to TS3Client/Audio/StreamAudioProducer.cs index dc32f141..f068205b 100644 --- a/TS3Client/Full/Audio/StreamAudioProducer.cs +++ b/TS3Client/Audio/StreamAudioProducer.cs @@ -7,7 +7,7 @@ // You should have received a copy of the Open Software License along with this // program. If not, see . -namespace TS3Client.Full.Audio +namespace TS3Client.Audio { using System.IO; diff --git a/TS3Client/Full/Audio/VolumePipe.cs b/TS3Client/Audio/VolumePipe.cs similarity index 94% rename from TS3Client/Full/Audio/VolumePipe.cs rename to TS3Client/Audio/VolumePipe.cs index f007076c..47e7bb76 100644 --- a/TS3Client/Full/Audio/VolumePipe.cs +++ b/TS3Client/Audio/VolumePipe.cs @@ -7,12 +7,13 @@ // You should have received a copy of the Open Software License along with this // program. If not, see . -namespace TS3Client.Full.Audio +namespace TS3Client.Audio { using System; public class VolumePipe : IAudioPipe { + public bool Active => OutStream?.Active ?? false; public float Volume { get; set; } = 1; public IAudioPassiveConsumer OutStream { get; set; } diff --git a/TS3Client/Full/Ts3FullClient.cs b/TS3Client/Full/Ts3FullClient.cs index 55075081..d166ea09 100644 --- a/TS3Client/Full/Ts3FullClient.cs +++ b/TS3Client/Full/Ts3FullClient.cs @@ -469,9 +469,11 @@ public override void Dispose() } #region Audio - /// Incomming voice packets. + /// Receive voice packets. public IAudioPassiveConsumer OutStream { get; set; } - /// Outgoing voice data. + /// When voice data can be sent. + public bool Active => true; // TODO may set to false if no talk power, etc. + /// Send voice data. /// The encoded audio buffer. /// The metadata where to send the packet. public void Write(Span data, Meta meta) diff --git a/TS3Client/Generated/Permissions.cs b/TS3Client/Generated/Permissions.cs index 49431099..14f47564 100644 --- a/TS3Client/Generated/Permissions.cs +++ b/TS3Client/Generated/Permissions.cs @@ -271,7 +271,7 @@ public enum PermissionId : int i_ft_needed_directory_create_power = 244, i_ft_quota_mb_download_per_client = 245, i_ft_quota_mb_upload_per_client = 246, - // ReSharper enable InconsistentNaming, UnusedMember.Global + // ReSharper restore InconsistentNaming, UnusedMember.Global } public static class PerissionInfo @@ -280,7 +280,6 @@ public static string Get(PermissionId permid) { switch (permid) { - // ReSharper disable InconsistentNaming, UnusedMember.Global case PermissionId.undefined: return "Undefined permission"; case PermissionId.unknown : return "May occour on error returns with no associated permission"; case PermissionId.b_serverinstance_help_view : return "Retrieve information about ServerQuery commands"; @@ -530,7 +529,6 @@ public static string Get(PermissionId permid) case PermissionId.i_ft_quota_mb_download_per_client : return "Download quota per client in MByte"; case PermissionId.i_ft_quota_mb_upload_per_client : return "Upload quota per client in MByte"; default: throw Util.UnhandledDefault(permid); - // ReSharper enable InconsistentNaming, UnusedMember.Global } } } diff --git a/TS3Client/Generated/Permissions.tt b/TS3Client/Generated/Permissions.tt index 2d4d2b19..2e2a0578 100644 --- a/TS3Client/Generated/Permissions.tt +++ b/TS3Client/Generated/Permissions.tt @@ -35,7 +35,7 @@ namespace TS3Client // ReSharper disable InconsistentNaming, UnusedMember.Global undefined = -1,<# foreach (var line in data.Skip(1)) { #> <#= line[0] #> = <#= line[2] #>,<# } #> - // ReSharper enable InconsistentNaming, UnusedMember.Global + // ReSharper restore InconsistentNaming, UnusedMember.Global } public static class PerissionInfo @@ -44,11 +44,9 @@ namespace TS3Client { switch (permid) { - // ReSharper disable InconsistentNaming, UnusedMember.Global case PermissionId.undefined: return "Undefined permission";<# foreach (var line in data.Skip(1)) { #> case PermissionId.<#= line[0] #> : return "<#= line[1] #>";<# } #> default: throw Util.UnhandledDefault(permid); - // ReSharper enable InconsistentNaming, UnusedMember.Global } } } diff --git a/TS3Client/Generated/Versions.cs b/TS3Client/Generated/Versions.cs index 6979105a..ab27cb34 100644 --- a/TS3Client/Generated/Versions.cs +++ b/TS3Client/Generated/Versions.cs @@ -54,6 +54,7 @@ public VersionSign(string name, string plattform, string sign) // Many ids implemented from here: https://r4p3.net/threads/client-builds.499/ + // ReSharper disable InconsistentNaming, UnusedMember.Global public static VersionSign VER_WIN_3_0_11 { get; } = new VersionSign("3.0.11 [Build: 1375083581]", ClientPlattform.Windows, "54wPDkfv0kT56UE0lv/LFkFJObH+Q4Irmo4Brfz1EcvjVhj8hJ+RCHcVTZsdKU2XvVvh+VLJpURulEHsAOsyBw=="); public static VersionSign VER_WIN_3_0_19_3 { get; } = new VersionSign("3.0.19.3 [Build: 1466672534]", ClientPlattform.Windows, "a1OYzvM18mrmfUQBUgxYBxYz2DUU6y5k3/mEL6FurzU0y97Bd1FL7+PRpcHyPkg4R+kKAFZ1nhyzbgkGphDWDg=="); @@ -72,6 +73,7 @@ public VersionSign(string name, string plattform, string sign) public static VersionSign VER_WIN_3_X_X { get; } = new VersionSign("3.?.? [Build: 5680278000]", ClientPlattform.Windows, "DX5NIYLvfJEUjuIbCidnoeozxIDRRkpq3I9vVMBmE9L2qnekOoBzSenkzsg2lC9CMv8K5hkEzhr2TYUYSwUXCg=="); public static VersionSign VER_AND_3_X_X { get; } = new VersionSign("3.?.? [Build: 5680278000]", ClientPlattform.Android, "AWb948BY32Z7bpIyoAlQguSmxOGcmjESPceQe1DpW5IZ4+AW1KfTk2VUIYNfUPsxReDJMCtlhVKslzhR2lf0AA=="); public static VersionSign VER_IOS_3_X_X { get; } = new VersionSign("3.?.? [Build: 5680278000]", ClientPlattform.Ios, "XrAf+Buq6Eb0ehEW/niFp06YX+nGGOS0Ke4MoUBzn+cX9q6G5C0A/d5XtgcNMe8r9jJgV/adIYVpsGS3pVlSAA=="); + // ReSharper restore InconsistentNaming, UnusedMember.Global } public enum ClientPlattform diff --git a/TS3Client/Generated/Versions.tt b/TS3Client/Generated/Versions.tt index 69847364..23adc21e 100644 --- a/TS3Client/Generated/Versions.tt +++ b/TS3Client/Generated/Versions.tt @@ -77,8 +77,10 @@ namespace TS3Client.Full // Many ids implemented from here: https://r4p3.net/threads/client-builds.499/ + // ReSharper disable InconsistentNaming, UnusedMember.Global <# foreach (var line in data.Skip(1)) { var ver = dict[line[1]]; #> public static VersionSign VER_<#= ver.plat #>_<#= BuildToFld(line[0]) #> { get; } = new VersionSign("<#= line[0] #>", ClientPlattform.<#= ver.enu #>, "<#= line[2] #>");<# } #> + // ReSharper restore InconsistentNaming, UnusedMember.Global } public enum ClientPlattform diff --git a/TS3Client/TS3Client.csproj b/TS3Client/TS3Client.csproj index 1a90eb13..92774687 100644 --- a/TS3Client/TS3Client.csproj +++ b/TS3Client/TS3Client.csproj @@ -72,14 +72,15 @@ + - - - - - - + + + + + + True True @@ -97,14 +98,14 @@ - - - - - - - - + + + + + + + + @@ -145,9 +146,9 @@ - - - + + + Designer