diff --git a/DCS-SR-Client/Audio/Managers/AudioManager.cs b/DCS-SR-Client/Audio/Managers/AudioManager.cs index 22bdf6455..cf2243e04 100644 --- a/DCS-SR-Client/Audio/Managers/AudioManager.cs +++ b/DCS-SR-Client/Audio/Managers/AudioManager.cs @@ -1,581 +1,602 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; using System.Diagnostics; -using System.Net; -using System.Threading; -using System.Windows; -using Ciribob.DCS.SimpleRadio.Standalone.Client.Audio.Utility; -using Ciribob.DCS.SimpleRadio.Standalone.Client.DSP; -using Ciribob.DCS.SimpleRadio.Standalone.Client.Input; -using Ciribob.DCS.SimpleRadio.Standalone.Client.Network; -using Ciribob.DCS.SimpleRadio.Standalone.Client.Settings; -using Ciribob.DCS.SimpleRadio.Standalone.Client.Singletons; -using Ciribob.DCS.SimpleRadio.Standalone.Common; -using Ciribob.DCS.SimpleRadio.Standalone.Common.Helpers; -using Easy.MessageHub; -using FragLabs.Audio.Codecs; -using NAudio.CoreAudioApi; -using NAudio.Wave; -using NAudio.Wave.SampleProviders; -using NLog; +using System.Net; +using System.Threading; +using System.Windows; +using Ciribob.DCS.SimpleRadio.Standalone.Client.Audio.Utility; +using Ciribob.DCS.SimpleRadio.Standalone.Client.DSP; +using Ciribob.DCS.SimpleRadio.Standalone.Client.Input; +using Ciribob.DCS.SimpleRadio.Standalone.Client.Network; +using Ciribob.DCS.SimpleRadio.Standalone.Client.Settings; +using Ciribob.DCS.SimpleRadio.Standalone.Client.Singletons; +using Ciribob.DCS.SimpleRadio.Standalone.Common; +using Ciribob.DCS.SimpleRadio.Standalone.Common.Helpers; +using Easy.MessageHub; +using FragLabs.Audio.Codecs; +using NAudio.CoreAudioApi; +using NAudio.Wave; +using NAudio.Wave.SampleProviders; +using NLog; using WPFCustomMessageBox; -using Application = FragLabs.Audio.Codecs.Opus.Application; - -namespace Ciribob.DCS.SimpleRadio.Standalone.Client.Audio.Managers -{ - public class AudioManager - { - public static readonly int INPUT_SAMPLE_RATE = 16000; - - // public static readonly int OUTPUT_SAMPLE_RATE = 44100; - public static readonly int INPUT_AUDIO_LENGTH_MS = 40; //TODO test this! Was 80ms but that broke opus - - public static readonly int SEGMENT_FRAMES = (INPUT_SAMPLE_RATE / 1000) * INPUT_AUDIO_LENGTH_MS - ; //640 is 40ms as INPUT_SAMPLE_RATE / 1000 *40 = 640 - - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - - private readonly CachedAudioEffect[] _cachedAudioEffects; - - private readonly ConcurrentDictionary _clientsBufferedAudio = - new ConcurrentDictionary(); - - private readonly ConcurrentDictionary _clientsList; - private MixingSampleProvider _clientAudioMixer; - - private OpusDecoder _decoder; - - //buffer for effects - //plays in parallel with radio output buffer - private RadioAudioProvider[] _effectsOutputBuffer; - - private OpusEncoder _encoder; - - private readonly Queue _micInputQueue = new Queue(SEGMENT_FRAMES * 3); - - private float _speakerBoost = 1.0f; - private volatile bool _stop = true; - private TCPVoiceHandler _tcpVoiceHandler; - private VolumeSampleProviderWithPeak _volumeSampleProvider; - - private WaveIn _waveIn; - private WasapiOut _waveOut; - - public float MicMax { get; set; } = -100; - public float SpeakerMax { get; set; } = -100; - - private ClientStateSingleton _clientStateSingleton = ClientStateSingleton.Instance; - private WasapiOut _micWaveOut; - private BufferedWaveProvider _micWaveOutBuffer; - - private readonly SettingsStore _settings = SettingsStore.Instance; - private Preprocessor _speex; - - public AudioManager(ConcurrentDictionary clientsList) - { - _clientsList = clientsList; - - _cachedAudioEffects = - new CachedAudioEffect[Enum.GetNames(typeof(CachedAudioEffect.AudioEffectTypes)).Length]; - for (var i = 0; i < _cachedAudioEffects.Length; i++) - { - _cachedAudioEffects[i] = new CachedAudioEffect((CachedAudioEffect.AudioEffectTypes) i); - } - } - - public float MicBoost { get; set; } = 1.0f; - - public float SpeakerBoost - { - get { return _speakerBoost; } - set - { - _speakerBoost = value; - if (_volumeSampleProvider != null) - { - _volumeSampleProvider.Volume = value; - } - } - } - - public void StartEncoding(int mic, MMDevice speakers, string guid, InputDeviceManager inputManager, - IPAddress ipAddress, int port, MMDevice micOutput) - { - _stop = false; - - - try - { - _micInputQueue.Clear(); - - InitMixers(); - - InitAudioBuffers(); - - //Audio manager should start / stop and cleanup based on connection successfull and disconnect - //Should use listeners to synchronise all the state - - _waveOut = new WasapiOut(speakers, AudioClientShareMode.Shared, true, 40); - - //add final volume boost to all mixed audio - _volumeSampleProvider = new VolumeSampleProviderWithPeak(_clientAudioMixer, - (peak => SpeakerMax = (float) VolumeConversionHelper.ConvertFloatToDB(peak))); - _volumeSampleProvider.Volume = SpeakerBoost; - - if (speakers.AudioClient.MixFormat.Channels == 1) - { - if (_volumeSampleProvider.WaveFormat.Channels == 2) - { - _waveOut.Init(_volumeSampleProvider.ToMono()); - } - else - { - //already mono - _waveOut.Init(_volumeSampleProvider); - } - } - else - { - if (_volumeSampleProvider.WaveFormat.Channels == 1) - { - _waveOut.Init(_volumeSampleProvider.ToStereo()); - } - else - { - //already stereo - _waveOut.Init(_volumeSampleProvider); - } - } - _waveOut.Play(); - - //opus - _encoder = OpusEncoder.Create(INPUT_SAMPLE_RATE, 1, Application.Voip); - _encoder.ForwardErrorCorrection = false; - _decoder = OpusDecoder.Create(INPUT_SAMPLE_RATE, 1); - _decoder.ForwardErrorCorrection = false; - - //speex - _speex = new Preprocessor(AudioManager.SEGMENT_FRAMES, AudioManager.INPUT_SAMPLE_RATE); - } - catch (Exception ex) - { - Logger.Error(ex, "Error starting audio Output - Quitting! " + ex.Message); - - MessageBox.Show( - $"Problem Initialising Audio Output! Try a different Output device and please post your client log on the forums", - "Audio Output Error", MessageBoxButton.OK, - MessageBoxImage.Error); - - Environment.Exit(1); - } - - if (micOutput != null) // && micOutput !=speakers - { - //TODO handle case when they're the same? - - try - { - _micWaveOut = new WasapiOut(micOutput, AudioClientShareMode.Shared, true, 40); - - _micWaveOutBuffer = new BufferedWaveProvider(new WaveFormat(AudioManager.INPUT_SAMPLE_RATE, 16, 1)); - _micWaveOutBuffer.ReadFully = true; - _micWaveOutBuffer.DiscardOnBufferOverflow = true; - - var sampleProvider = _micWaveOutBuffer.ToSampleProvider(); - - if (micOutput.AudioClient.MixFormat.Channels == 1) - { - if (sampleProvider.WaveFormat.Channels == 2) - { - _micWaveOut.Init(sampleProvider.ToMono()); - } - else - { - //already mono - _micWaveOut.Init(sampleProvider); - } - } - else - { - if (sampleProvider.WaveFormat.Channels == 1) - { - _micWaveOut.Init(sampleProvider.ToStereo()); - } - else - { - //already stereo - _micWaveOut.Init(sampleProvider); - } - } - - _micWaveOut.Play(); - } - catch (Exception ex) - { - Logger.Error(ex, "Error starting mic audio Output - Quitting! " + ex.Message); - - MessageBox.Show( - $"Problem Initialising Mic Audio Output! Try a different Output device and please post your client log on the forums", - "Audio Output Error", MessageBoxButton.OK, - MessageBoxImage.Error); - - Environment.Exit(1); - } - } - - - if (mic != -1) - { - try - { - _waveIn = new WaveIn(WaveCallbackInfo.FunctionCallback()) - { - BufferMilliseconds = INPUT_AUDIO_LENGTH_MS, - DeviceNumber = mic, - }; - - _waveIn.NumberOfBuffers = 2; - _waveIn.DataAvailable += _waveIn_DataAvailable; - _waveIn.WaveFormat = new WaveFormat(INPUT_SAMPLE_RATE, 16, 1); - - _tcpVoiceHandler = - new TCPVoiceHandler(_clientsList, guid, ipAddress, port, _decoder, this, inputManager); - var voiceSenderThread = new Thread(_tcpVoiceHandler.Listen); - - voiceSenderThread.Start(); - - _waveIn.StartRecording(); - - - MessageHub.Instance.Subscribe(RemoveClientBuffer); - } - catch (Exception ex) - { - Logger.Error(ex, "Error starting audio Input - Quitting! " + ex.Message); - - if (Environment.OSVersion.Version.Major == 10) +using Application = FragLabs.Audio.Codecs.Opus.Application; + +namespace Ciribob.DCS.SimpleRadio.Standalone.Client.Audio.Managers +{ + public class AudioManager + { + public static readonly int INPUT_SAMPLE_RATE = 16000; + + // public static readonly int OUTPUT_SAMPLE_RATE = 44100; + public static readonly int INPUT_AUDIO_LENGTH_MS = 40; //TODO test this! Was 80ms but that broke opus + + public static readonly int SEGMENT_FRAMES = (INPUT_SAMPLE_RATE / 1000) * INPUT_AUDIO_LENGTH_MS + ; //640 is 40ms as INPUT_SAMPLE_RATE / 1000 *40 = 640 + + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + + private readonly CachedAudioEffect[] _cachedAudioEffects; + + private readonly ConcurrentDictionary _clientsBufferedAudio = + new ConcurrentDictionary(); + + private readonly ConcurrentDictionary _clientsList; + private MixingSampleProvider _clientAudioMixer; + + private OpusDecoder _decoder; + + //buffer for effects + //plays in parallel with radio output buffer + private RadioAudioProvider[] _effectsOutputBuffer; + + private OpusEncoder _encoder; + + private readonly Queue _micInputQueue = new Queue(SEGMENT_FRAMES * 3); + + private float _speakerBoost = 1.0f; + private volatile bool _stop = true; + private TCPVoiceHandler _tcpVoiceHandler; + private VolumeSampleProviderWithPeak _volumeSampleProvider; + + private WaveIn _waveIn; + private WasapiOut _waveOut; + + public float MicMax { get; set; } = -100; + public float SpeakerMax { get; set; } = -100; + + private ClientStateSingleton _clientStateSingleton = ClientStateSingleton.Instance; + private WasapiOut _micWaveOut; + private BufferedWaveProvider _micWaveOutBuffer; + + private readonly SettingsStore _settings = SettingsStore.Instance; + private Preprocessor _speex; + + public AudioManager(ConcurrentDictionary clientsList) + { + _clientsList = clientsList; + + _cachedAudioEffects = + new CachedAudioEffect[Enum.GetNames(typeof(CachedAudioEffect.AudioEffectTypes)).Length]; + for (var i = 0; i < _cachedAudioEffects.Length; i++) + { + _cachedAudioEffects[i] = new CachedAudioEffect((CachedAudioEffect.AudioEffectTypes) i); + } + } + + public float MicBoost { get; set; } = 1.0f; + + public float SpeakerBoost + { + get { return _speakerBoost; } + set + { + _speakerBoost = value; + if (_volumeSampleProvider != null) + { + _volumeSampleProvider.Volume = value; + } + } + } + + public void StartEncoding(int mic, MMDevice speakers, string guid, InputDeviceManager inputManager, + IPAddress ipAddress, int port, MMDevice micOutput) + { + _stop = false; + + + try + { + _micInputQueue.Clear(); + + InitMixers(); + + InitAudioBuffers(); + + //Audio manager should start / stop and cleanup based on connection successfull and disconnect + //Should use listeners to synchronise all the state + + _waveOut = new WasapiOut(speakers, AudioClientShareMode.Shared, true, 40); + + //add final volume boost to all mixed audio + _volumeSampleProvider = new VolumeSampleProviderWithPeak(_clientAudioMixer, + (peak => SpeakerMax = (float) VolumeConversionHelper.ConvertFloatToDB(peak))); + _volumeSampleProvider.Volume = SpeakerBoost; + + if (speakers.AudioClient.MixFormat.Channels == 1) + { + if (_volumeSampleProvider.WaveFormat.Channels == 2) + { + _waveOut.Init(_volumeSampleProvider.ToMono()); + } + else + { + //already mono + _waveOut.Init(_volumeSampleProvider); + } + } + else + { + if (_volumeSampleProvider.WaveFormat.Channels == 1) { - var messageBoxResult = CustomMessageBox.ShowYesNoCancel( - "Problem initialising Audio Input!\n\nIf you are using Windows 10, this could be caused by your privacy settings (make sure to allow apps to access your microphone).\nAlternatively, try a different Input device and please post your client log to the support Discord server.", - "Audio Input Error", - "OPEN PRIVACY SETTINGS", - "JOIN DISCORD SERVER", - "CLOSE", - MessageBoxImage.Error); - - if (messageBoxResult == MessageBoxResult.Yes) + _waveOut.Init(_volumeSampleProvider.ToStereo()); + } + else + { + //already stereo + _waveOut.Init(_volumeSampleProvider); + } + } + _waveOut.Play(); + + //opus + _encoder = OpusEncoder.Create(INPUT_SAMPLE_RATE, 1, Application.Voip); + _encoder.ForwardErrorCorrection = false; + _decoder = OpusDecoder.Create(INPUT_SAMPLE_RATE, 1); + _decoder.ForwardErrorCorrection = false; + + //speex + _speex = new Preprocessor(AudioManager.SEGMENT_FRAMES, AudioManager.INPUT_SAMPLE_RATE); + } + catch (Exception ex) + { + Logger.Error(ex, "Error starting audio Output - Quitting! " + ex.Message); + + + ShowOutputError("Problem Initialising Audio Output!"); + + + Environment.Exit(1); + } + + if (micOutput != null) // && micOutput !=speakers + { + //TODO handle case when they're the same? + + try + { + _micWaveOut = new WasapiOut(micOutput, AudioClientShareMode.Shared, true, 40); + + _micWaveOutBuffer = new BufferedWaveProvider(new WaveFormat(AudioManager.INPUT_SAMPLE_RATE, 16, 1)); + _micWaveOutBuffer.ReadFully = true; + _micWaveOutBuffer.DiscardOnBufferOverflow = true; + + var sampleProvider = _micWaveOutBuffer.ToSampleProvider(); + + if (micOutput.AudioClient.MixFormat.Channels == 1) + { + if (sampleProvider.WaveFormat.Channels == 2) { - Process.Start("ms-settings:privacy-microphone"); + _micWaveOut.Init(sampleProvider.ToMono()); } - else if (messageBoxResult == MessageBoxResult.No) + else { - Process.Start("https://discord.gg/baw7g3t"); + //already mono + _micWaveOut.Init(sampleProvider); } } else { - var messageBoxResult = CustomMessageBox.ShowYesNo( - "Problem initialising Audio Input!\n\nTry a different Input device and please post your client log to the support Discord server.", - "Audio Input Error", - "JOIN DISCORD SERVER", - "CLOSE", - MessageBoxImage.Error); - - if (messageBoxResult == MessageBoxResult.Yes) + if (sampleProvider.WaveFormat.Channels == 1) + { + _micWaveOut.Init(sampleProvider.ToStereo()); + } + else + { + //already stereo + _micWaveOut.Init(sampleProvider); + } + } + + _micWaveOut.Play(); + } + catch (Exception ex) + { + Logger.Error(ex, "Error starting mic audio Output - Quitting! " + ex.Message); + + ShowOutputError("Problem Initialising Mic Audio Output!"); + + + Environment.Exit(1); + } + } + + + if (mic != -1) + { + try + { + _waveIn = new WaveIn(WaveCallbackInfo.FunctionCallback()) + { + BufferMilliseconds = INPUT_AUDIO_LENGTH_MS, + DeviceNumber = mic, + }; + + _waveIn.NumberOfBuffers = 2; + _waveIn.DataAvailable += _waveIn_DataAvailable; + _waveIn.WaveFormat = new WaveFormat(INPUT_SAMPLE_RATE, 16, 1); + + _tcpVoiceHandler = + new TCPVoiceHandler(_clientsList, guid, ipAddress, port, _decoder, this, inputManager); + var voiceSenderThread = new Thread(_tcpVoiceHandler.Listen); + + voiceSenderThread.Start(); + + _waveIn.StartRecording(); + + + MessageHub.Instance.Subscribe(RemoveClientBuffer); + } + catch (Exception ex) + { + Logger.Error(ex, "Error starting audio Input - Quitting! " + ex.Message); + + ShowInputError("Problem initialising Audio Input!"); + + Environment.Exit(1); + } + } + } + + private void ShowInputError(string message) + { + if (Environment.OSVersion.Version.Major == 10) + { + var messageBoxResult = CustomMessageBox.ShowYesNoCancel( + $"{message}\n\n" + + $"If you are using Windows 10, this could be caused by your privacy settings (make sure to allow apps to access your microphone)." + + $"\nAlternatively, try a different Input device and please post your client log to the support Discord server.", + "Audio Input Error", + "OPEN PRIVACY SETTINGS", + "JOIN DISCORD SERVER", + "CLOSE", + MessageBoxImage.Error); + + if (messageBoxResult == MessageBoxResult.Yes) + { + Process.Start("ms-settings:privacy-microphone"); + } + else if (messageBoxResult == MessageBoxResult.No) + { + Process.Start("https://discord.gg/baw7g3t"); + } + } + else + { + var messageBoxResult = CustomMessageBox.ShowYesNo( + $"{message}\n\n" + + "Try a different Input device and please post your client log to the support Discord server.", + "Audio Input Error", + "JOIN DISCORD SERVER", + "CLOSE", + MessageBoxImage.Error); + + if (messageBoxResult == MessageBoxResult.Yes) + { + Process.Start("https://discord.gg/baw7g3t"); + } + } + } + + private void ShowOutputError(string message) + { + var messageBoxResult = CustomMessageBox.ShowYesNo( + $"{message}\n\n" + + "Try a different output device and please post your client log to the support Discord server.", + "Audio Output Error", + "JOIN DISCORD SERVER", + "CLOSE", + MessageBoxImage.Error); + + if (messageBoxResult == MessageBoxResult.Yes) + { + Process.Start("https://discord.gg/baw7g3t"); + } + } + + private void InitMixers() + { + _clientAudioMixer = new MixingSampleProvider(WaveFormat.CreateIeeeFloatWaveFormat(INPUT_SAMPLE_RATE, 2)); + _clientAudioMixer.ReadFully = true; + } + + private void InitAudioBuffers() + { + _effectsOutputBuffer = new RadioAudioProvider[_clientStateSingleton.DcsPlayerRadioInfo.radios.Length]; + + for (var i = 0; i < _clientStateSingleton.DcsPlayerRadioInfo.radios.Length; i++) + { + _effectsOutputBuffer[i] = new RadioAudioProvider(INPUT_SAMPLE_RATE); + _clientAudioMixer.AddMixerInput(_effectsOutputBuffer[i].VolumeSampleProvider); + } + } + + + public void PlaySoundEffectStartReceive(int transmitOnRadio, bool encrypted, float volume) + { + if (_settings.GetClientSetting(SettingsKeys.RadioRxEffects_Start).BoolValue) + { + var _effectsBuffer = _effectsOutputBuffer[transmitOnRadio]; + + if (encrypted && (_settings.GetClientSetting(SettingsKeys.RadioEncryptionEffects).BoolValue)) + { + _effectsBuffer.VolumeSampleProvider.Volume = volume; + _effectsBuffer.AddAudioSamples( + _cachedAudioEffects[(int) CachedAudioEffect.AudioEffectTypes.KY_58_RX].AudioEffectBytes, + transmitOnRadio); + } + else + { + _effectsBuffer.VolumeSampleProvider.Volume = volume; + _effectsBuffer.AddAudioSamples( + _cachedAudioEffects[(int) CachedAudioEffect.AudioEffectTypes.RADIO_TX].AudioEffectBytes, + transmitOnRadio); + } + } + } + + public void PlaySoundEffectStartTransmit(int transmitOnRadio, bool encrypted, float volume) + { + if (_settings.GetClientSetting(SettingsKeys.RadioTxEffects_Start).BoolValue) + { + var _effectBuffer = _effectsOutputBuffer[transmitOnRadio]; + + + if (encrypted && (_settings.GetClientSetting(SettingsKeys.RadioEncryptionEffects).BoolValue)) + { + _effectBuffer.VolumeSampleProvider.Volume = volume; + _effectBuffer.AddAudioSamples( + _cachedAudioEffects[(int) CachedAudioEffect.AudioEffectTypes.KY_58_TX].AudioEffectBytes, + transmitOnRadio); + } + else + { + _effectBuffer.VolumeSampleProvider.Volume = volume; + _effectBuffer.AddAudioSamples( + _cachedAudioEffects[(int) CachedAudioEffect.AudioEffectTypes.RADIO_TX].AudioEffectBytes, + transmitOnRadio); + } + } + } + + + public void PlaySoundEffectEndReceive(int transmitOnRadio, float volume) + { + if (_settings.GetClientSetting(SettingsKeys.RadioRxEffects_End).BoolValue) + { + var _effectsBuffer = _effectsOutputBuffer[transmitOnRadio]; + + _effectsBuffer.VolumeSampleProvider.Volume = volume; + _effectsBuffer.AddAudioSamples( + _cachedAudioEffects[(int) CachedAudioEffect.AudioEffectTypes.RADIO_RX].AudioEffectBytes, + transmitOnRadio); + } + } + + public void PlaySoundEffectEndTransmit(int transmitOnRadio, float volume) + { + if (_settings.GetClientSetting(SettingsKeys.RadioTxEffects_End).BoolValue) + { + var _effectBuffer = _effectsOutputBuffer[transmitOnRadio]; + + _effectBuffer.VolumeSampleProvider.Volume = volume; + _effectBuffer.AddAudioSamples( + _cachedAudioEffects[(int) CachedAudioEffect.AudioEffectTypes.RADIO_RX].AudioEffectBytes, + transmitOnRadio); + } + } + + //Stopwatch _stopwatch = new Stopwatch(); + private void _waveIn_DataAvailable(object sender, WaveInEventArgs e) + { + // if(_stopwatch.ElapsedMilliseconds > 22) + //Console.WriteLine($"Time: {_stopwatch.ElapsedMilliseconds} - Bytes: {e.BytesRecorded}"); + // _stopwatch.Restart(); + + short[] pcmShort = null; + + + if ((e.BytesRecorded/2 == SEGMENT_FRAMES) && (_micInputQueue.Count == 0)) + { + //perfect! + pcmShort = new short[SEGMENT_FRAMES]; + Buffer.BlockCopy(e.Buffer, 0, pcmShort, 0, e.BytesRecorded); + } + else + { + for (var i = 0; i < e.BytesRecorded; i++) + { + _micInputQueue.Enqueue(e.Buffer[i]); + } + } + + //read out the queue + while ((pcmShort != null) || (_micInputQueue.Count >= AudioManager.SEGMENT_FRAMES)) + { + //null sound buffer so read from the queue + if (pcmShort == null) + { + pcmShort = new short[AudioManager.SEGMENT_FRAMES]; + + for (var i = 0; i < AudioManager.SEGMENT_FRAMES; i++) + { + pcmShort[i] = _micInputQueue.Dequeue(); + } + } + + //null sound buffer so read from the queue + if (pcmShort == null) + { + pcmShort = new short[AudioManager.SEGMENT_FRAMES]; + + for (var i = 0; i < AudioManager.SEGMENT_FRAMES; i++) + { + pcmShort[i] = _micInputQueue.Dequeue(); + } + } + + try + { + //volume boost pre + for (var i = 0; i < pcmShort.Length; i++) + { + // n.b. no clipping test going on here + pcmShort[i] = (short)(pcmShort[i] * MicBoost); + } + + //process with Speex + _speex.Process(new ArraySegment(pcmShort)); + + float max = 0; + for (var i = 0; i < pcmShort.Length; i++) + { + //determine peak + if (pcmShort[i] > max) { - Process.Start("https://discord.gg/baw7g3t"); + + max = pcmShort[i]; } - } - - Environment.Exit(1); - } - } - } - - private void InitMixers() - { - _clientAudioMixer = new MixingSampleProvider(WaveFormat.CreateIeeeFloatWaveFormat(INPUT_SAMPLE_RATE, 2)); - _clientAudioMixer.ReadFully = true; - } - - private void InitAudioBuffers() - { - _effectsOutputBuffer = new RadioAudioProvider[_clientStateSingleton.DcsPlayerRadioInfo.radios.Length]; - - for (var i = 0; i < _clientStateSingleton.DcsPlayerRadioInfo.radios.Length; i++) - { - _effectsOutputBuffer[i] = new RadioAudioProvider(INPUT_SAMPLE_RATE); - _clientAudioMixer.AddMixerInput(_effectsOutputBuffer[i].VolumeSampleProvider); - } - } - - - public void PlaySoundEffectStartReceive(int transmitOnRadio, bool encrypted, float volume) - { - if (_settings.GetClientSetting(SettingsKeys.RadioRxEffects_Start).BoolValue) - { - var _effectsBuffer = _effectsOutputBuffer[transmitOnRadio]; - - if (encrypted && (_settings.GetClientSetting(SettingsKeys.RadioEncryptionEffects).BoolValue)) - { - _effectsBuffer.VolumeSampleProvider.Volume = volume; - _effectsBuffer.AddAudioSamples( - _cachedAudioEffects[(int) CachedAudioEffect.AudioEffectTypes.KY_58_RX].AudioEffectBytes, - transmitOnRadio); - } - else - { - _effectsBuffer.VolumeSampleProvider.Volume = volume; - _effectsBuffer.AddAudioSamples( - _cachedAudioEffects[(int) CachedAudioEffect.AudioEffectTypes.RADIO_TX].AudioEffectBytes, - transmitOnRadio); - } - } - } - - public void PlaySoundEffectStartTransmit(int transmitOnRadio, bool encrypted, float volume) - { - if (_settings.GetClientSetting(SettingsKeys.RadioTxEffects_Start).BoolValue) - { - var _effectBuffer = _effectsOutputBuffer[transmitOnRadio]; - - - if (encrypted && (_settings.GetClientSetting(SettingsKeys.RadioEncryptionEffects).BoolValue)) - { - _effectBuffer.VolumeSampleProvider.Volume = volume; - _effectBuffer.AddAudioSamples( - _cachedAudioEffects[(int) CachedAudioEffect.AudioEffectTypes.KY_58_TX].AudioEffectBytes, - transmitOnRadio); - } - else - { - _effectBuffer.VolumeSampleProvider.Volume = volume; - _effectBuffer.AddAudioSamples( - _cachedAudioEffects[(int) CachedAudioEffect.AudioEffectTypes.RADIO_TX].AudioEffectBytes, - transmitOnRadio); - } - } - } - - - public void PlaySoundEffectEndReceive(int transmitOnRadio, float volume) - { - if (_settings.GetClientSetting(SettingsKeys.RadioRxEffects_End).BoolValue) - { - var _effectsBuffer = _effectsOutputBuffer[transmitOnRadio]; - - _effectsBuffer.VolumeSampleProvider.Volume = volume; - _effectsBuffer.AddAudioSamples( - _cachedAudioEffects[(int) CachedAudioEffect.AudioEffectTypes.RADIO_RX].AudioEffectBytes, - transmitOnRadio); - } - } - - public void PlaySoundEffectEndTransmit(int transmitOnRadio, float volume) - { - if (_settings.GetClientSetting(SettingsKeys.RadioTxEffects_End).BoolValue) - { - var _effectBuffer = _effectsOutputBuffer[transmitOnRadio]; - - _effectBuffer.VolumeSampleProvider.Volume = volume; - _effectBuffer.AddAudioSamples( - _cachedAudioEffects[(int) CachedAudioEffect.AudioEffectTypes.RADIO_RX].AudioEffectBytes, - transmitOnRadio); - } - } - - //Stopwatch _stopwatch = new Stopwatch(); - private void _waveIn_DataAvailable(object sender, WaveInEventArgs e) - { - // if(_stopwatch.ElapsedMilliseconds > 22) - //Console.WriteLine($"Time: {_stopwatch.ElapsedMilliseconds} - Bytes: {e.BytesRecorded}"); - // _stopwatch.Restart(); - - short[] pcmShort = null; - - - if ((e.BytesRecorded/2 == SEGMENT_FRAMES) && (_micInputQueue.Count == 0)) - { - //perfect! - pcmShort = new short[SEGMENT_FRAMES]; - Buffer.BlockCopy(e.Buffer, 0, pcmShort, 0, e.BytesRecorded); - } - else - { - for (var i = 0; i < e.BytesRecorded; i++) - { - _micInputQueue.Enqueue(e.Buffer[i]); - } - } - - //read out the queue - while ((pcmShort != null) || (_micInputQueue.Count >= AudioManager.SEGMENT_FRAMES)) - { - //null sound buffer so read from the queue - if (pcmShort == null) - { - pcmShort = new short[AudioManager.SEGMENT_FRAMES]; - - for (var i = 0; i < AudioManager.SEGMENT_FRAMES; i++) - { - pcmShort[i] = _micInputQueue.Dequeue(); - } - } - - //null sound buffer so read from the queue - if (pcmShort == null) - { - pcmShort = new short[AudioManager.SEGMENT_FRAMES]; - - for (var i = 0; i < AudioManager.SEGMENT_FRAMES; i++) - { - pcmShort[i] = _micInputQueue.Dequeue(); - } - } - - try - { - //volume boost pre - for (var i = 0; i < pcmShort.Length; i++) - { - // n.b. no clipping test going on here - pcmShort[i] = (short)(pcmShort[i] * MicBoost); - } - - //process with Speex - _speex.Process(new ArraySegment(pcmShort)); - - float max = 0; - for (var i = 0; i < pcmShort.Length; i++) - { - //determine peak - if (pcmShort[i] > max) - { - - max = pcmShort[i]; - } - } - //convert to dB - MicMax = (float)VolumeConversionHelper.ConvertFloatToDB(max / 32768F); - - var pcmBytes = new byte[pcmShort.Length * 2]; - Buffer.BlockCopy(pcmShort, 0, pcmBytes, 0, pcmBytes.Length); - - //encode as opus bytes - int len; - var buff = _encoder.Encode(pcmBytes, pcmBytes.Length, out len); - - if ((_tcpVoiceHandler != null) && (buff != null) && (len > 0)) - { - //create copy with small buffer - var encoded = new byte[len]; - - Buffer.BlockCopy(buff, 0, encoded, 0, len); - - // Console.WriteLine("Sending: " + e.BytesRecorded); - if (_tcpVoiceHandler.Send(encoded, len)) - { - //send audio so play over local too - _micWaveOutBuffer?.AddSamples(pcmBytes, 0, pcmBytes.Length); - } - } - else - { - Logger.Error($"Invalid Bytes for Encoding - {e.BytesRecorded} should be {SEGMENT_FRAMES} "); - } - } - catch (Exception ex) - { - Logger.Error(ex, "Error encoding Opus! " + ex.Message); - } - - pcmShort = null; - } - } - - public void StopEncoding() - { - - _waveIn?.StopRecording(); - _waveIn?.Dispose(); - _waveIn = null; - - _waveOut?.Stop(); - _waveOut?.Dispose(); - _waveOut = null; - - _micWaveOut?.Stop(); - _micWaveOut?.Dispose(); - _micWaveOut = null; - - _volumeSampleProvider = null; - _clientAudioMixer?.RemoveAllMixerInputs(); - _clientAudioMixer = null; - - _clientsBufferedAudio.Clear(); - - _encoder?.Dispose(); - _encoder = null; - - _decoder?.Dispose(); - _decoder = null; - - _tcpVoiceHandler?.RequestStop(); - _tcpVoiceHandler = null; - - _speex?.Dispose(); - _speex = null; - - _stop = true; - - SpeakerMax = -100; - MicMax = -100; - - _effectsOutputBuffer = null; - - MessageHub.Instance.ClearSubscriptions(); - } - - public void AddClientAudio(ClientAudio audio) - { - //sort out effects! - - //16bit PCM Audio - //TODO: Clean - remove if we havent received audio in a while? - // If we have recieved audio, create a new buffered audio and read it - ClientAudioProvider client = null; - if (_clientsBufferedAudio.ContainsKey(audio.ClientGuid)) - { - client = _clientsBufferedAudio[audio.ClientGuid]; - } - else - { - client = new ClientAudioProvider(); - _clientsBufferedAudio[audio.ClientGuid] = client; - - _clientAudioMixer.AddMixerInput(client.SampleProvider); - } - - client.AddClientAudioSamples(audio); - } - - private void RemoveClientBuffer(SRClient srClient) - { - ClientAudioProvider clientAudio = null; - _clientsBufferedAudio.TryRemove(srClient.ClientGuid, out clientAudio); - - if (clientAudio != null) - { - try - { - _clientAudioMixer.RemoveMixerInput(clientAudio.SampleProvider); - } - catch (Exception ex) - { - Logger.Error(ex, "Error removing client input"); - } - } - } - } + } + //convert to dB + MicMax = (float)VolumeConversionHelper.ConvertFloatToDB(max / 32768F); + + var pcmBytes = new byte[pcmShort.Length * 2]; + Buffer.BlockCopy(pcmShort, 0, pcmBytes, 0, pcmBytes.Length); + + //encode as opus bytes + int len; + var buff = _encoder.Encode(pcmBytes, pcmBytes.Length, out len); + + if ((_tcpVoiceHandler != null) && (buff != null) && (len > 0)) + { + //create copy with small buffer + var encoded = new byte[len]; + + Buffer.BlockCopy(buff, 0, encoded, 0, len); + + // Console.WriteLine("Sending: " + e.BytesRecorded); + if (_tcpVoiceHandler.Send(encoded, len)) + { + //send audio so play over local too + _micWaveOutBuffer?.AddSamples(pcmBytes, 0, pcmBytes.Length); + } + } + else + { + Logger.Error($"Invalid Bytes for Encoding - {e.BytesRecorded} should be {SEGMENT_FRAMES} "); + } + } + catch (Exception ex) + { + Logger.Error(ex, "Error encoding Opus! " + ex.Message); + } + + pcmShort = null; + } + } + + public void StopEncoding() + { + + _waveIn?.StopRecording(); + _waveIn?.Dispose(); + _waveIn = null; + + _waveOut?.Stop(); + _waveOut?.Dispose(); + _waveOut = null; + + _micWaveOut?.Stop(); + _micWaveOut?.Dispose(); + _micWaveOut = null; + + _volumeSampleProvider = null; + _clientAudioMixer?.RemoveAllMixerInputs(); + _clientAudioMixer = null; + + _clientsBufferedAudio.Clear(); + + _encoder?.Dispose(); + _encoder = null; + + _decoder?.Dispose(); + _decoder = null; + + _tcpVoiceHandler?.RequestStop(); + _tcpVoiceHandler = null; + + _speex?.Dispose(); + _speex = null; + + _stop = true; + + SpeakerMax = -100; + MicMax = -100; + + _effectsOutputBuffer = null; + + MessageHub.Instance.ClearSubscriptions(); + } + + public void AddClientAudio(ClientAudio audio) + { + //sort out effects! + + //16bit PCM Audio + //TODO: Clean - remove if we havent received audio in a while? + // If we have recieved audio, create a new buffered audio and read it + ClientAudioProvider client = null; + if (_clientsBufferedAudio.ContainsKey(audio.ClientGuid)) + { + client = _clientsBufferedAudio[audio.ClientGuid]; + } + else + { + client = new ClientAudioProvider(); + _clientsBufferedAudio[audio.ClientGuid] = client; + + _clientAudioMixer.AddMixerInput(client.SampleProvider); + } + + client.AddClientAudioSamples(audio); + } + + private void RemoveClientBuffer(SRClient srClient) + { + ClientAudioProvider clientAudio = null; + _clientsBufferedAudio.TryRemove(srClient.ClientGuid, out clientAudio); + + if (clientAudio != null) + { + try + { + _clientAudioMixer.RemoveMixerInput(clientAudio.SampleProvider); + } + catch (Exception ex) + { + Logger.Error(ex, "Error removing client input"); + } + } + } + } } \ No newline at end of file diff --git a/DCS-SR-Client/Audio/Managers/AudioPreview.cs b/DCS-SR-Client/Audio/Managers/AudioPreview.cs index 7e0abb0fa..e28b91574 100644 --- a/DCS-SR-Client/Audio/Managers/AudioPreview.cs +++ b/DCS-SR-Client/Audio/Managers/AudioPreview.cs @@ -1,336 +1,346 @@ -using System; -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Diagnostics; -using System.Windows; -using Ciribob.DCS.SimpleRadio.Standalone.Client.Audio.Managers; -using Ciribob.DCS.SimpleRadio.Standalone.Client.Audio.Utility; -using Ciribob.DCS.SimpleRadio.Standalone.Client.DSP; -using Ciribob.DCS.SimpleRadio.Standalone.Client.Settings; -using Ciribob.DCS.SimpleRadio.Standalone.Common; -using Ciribob.DCS.SimpleRadio.Standalone.Common.Helpers; -using FragLabs.Audio.Codecs; -using NAudio.CoreAudioApi; -using NAudio.Wave; -using NAudio.Wave.SampleProviders; -using NLog; -using WPFCustomMessageBox; - -namespace Ciribob.DCS.SimpleRadio.Standalone.Client.Audio -{ - internal class AudioPreview - { - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - private BufferedWaveProvider _playBuffer; - private WaveIn _waveIn; - private WasapiOut _waveOut; - - private VolumeSampleProviderWithPeak _volumeSampleProvider; - private BufferedWaveProvider _buffBufferedWaveProvider; - - public float MicBoost { get; set; } = 1.0f; - - private float _speakerBoost = 1.0f; - private OpusEncoder _encoder; - private OpusDecoder _decoder; - - private Preprocessor _speex; - - private readonly Queue _micInputQueue = new Queue(AudioManager.SEGMENT_FRAMES * 3); - private WaveFileWriter _waveFile; - private SettingsStore _settings; - - public float SpeakerBoost - { - get { return _speakerBoost; } - set - { - _speakerBoost = value; - if (_volumeSampleProvider != null) - { - _volumeSampleProvider.Volume = value; - } - } - } - - public float MicMax { get; set; } = -100; - public float SpeakerMax { get; set; } = -100; - - public void StartPreview(int mic, MMDevice speakers) - { - try - { - _settings = SettingsStore.Instance; - _waveOut = new WasapiOut(speakers, AudioClientShareMode.Shared, true, 40); - - _buffBufferedWaveProvider = - new BufferedWaveProvider(new WaveFormat(AudioManager.INPUT_SAMPLE_RATE, 16, 1)); - _buffBufferedWaveProvider.ReadFully = true; - _buffBufferedWaveProvider.DiscardOnBufferOverflow = true; - - RadioFilter filter = new RadioFilter(_buffBufferedWaveProvider.ToSampleProvider()); - - //add final volume boost to all mixed audio - _volumeSampleProvider = new VolumeSampleProviderWithPeak(filter, - (peak => SpeakerMax = (float) VolumeConversionHelper.ConvertFloatToDB(peak))); - _volumeSampleProvider.Volume = SpeakerBoost; - - if (speakers.AudioClient.MixFormat.Channels == 1) - { - if (_volumeSampleProvider.WaveFormat.Channels == 2) - { - _waveOut.Init(_volumeSampleProvider.ToMono()); - } - else - { - //already mono - _waveOut.Init(_volumeSampleProvider); - } - } - else - { - if (_volumeSampleProvider.WaveFormat.Channels == 1) - { - _waveOut.Init(_volumeSampleProvider.ToStereo()); - } - else - { - //already stereo - _waveOut.Init(_volumeSampleProvider); - } - } - - _waveOut.Play(); - } - catch (Exception ex) - { - Logger.Error(ex, "Error starting audio Output - Quitting! " + ex.Message); - - var messageBoxResult = CustomMessageBox.ShowYesNo( - "Problem initialising Audio Output!\n\nTry a different Output device and please post your client log to the support Discord server.\n\nJoin support Discord server now?", - "Audio Output Error", +using System.Windows; +using Ciribob.DCS.SimpleRadio.Standalone.Client.Audio.Managers; +using Ciribob.DCS.SimpleRadio.Standalone.Client.Audio.Utility; +using Ciribob.DCS.SimpleRadio.Standalone.Client.DSP; +using Ciribob.DCS.SimpleRadio.Standalone.Client.Settings; +using Ciribob.DCS.SimpleRadio.Standalone.Common; +using Ciribob.DCS.SimpleRadio.Standalone.Common.Helpers; +using FragLabs.Audio.Codecs; +using NAudio.CoreAudioApi; +using NAudio.Wave; +using NAudio.Wave.SampleProviders; +using NLog; +using WPFCustomMessageBox; + +namespace Ciribob.DCS.SimpleRadio.Standalone.Client.Audio +{ + internal class AudioPreview + { + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + private BufferedWaveProvider _playBuffer; + private WaveIn _waveIn; + private WasapiOut _waveOut; + + private VolumeSampleProviderWithPeak _volumeSampleProvider; + private BufferedWaveProvider _buffBufferedWaveProvider; + + public float MicBoost { get; set; } = 1.0f; + + private float _speakerBoost = 1.0f; + private OpusEncoder _encoder; + private OpusDecoder _decoder; + + private Preprocessor _speex; + + private readonly Queue _micInputQueue = new Queue(AudioManager.SEGMENT_FRAMES * 3); + private WaveFileWriter _waveFile; + private SettingsStore _settings; + + public float SpeakerBoost + { + get { return _speakerBoost; } + set + { + _speakerBoost = value; + if (_volumeSampleProvider != null) + { + _volumeSampleProvider.Volume = value; + } + } + } + + public float MicMax { get; set; } = -100; + public float SpeakerMax { get; set; } = -100; + + public void StartPreview(int mic, MMDevice speakers) + { + try + { + _settings = SettingsStore.Instance; + _waveOut = new WasapiOut(speakers, AudioClientShareMode.Shared, true, 40); + + _buffBufferedWaveProvider = + new BufferedWaveProvider(new WaveFormat(AudioManager.INPUT_SAMPLE_RATE, 16, 1)); + _buffBufferedWaveProvider.ReadFully = true; + _buffBufferedWaveProvider.DiscardOnBufferOverflow = true; + + RadioFilter filter = new RadioFilter(_buffBufferedWaveProvider.ToSampleProvider()); + + //add final volume boost to all mixed audio + _volumeSampleProvider = new VolumeSampleProviderWithPeak(filter, + (peak => SpeakerMax = (float) VolumeConversionHelper.ConvertFloatToDB(peak))); + _volumeSampleProvider.Volume = SpeakerBoost; + + if (speakers.AudioClient.MixFormat.Channels == 1) + { + if (_volumeSampleProvider.WaveFormat.Channels == 2) + { + _waveOut.Init(_volumeSampleProvider.ToMono()); + } + else + { + //already mono + _waveOut.Init(_volumeSampleProvider); + } + } + else + { + if (_volumeSampleProvider.WaveFormat.Channels == 1) + { + _waveOut.Init(_volumeSampleProvider.ToStereo()); + } + else + { + //already stereo + _waveOut.Init(_volumeSampleProvider); + } + } + + _waveOut.Play(); + } + catch (Exception ex) + { + Logger.Error(ex, "Error starting audio Output - Quitting! " + ex.Message); + + ShowOutputError("Problem Initialising Audio Output!"); + + Environment.Exit(1); + } + + try + { + _speex = new Preprocessor(AudioManager.SEGMENT_FRAMES, AudioManager.INPUT_SAMPLE_RATE); + //opus + _encoder = OpusEncoder.Create(AudioManager.INPUT_SAMPLE_RATE, 1, + FragLabs.Audio.Codecs.Opus.Application.Voip); + _encoder.ForwardErrorCorrection = false; + _decoder = OpusDecoder.Create(AudioManager.INPUT_SAMPLE_RATE, 1); + _decoder.ForwardErrorCorrection = false; + + _waveIn = new WaveIn(WaveCallbackInfo.FunctionCallback()) + { + BufferMilliseconds = AudioManager.INPUT_AUDIO_LENGTH_MS, + DeviceNumber = mic + }; + + _waveIn.NumberOfBuffers = 2; + _waveIn.DataAvailable += _waveIn_DataAvailable; + _waveIn.WaveFormat = new WaveFormat(AudioManager.INPUT_SAMPLE_RATE, 16, 1); + + //debug wave file + //_waveFile = new WaveFileWriter(@"C:\Temp\Test-Preview.wav", _waveIn.WaveFormat); + + _waveIn.StartRecording(); + } + catch (Exception ex) + { + Logger.Error(ex, "Error starting audio Input - Quitting! " + ex.Message); + ShowInputError(); + + Environment.Exit(1); + } + } + + private void ShowInputError() + { + if (Environment.OSVersion.Version.Major == 10) + { + var messageBoxResult = CustomMessageBox.ShowYesNoCancel( + "Problem initialising Audio Input!\n\nIf you are using Windows 10, this could be caused by your privacy settings (make sure to allow apps to access your microphone).\nAlternatively, try a different Input device and please post your client log to the support Discord server.", + "Audio Input Error", + "OPEN PRIVACY SETTINGS", "JOIN DISCORD SERVER", - "CLOSE", - MessageBoxImage.Error); - + "CLOSE", + MessageBoxImage.Error); + if (messageBoxResult == MessageBoxResult.Yes) { - Process.Start("https://discord.gg/baw7g3t"); - } - - Environment.Exit(1); - } - - try - { - _speex = new Preprocessor(AudioManager.SEGMENT_FRAMES, AudioManager.INPUT_SAMPLE_RATE); - //opus - _encoder = OpusEncoder.Create(AudioManager.INPUT_SAMPLE_RATE, 1, - FragLabs.Audio.Codecs.Opus.Application.Voip); - _encoder.ForwardErrorCorrection = false; - _decoder = OpusDecoder.Create(AudioManager.INPUT_SAMPLE_RATE, 1); - _decoder.ForwardErrorCorrection = false; - - _waveIn = new WaveIn(WaveCallbackInfo.FunctionCallback()) - { - BufferMilliseconds = AudioManager.INPUT_AUDIO_LENGTH_MS, - DeviceNumber = mic - }; - - _waveIn.NumberOfBuffers = 2; - _waveIn.DataAvailable += _waveIn_DataAvailable; - _waveIn.WaveFormat = new WaveFormat(AudioManager.INPUT_SAMPLE_RATE, 16, 1); - - //debug wave file - //_waveFile = new WaveFileWriter(@"C:\Temp\Test-Preview.wav", _waveIn.WaveFormat); - - _waveIn.StartRecording(); - } - catch (Exception ex) - { - Logger.Error(ex, "Error starting audio Input - Quitting! " + ex.Message); - - if (Environment.OSVersion.Version.Major == 10) + Process.Start("ms-settings:privacy-microphone"); + } + else if (messageBoxResult == MessageBoxResult.No) { - var messageBoxResult = CustomMessageBox.ShowYesNoCancel( - "Problem initialising Audio Input!\n\nIf you are using Windows 10, this could be caused by your privacy settings (make sure to allow apps to access your microphone).\nAlternatively, try a different Input device and please post your client log to the support Discord server.", - "Audio Input Error", - "OPEN PRIVACY SETTINGS", - "JOIN DISCORD SERVER", - "CLOSE", + Process.Start("https://discord.gg/baw7g3t"); + } + } + else + { + var messageBoxResult = CustomMessageBox.ShowYesNo( + "Problem initialising Audio Input!\n\nTry a different Input device and please post your client log to the support Discord server.", + "Audio Input Error", + "JOIN DISCORD SERVER", + "CLOSE", MessageBoxImage.Error); - if (messageBoxResult == MessageBoxResult.Yes) + if (messageBoxResult == MessageBoxResult.Yes) + { + Process.Start("https://discord.gg/baw7g3t"); + } + } + } + + private void ShowOutputError(string message) + { + var messageBoxResult = CustomMessageBox.ShowYesNo( + $"{message}\n\n" + + "Try a different output device and please post your client log to the support Discord server.", + "Audio Output Error", + "JOIN DISCORD SERVER", + "CLOSE", + MessageBoxImage.Error); + + if (messageBoxResult == MessageBoxResult.Yes) + { + Process.Start("https://discord.gg/baw7g3t"); + } + } + + private void _waveIn_DataAvailable(object sender, WaveInEventArgs e) + { + //fill sound buffer + + short[] pcmShort = null; + + + if ((e.BytesRecorded / 2 == AudioManager.SEGMENT_FRAMES) && (_micInputQueue.Count == 0)) + { + //perfect! + pcmShort = new short[AudioManager.SEGMENT_FRAMES]; + Buffer.BlockCopy(e.Buffer, 0, pcmShort, 0, e.BytesRecorded); + } + else + { + for (var i = 0; i < e.BytesRecorded; i++) + { + _micInputQueue.Enqueue(e.Buffer[i]); + } + } + + //read out the queue + while ((pcmShort != null) || (_micInputQueue.Count >= AudioManager.SEGMENT_FRAMES)) + { + //null sound buffer so read from the queue + if (pcmShort == null) + { + pcmShort = new short[AudioManager.SEGMENT_FRAMES]; + + for (var i = 0; i < AudioManager.SEGMENT_FRAMES; i++) { - Process.Start("ms-settings:privacy-microphone"); + pcmShort[i] = _micInputQueue.Dequeue(); } - else if (messageBoxResult == MessageBoxResult.No) + } + + try + { + //volume boost pre +// for (var i = 0; i < pcmShort.Length; i++) +// { +// //clipping tests thanks to Coug4r +// if (_settings.GetClientSetting(SettingsKeys.RadioEffects).BoolValue) +// { +// if (pcmShort[i] > 4000) +// { +// pcmShort[i] = 4000; +// } +// else if (pcmShort[i] < -4000) +// { +// pcmShort[i] = -4000; +// } +// } +// +// // n.b. no clipping test going on here +// //pcmShort[i] = (short) (pcmShort[i] * MicBoost); +// } + + //process with Speex + _speex.Process(new ArraySegment(pcmShort)); + + float max = 0; + for (var i = 0; i < pcmShort.Length; i++) { - Process.Start("https://discord.gg/baw7g3t"); + + + //determine peak + if (pcmShort[i] > max) + { + + max = pcmShort[i]; + + } } - } - else - { - var messageBoxResult = CustomMessageBox.ShowYesNo( - "Problem initialising Audio Input!\n\nTry a different Input device and please post your client log to the support Discord server.", - "Audio Input Error", - "JOIN DISCORD SERVER", - "CLOSE", - MessageBoxImage.Error); + //convert to dB + MicMax = (float)VolumeConversionHelper.ConvertFloatToDB(max / 32768F); - if (messageBoxResult == MessageBoxResult.Yes) + var pcmBytes = new byte[pcmShort.Length * 2]; + Buffer.BlockCopy(pcmShort, 0, pcmBytes, 0, pcmBytes.Length); + + // _buffBufferedWaveProvider.AddSamples(pcmBytes, 0, pcmBytes.Length); + //encode as opus bytes + int len; + //need to get framing right for opus - + var buff = _encoder.Encode(pcmBytes, pcmBytes.Length, out len); + + if ((buff != null) && (len > 0)) { - Process.Start("https://discord.gg/baw7g3t"); + //create copy with small buffer + var encoded = new byte[len]; + + Buffer.BlockCopy(buff, 0, encoded, 0, len); + + var decodedLength = 0; + //now decode + var decodedBytes = _decoder.Decode(encoded, len, out decodedLength); + + _buffBufferedWaveProvider.AddSamples(decodedBytes, 0, decodedLength); + + //_waveFile.Write(decodedBytes, 0,decodedLength); + // _waveFile.Flush(); + } + else + { + Logger.Error( + $"Invalid Bytes for Encoding - {e.BytesRecorded} should be {AudioManager.SEGMENT_FRAMES} "); } - } - - Environment.Exit(1); - } - } - - private void _waveIn_DataAvailable(object sender, WaveInEventArgs e) - { - //fill sound buffer - - short[] pcmShort = null; - - - if ((e.BytesRecorded / 2 == AudioManager.SEGMENT_FRAMES) && (_micInputQueue.Count == 0)) - { - //perfect! - pcmShort = new short[AudioManager.SEGMENT_FRAMES]; - Buffer.BlockCopy(e.Buffer, 0, pcmShort, 0, e.BytesRecorded); - } - else - { - for (var i = 0; i < e.BytesRecorded; i++) - { - _micInputQueue.Enqueue(e.Buffer[i]); - } - } - - //read out the queue - while ((pcmShort != null) || (_micInputQueue.Count >= AudioManager.SEGMENT_FRAMES)) - { - //null sound buffer so read from the queue - if (pcmShort == null) - { - pcmShort = new short[AudioManager.SEGMENT_FRAMES]; - - for (var i = 0; i < AudioManager.SEGMENT_FRAMES; i++) - { - pcmShort[i] = _micInputQueue.Dequeue(); - } - } - - try - { - //volume boost pre -// for (var i = 0; i < pcmShort.Length; i++) -// { -// //clipping tests thanks to Coug4r -// if (_settings.GetClientSetting(SettingsKeys.RadioEffects).BoolValue) -// { -// if (pcmShort[i] > 4000) -// { -// pcmShort[i] = 4000; -// } -// else if (pcmShort[i] < -4000) -// { -// pcmShort[i] = -4000; -// } -// } -// -// // n.b. no clipping test going on here -// //pcmShort[i] = (short) (pcmShort[i] * MicBoost); -// } - - //process with Speex - _speex.Process(new ArraySegment(pcmShort)); - - float max = 0; - for (var i = 0; i < pcmShort.Length; i++) - { - - - //determine peak - if (pcmShort[i] > max) - { - - max = pcmShort[i]; - - } - } - //convert to dB - MicMax = (float)VolumeConversionHelper.ConvertFloatToDB(max / 32768F); - - var pcmBytes = new byte[pcmShort.Length * 2]; - Buffer.BlockCopy(pcmShort, 0, pcmBytes, 0, pcmBytes.Length); - - // _buffBufferedWaveProvider.AddSamples(pcmBytes, 0, pcmBytes.Length); - //encode as opus bytes - int len; - //need to get framing right for opus - - var buff = _encoder.Encode(pcmBytes, pcmBytes.Length, out len); - - if ((buff != null) && (len > 0)) - { - //create copy with small buffer - var encoded = new byte[len]; - - Buffer.BlockCopy(buff, 0, encoded, 0, len); - - var decodedLength = 0; - //now decode - var decodedBytes = _decoder.Decode(encoded, len, out decodedLength); - - _buffBufferedWaveProvider.AddSamples(decodedBytes, 0, decodedLength); - - //_waveFile.Write(decodedBytes, 0,decodedLength); - // _waveFile.Flush(); - } - else - { - Logger.Error( - $"Invalid Bytes for Encoding - {e.BytesRecorded} should be {AudioManager.SEGMENT_FRAMES} "); - } - } - catch (Exception ex) - { - Logger.Error(ex, "Error encoding Opus! " + ex.Message); - } - - pcmShort = null; - } - } - - public void StopEncoding() - { - _waveIn?.Dispose(); - _waveIn = null; - - _waveOut?.Dispose(); - _waveOut = null; - - _playBuffer?.ClearBuffer(); - _playBuffer = null; - - _encoder?.Dispose(); - _encoder = null; - - _decoder?.Dispose(); - _decoder = null; - - _playBuffer?.ClearBuffer(); - _playBuffer = null; - - _speex?.Dispose(); - _speex = null; - - _waveFile?.Dispose(); - _waveFile = null; - - SpeakerMax = -100; - MicMax = -100; - } - } + } + catch (Exception ex) + { + Logger.Error(ex, "Error encoding Opus! " + ex.Message); + } + + pcmShort = null; + } + } + + public void StopEncoding() + { + _waveIn?.Dispose(); + _waveIn = null; + + _waveOut?.Dispose(); + _waveOut = null; + + _playBuffer?.ClearBuffer(); + _playBuffer = null; + + _encoder?.Dispose(); + _encoder = null; + + _decoder?.Dispose(); + _decoder = null; + + _playBuffer?.ClearBuffer(); + _playBuffer = null; + + _speex?.Dispose(); + _speex = null; + + _waveFile?.Dispose(); + _waveFile = null; + + SpeakerMax = -100; + MicMax = -100; + } + } } \ No newline at end of file diff --git a/DCS-SR-Client/AudioEffects/KY-58-RX-1600.wav b/DCS-SR-Client/AudioEffects/KY-58-RX-1600.wav index 5bc781ea2..1818e8225 100644 Binary files a/DCS-SR-Client/AudioEffects/KY-58-RX-1600.wav and b/DCS-SR-Client/AudioEffects/KY-58-RX-1600.wav differ diff --git a/DCS-SR-Client/AudioEffects/KY-58-TX-1600.wav b/DCS-SR-Client/AudioEffects/KY-58-TX-1600.wav index 78efb7863..f39b536e4 100644 Binary files a/DCS-SR-Client/AudioEffects/KY-58-TX-1600.wav and b/DCS-SR-Client/AudioEffects/KY-58-TX-1600.wav differ diff --git a/DCS-SR-Client/Properties/AssemblyInfo.cs b/DCS-SR-Client/Properties/AssemblyInfo.cs index 43d812cf5..8758ddef0 100644 --- a/DCS-SR-Client/Properties/AssemblyInfo.cs +++ b/DCS-SR-Client/Properties/AssemblyInfo.cs @@ -52,5 +52,5 @@ // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.5.0.0")] -[assembly: AssemblyFileVersion("1.5.0.0")] \ No newline at end of file +[assembly: AssemblyVersion("1.5.1.0")] +[assembly: AssemblyFileVersion("1.5.1.0")] \ No newline at end of file diff --git a/DCS-SR-Client/UI/ClientWindow/MainWindow.xaml b/DCS-SR-Client/UI/ClientWindow/MainWindow.xaml index 368d130b7..653bd0ea1 100644 --- a/DCS-SR-Client/UI/ClientWindow/MainWindow.xaml +++ b/DCS-SR-Client/UI/ClientWindow/MainWindow.xaml @@ -670,7 +670,7 @@ Grid.Column="0" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" - Content="Radio Switch works as Push To Talk" /> + Content="Radio Switch works as Push To Talk (PTT)" /> + Content="ALWAYS allow SRS Hotkeys" /> + Content="Allow INCOCKPIT DCS Controlled PTT" /> - /// Interaction logic for MainWindow.xaml - /// - public partial class MainWindow : MetroWindow - { - public delegate void ReceivedAutoConnect(string address, int port); - - public delegate void ToggleOverlayCallback(bool uiButton); - - private readonly AudioManager _audioManager; - - private readonly ConcurrentDictionary _clients = new ConcurrentDictionary(); - - private readonly string _guid; - private readonly Logger Logger = LogManager.GetCurrentClassLogger(); - private AudioPreview _audioPreview; - private ClientSync _client; - private DCSAutoConnectListener _dcsAutoConnectListener; - private int _port = 5002; - - private Overlay.RadioOverlayWindow _radioOverlayWindow; - private AwacsRadioOverlayWindow.RadioOverlayWindow _awacsRadioOverlay; - - private IPAddress _resolvedIp; - private ServerSettingsWindow _serverSettingsWindow; - - private bool _stop = true; - - //used to debounce toggle - private long _toggleShowHide; - - private readonly DispatcherTimer _updateTimer; - private MMDeviceCollection outputDeviceList; - private ServerAddress _serverAddress; - private readonly DelegateCommand _connectCommand; - - private readonly Settings.SettingsStore _settings = Settings.SettingsStore.Instance; - private readonly ClientStateSingleton _clientStateSingleton = ClientStateSingleton.Instance; - - public MainWindow() - { - GCSettings.LatencyMode = GCLatencyMode.SustainedLowLatency; - - InitializeComponent(); - - DataContext = this; - - var client = ClientStateSingleton.Instance; - - this.WindowStartupLocation = WindowStartupLocation.Manual; - this.Left = _settings.GetPositionSetting(SettingsKeys.ClientX).FloatValue; - this.Top = _settings.GetPositionSetting(SettingsKeys.ClientY).FloatValue; - - - - Title = Title + " - " + UpdaterChecker.VERSION; - - if (_settings.GetClientSetting(SettingsKeys.StartMinimised).BoolValue) - { - Hide(); - WindowState = WindowState.Minimized; - - Logger.Info("Started DCS-SimpleRadio Client " + UpdaterChecker.VERSION + " minimized"); - } - else - { - Logger.Info("Started DCS-SimpleRadio Client " + UpdaterChecker.VERSION); - } - - _guid = _settings.GetClientSetting(SettingsKeys.CliendIdShort).StringValue; - - Analytics.Log("Client", "Startup", _settings.GetClientSetting(SettingsKeys.ClientIdLong).RawValue); - - InitSettingsScreen(); - - InitInput(); - - InitAudioInput(); - - InitAudioOutput(); - InitMicAudioOutput(); - - _connectCommand = new DelegateCommand(Connect, () => ServerAddress != null); - FavouriteServersViewModel = new FavouriteServersViewModel(new CsvFavouriteServerStore()); - - InitDefaultAddress(); - - - SpeakerBoost.Value = _settings.GetClientSetting(SettingsKeys.SpeakerBoost).DoubleValue; - - Speaker_VU.Value = -100; - Mic_VU.Value = -100; - - _audioManager = new AudioManager(_clients); - _audioManager.SpeakerBoost = VolumeConversionHelper.ConvertVolumeSliderToScale((float) SpeakerBoost.Value); - - - if ((SpeakerBoostLabel != null) && (SpeakerBoost != null)) - { - SpeakerBoostLabel.Content = VolumeConversionHelper.ConvertLinearDiffToDB(_audioManager.SpeakerBoost); - } - - UpdaterChecker.CheckForUpdate(); - - - InitFlowDocument(); - - _dcsAutoConnectListener = new DCSAutoConnectListener(AutoConnect); - - _updateTimer = new DispatcherTimer {Interval = TimeSpan.FromMilliseconds(100)}; - _updateTimer.Tick += UpdateClientCount_VUMeters; - _updateTimer.Start(); - } - - private void InitFlowDocument() - { - //make hyperlinks work - var hyperlinks = WPFElementHelper.GetVisuals(AboutFlowDocument).OfType(); - foreach (var link in hyperlinks) - link.RequestNavigate += new System.Windows.Navigation.RequestNavigateEventHandler((sender, args) => - { - Process.Start(new ProcessStartInfo(args.Uri.AbsoluteUri)); - args.Handled = true; - }); - } - - private void InitDefaultAddress() - { - // legacy setting migration - if (!string.IsNullOrEmpty(_settings.GetClientSetting(SettingsKeys.LastServer).StringValue) && - FavouriteServersViewModel.Addresses.Count == 0) - { - var oldAddress = new ServerAddress(_settings.GetClientSetting(SettingsKeys.LastServer).StringValue, - _settings.GetClientSetting(SettingsKeys.LastServer).StringValue, true); - FavouriteServersViewModel.Addresses.Add(oldAddress); - } - - ServerAddress = FavouriteServersViewModel.DefaultServerAddress; - } - - private void InitInput() - { - InputManager = new InputDeviceManager(this, ToggleOverlay); - - Radio1.InputName = "Radio 1"; - Radio1.ControlInputBinding = InputBinding.Switch1; - Radio1.InputDeviceManager = InputManager; - - Radio2.InputName = "Radio 2"; - Radio2.ControlInputBinding = InputBinding.Switch2; - Radio2.InputDeviceManager = InputManager; - - Radio3.InputName = "Radio 3"; - Radio3.ControlInputBinding = InputBinding.Switch3; - Radio3.InputDeviceManager = InputManager; - - PTT.InputName = "Push To Talk - PTT"; - PTT.ControlInputBinding = InputBinding.Ptt; - PTT.InputDeviceManager = InputManager; - - Intercom.InputName = "Intercom Select"; - Intercom.ControlInputBinding = InputBinding.Intercom; - Intercom.InputDeviceManager = InputManager; - - RadioOverlay.InputName = "Overlay Toggle"; - RadioOverlay.ControlInputBinding = InputBinding.OverlayToggle; - RadioOverlay.InputDeviceManager = InputManager; - - Radio4.InputName = "Radio 4"; - Radio4.ControlInputBinding = InputBinding.Switch4; - Radio4.InputDeviceManager = InputManager; - - Radio5.InputName = "Radio 5"; - Radio5.ControlInputBinding = InputBinding.Switch5; - Radio5.InputDeviceManager = InputManager; - - Radio6.InputName = "Radio 6"; - Radio6.ControlInputBinding = InputBinding.Switch6; - Radio6.InputDeviceManager = InputManager; - - Radio7.InputName = "Radio 7"; - Radio7.ControlInputBinding = InputBinding.Switch7; - Radio7.InputDeviceManager = InputManager; - - Radio8.InputName = "Radio 8"; - Radio8.ControlInputBinding = InputBinding.Switch8; - Radio8.InputDeviceManager = InputManager; - - Radio9.InputName = "Radio 9"; - Radio9.ControlInputBinding = InputBinding.Switch9; - Radio9.InputDeviceManager = InputManager; - - Radio10.InputName = "Radio 10"; - Radio10.ControlInputBinding = InputBinding.Switch10; - Radio10.InputDeviceManager = InputManager; - - Up100.InputName = "Up 100MHz"; - Up100.ControlInputBinding = InputBinding.Up100; - Up100.InputDeviceManager = InputManager; - - Up10.InputName = "Up 10MHz"; - Up10.ControlInputBinding = InputBinding.Up10; - Up10.InputDeviceManager = InputManager; - - Up1.InputName = "Up 1MHz"; - Up1.ControlInputBinding = InputBinding.Up1; - Up1.InputDeviceManager = InputManager; - - Up01.InputName = "Up 0.1MHz"; - Up01.ControlInputBinding = InputBinding.Up01; - Up01.InputDeviceManager = InputManager; - - Up001.InputName = "Up 0.01MHz"; - Up001.ControlInputBinding = InputBinding.Up001; - Up001.InputDeviceManager = InputManager; - - Up0001.InputName = "Up 0.001MHz"; - Up0001.ControlInputBinding = InputBinding.Up0001; - Up0001.InputDeviceManager = InputManager; - - - Down100.InputName = "Down 100MHz"; - Down100.ControlInputBinding = InputBinding.Down100; - Down100.InputDeviceManager = InputManager; - - Down10.InputName = "Down 10MHz"; - Down10.ControlInputBinding = InputBinding.Down10; - Down10.InputDeviceManager = InputManager; - - Down1.InputName = "Down 1MHz"; - Down1.ControlInputBinding = InputBinding.Down1; - Down1.InputDeviceManager = InputManager; - - Down01.InputName = "Down 0.1MHz"; - Down01.ControlInputBinding = InputBinding.Down01; - Down01.InputDeviceManager = InputManager; - - Down001.InputName = "Down 0.01MHz"; - Down001.ControlInputBinding = InputBinding.Down001; - Down001.InputDeviceManager = InputManager; - - Down0001.InputName = "Down 0.001MHz"; - Down0001.ControlInputBinding = InputBinding.Down0001; - Down0001.InputDeviceManager = InputManager; - - ToggleGuard.InputName = "Toggle Guard"; - ToggleGuard.ControlInputBinding = InputBinding.ToggleGuard; - ToggleGuard.InputDeviceManager = InputManager; - - NextRadio.InputName = "Select Next Radio"; - NextRadio.ControlInputBinding = InputBinding.NextRadio; - NextRadio.InputDeviceManager = InputManager; - - PreviousRadio.InputName = "Select Previous Radio"; - PreviousRadio.ControlInputBinding = InputBinding.PreviousRadio; - PreviousRadio.InputDeviceManager = InputManager; - - ToggleEncryption.InputName = "Toggle Encryption"; - ToggleEncryption.ControlInputBinding = InputBinding.ToggleEncryption; - ToggleEncryption.InputDeviceManager = InputManager; - - EncryptionKeyIncrease.InputName = "Encryption Key Up"; - EncryptionKeyIncrease.ControlInputBinding = InputBinding.EncryptionKeyIncrease; - EncryptionKeyIncrease.InputDeviceManager = InputManager; - - EncryptionKeyDecrease.InputName = "Encryption Key Down"; - EncryptionKeyDecrease.ControlInputBinding = InputBinding.EncryptionKeyDecrease; - EncryptionKeyDecrease.InputDeviceManager = InputManager; - - RadioChannelUp.InputName = "Radio Channel Up"; - RadioChannelUp.ControlInputBinding = InputBinding.RadioChannelUp; - RadioChannelUp.InputDeviceManager = InputManager; - - RadioChannelDown.InputName = "Radio Channel Down"; - RadioChannelDown.ControlInputBinding = InputBinding.RadioChannelDown; - RadioChannelDown.InputDeviceManager = InputManager; - } - - public InputDeviceManager InputManager { get; set; } - - public FavouriteServersViewModel FavouriteServersViewModel { get; } - - public ServerAddress ServerAddress - { - get { return _serverAddress; } - set - { - _serverAddress = value; - ServerIp.Text = value.Address; - _connectCommand.RaiseCanExecuteChanged(); - } - } - - public ICommand ConnectCommand => _connectCommand; - - private void InitAudioInput() - { - Logger.Info("Audio Input - Saved ID " + - _settings.GetClientSetting(SettingsKeys.AudioInputDeviceId).StringValue); - - for (var i = 0; i < WaveIn.DeviceCount; i++) - { - //first time round - if (i == 0) - { - Mic.SelectedIndex = 0; - } - - var item = WaveIn.GetCapabilities(i); - Mic.Items.Add(new AudioDeviceListItem() - { - Text = item.ProductName, - Value = item - }); - - Logger.Info("Audio Input - " + item.ProductName + " " + item.ProductGuid.ToString() + " - Name GUID" + - item.NameGuid + " - CHN:" + item.Channels); - - if (item.ProductName.Trim().StartsWith(_settings.GetClientSetting(SettingsKeys.AudioInputDeviceId).StringValue.Trim())) - { - Mic.SelectedIndex = i; - Logger.Info("Audio Input - Found Saved "); - } - } - - // No microphone is available - users can still connect/listen, but audio input controls are disabled and sending is prevented - if (WaveIn.DeviceCount == 0 || Mic.SelectedIndex < 0) - { - Logger.Info("Audio Input - No audio input devices available, disabling mic preview"); - - _clientStateSingleton.MicrophoneAvailable = false; - - var noMicAvailableToolTip = new System.Windows.Controls.ToolTip(); - var noMicAvailableToolTipContent = new System.Windows.Controls.StackPanel(); - - noMicAvailableToolTipContent.Children.Add(new System.Windows.Controls.TextBlock - { - Text = "No microphone available", - FontWeight = FontWeights.Bold - }); - noMicAvailableToolTipContent.Children.Add(new System.Windows.Controls.TextBlock - { - Text = "No valid microphone is available - others will not be able to hear you." - }); - noMicAvailableToolTipContent.Children.Add(new System.Windows.Controls.TextBlock - { - Text = "You can still use SRS to listen to radio calls, but will not be able to transmit anything yourself." - }); - - noMicAvailableToolTip.Content = noMicAvailableToolTipContent; - - Preview.IsEnabled = false; - - Preview.ToolTip = noMicAvailableToolTip; - StartStop.ToolTip = noMicAvailableToolTip; - Mic.ToolTip = noMicAvailableToolTip; - Mic_VU.ToolTip = noMicAvailableToolTip; - } - else - { - Logger.Info("Audio Input - " + WaveIn.DeviceCount + " audio input devices available, configuring as usual"); - - _clientStateSingleton.MicrophoneAvailable = true; - - Preview.IsEnabled = true; - - Preview.ToolTip = null; - StartStop.ToolTip = null; - Mic.ToolTip = null; - Mic_VU.ToolTip = null; - } - } - - private void InitAudioOutput() - { - Logger.Info("Audio Output - Saved ID " + - _settings.GetClientSetting(SettingsKeys.AudioOutputDeviceId).RawValue); - - var enumerator = new MMDeviceEnumerator(); - outputDeviceList = enumerator.EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active); - int i = 0; - foreach (var device in outputDeviceList) - { - Speakers.Items.Add(new AudioDeviceListItem() - { - Text = device.FriendlyName, - Value = device - }); - - Logger.Info("Audio Output - " + device.DeviceFriendlyName + " " + device.ID + " CHN:" + - device.AudioClient.MixFormat.Channels + " Rate:" + - device.AudioClient.MixFormat.SampleRate.ToString()); - - //first time round the loop, select first item - if (i == 0) - { - Speakers.SelectedIndex = 0; - } - - if (device.ID == _settings.GetClientSetting(SettingsKeys.AudioOutputDeviceId).RawValue) - { - Speakers.SelectedIndex = i; //this one - } - - i++; - } - } - - private void InitMicAudioOutput() - { - Logger.Info("Mic Audio Output - Saved ID " + - _settings.GetClientSetting(SettingsKeys.MicAudioOutputDeviceId).RawValue); - - int i = 0; - - MicOutput.Items.Add(new AudioDeviceListItem() - { - Text = "NO MIC OUTPUT / PASSTHROUGH", - Value = null - }); - foreach (var device in outputDeviceList) - { - MicOutput.Items.Add(new AudioDeviceListItem() - { - Text = device.FriendlyName, - Value = device - }); - - Logger.Info("Mic Audio Output - " + device.DeviceFriendlyName + " " + device.ID + " CHN:" + - device.AudioClient.MixFormat.Channels + " Rate:" + - device.AudioClient.MixFormat.SampleRate.ToString()); - - //first time round the loop, select first item - if (i == 0) - { - MicOutput.SelectedIndex = 0; - } - - if (device.ID == _settings.GetClientSetting(SettingsKeys.MicAudioOutputDeviceId).RawValue) - { - MicOutput.SelectedIndex = i; //this one - } - - i++; - } - } - - private void UpdateClientCount_VUMeters(object sender, EventArgs e) - { - ClientCount.Content = _clients.Count; - - if (_audioPreview != null) - { - // Only update mic volume output if an audio input device is available - sometimes the value can still change, leaving the user with the impression their mic is working after all - if (_clientStateSingleton.MicrophoneAvailable) - { - Mic_VU.Value = _audioPreview.MicMax; - } - Speaker_VU.Value = _audioPreview.SpeakerMax; - } - else if (_audioManager != null) - { - // Only update mic volume output if an audio input device is available - sometimes the value can still change, leaving the user with the impression their mic is working after all - if (_clientStateSingleton.MicrophoneAvailable) - { - Mic_VU.Value = _audioManager.MicMax; - } - Speaker_VU.Value = _audioManager.SpeakerMax; - } - else - { - Mic_VU.Value = -100; - Speaker_VU.Value = -100; - } - } - - - private void InitSettingsScreen() - { - RadioEncryptionEffectsToggle.IsChecked = - _settings.GetClientSetting(SettingsKeys.RadioEncryptionEffects).BoolValue; - RadioSwitchIsPTT.IsChecked = - _settings.GetClientSetting(SettingsKeys.RadioSwitchIsPTT).BoolValue; - AutoConnectPromptToggle.IsChecked = _settings.GetClientSetting(SettingsKeys.AutoConnectPrompt).BoolValue; - RadioOverlayTaskbarItem.IsChecked = - _settings.GetClientSetting(SettingsKeys.RadioOverlayTaskbarHide).BoolValue; - RefocusDCS.IsChecked = _settings.GetClientSetting(SettingsKeys.RefocusDCS).BoolValue; - ExpandInputDevices.IsChecked = _settings.GetClientSetting(SettingsKeys.ExpandControls).BoolValue; - RadioTxStartToggle.IsChecked = _settings.GetClientSetting(SettingsKeys.RadioTxEffects_Start).BoolValue; - RadioTxEndToggle.IsChecked = _settings.GetClientSetting(SettingsKeys.RadioTxEffects_End).BoolValue; - - RadioRxStartToggle.IsChecked = _settings.GetClientSetting(SettingsKeys.RadioRxEffects_Start).BoolValue; - RadioRxEndToggle.IsChecked = _settings.GetClientSetting(SettingsKeys.RadioRxEffects_Start).BoolValue; - - MinimiseToTray.IsChecked = _settings.GetClientSetting(SettingsKeys.MinimiseToTray).BoolValue; - StartMinimised.IsChecked = _settings.GetClientSetting(SettingsKeys.StartMinimised).BoolValue; - - RadioSoundEffects.IsChecked = _settings.GetClientSetting(SettingsKeys.RadioEffects).BoolValue; - RadioSoundEffectsClipping.IsChecked = _settings.GetClientSetting(SettingsKeys.RadioEffectsClipping).BoolValue; - AutoSelectChannel.IsChecked = _settings.GetClientSetting(SettingsKeys.AutoSelectPresetChannel).BoolValue; - - AlwaysAllowHotas.IsChecked = _settings.GetClientSetting(SettingsKeys.AlwaysAllowHotasControls).BoolValue; - AllowDCSPTT.IsChecked = _settings.GetClientSetting(SettingsKeys.AllowDCSPTT).BoolValue; - } - - private void Connect() - { - if (!_stop) - { - Stop(); - } - else - { - SaveSelectedInputAndOutput(); - - try - { - //process hostname - var ipAddr = Dns.GetHostAddresses(GetAddressFromTextBox()); - - if (ipAddr.Length > 0) - { - _resolvedIp = ipAddr[0]; - _port = GetPortFromTextBox(); - - _client = new ClientSync(_clients, _guid); - _client.TryConnect(new IPEndPoint(_resolvedIp, _port), ConnectCallback); - - StartStop.Content = "Connecting..."; - StartStop.IsEnabled = false; - Mic.IsEnabled = false; - Speakers.IsEnabled = false; - MicOutput.IsEnabled = false; - } - else - { - //invalid ID - MessageBox.Show("Invalid IP or Host Name!", "Host Name Error", MessageBoxButton.OK, - MessageBoxImage.Error); - } - } - catch (Exception ex) when (ex is SocketException || ex is ArgumentException) - { - MessageBox.Show("Invalid IP or Host Name!", "Host Name Error", MessageBoxButton.OK, - MessageBoxImage.Error); - } - } - } - - private string GetAddressFromTextBox() - { - var addr = ServerIp.Text.Trim(); - - if (addr.Contains(":")) - { - return addr.Split(':')[0]; - } - - return addr; - } - - private int GetPortFromTextBox() - { - var addr = ServerIp.Text.Trim(); - - if (addr.Contains(":")) - { - int port; - if (int.TryParse(addr.Split(':')[1], out port)) - { - return port; - } - throw new ArgumentException("specified port is not valid"); - } - - return 5002; - } - - private void Stop() - { - StartStop.Content = "Connect"; - StartStop.IsEnabled = true; - Mic.IsEnabled = true; - Speakers.IsEnabled = true; - MicOutput.IsEnabled = true; - try - { - _audioManager.StopEncoding(); - } - catch (Exception ex) - { - } - - _stop = true; - - if (_client != null) - { - _client.Disconnect(); - _client = null; - } - } - - private void SaveSelectedInputAndOutput() - { - - var output = outputDeviceList[Speakers.SelectedIndex]; - - - //save app settings - // Only save selected microphone if one is actually available, resulting in a crash otherwise - if (_clientStateSingleton.MicrophoneAvailable) - { - _settings.SetClientSetting(SettingsKeys.AudioInputDeviceId, ((WaveInCapabilities)((AudioDeviceListItem)Mic.SelectedItem).Value).ProductName); - } - - _settings.SetClientSetting(SettingsKeys.AudioOutputDeviceId, output.ID); - - //check if we have optional output - if (MicOutput.SelectedIndex - 1 >= 0) - { - var micOutput = outputDeviceList[MicOutput.SelectedIndex - 1]; - //save settings - _settings.SetClientSetting(SettingsKeys.MicAudioOutputDeviceId, micOutput.ID); - } - else - { - //save settings as none - _settings.SetClientSetting(SettingsKeys.MicAudioOutputDeviceId, ""); - } - } - - private void ConnectCallback(bool result) - { - if (result) - { - if (_stop) - { - try - { - - var inputId = Mic.SelectedIndex; - var output = outputDeviceList[Speakers.SelectedIndex]; - - //check if we have optional output - MMDevice micOutput = null; - if (MicOutput.SelectedIndex - 1 >= 0) - { - micOutput = outputDeviceList[MicOutput.SelectedIndex - 1]; - } - - StartStop.Content = "Disconnect"; - StartStop.IsEnabled = true; - - _settings.SetClientSetting(SettingsKeys.LastServer, ServerIp.Text); - - _audioManager.StartEncoding(inputId, output, _guid, InputManager, - _resolvedIp, _port, micOutput); - _stop = false; - } - catch (Exception ex) - { - Logger.Error(ex, - "Unable to get audio device - likely output device error - Pick another. Error:" + - ex.Message); - Stop(); - - var messageBoxResult = CustomMessageBox.ShowYesNo( - "Problem initialising Audio Output!\n\nTry a different Output device and please post your client log to the support Discord server.\n\nJoin support Discord server now?", - "Audio Output Error", - "OPEN PRIVACY SETTINGS", - "JOIN DISCORD SERVER", - MessageBoxImage.Error); - - if (messageBoxResult == MessageBoxResult.Yes) +using InputBinding = Ciribob.DCS.SimpleRadio.Standalone.Client.Settings.InputBinding; + +namespace Ciribob.DCS.SimpleRadio.Standalone.Client.UI +{ + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class MainWindow : MetroWindow + { + public delegate void ReceivedAutoConnect(string address, int port); + + public delegate void ToggleOverlayCallback(bool uiButton); + + private readonly AudioManager _audioManager; + + private readonly ConcurrentDictionary _clients = new ConcurrentDictionary(); + + private readonly string _guid; + private readonly Logger Logger = LogManager.GetCurrentClassLogger(); + private AudioPreview _audioPreview; + private ClientSync _client; + private DCSAutoConnectListener _dcsAutoConnectListener; + private int _port = 5002; + + private Overlay.RadioOverlayWindow _radioOverlayWindow; + private AwacsRadioOverlayWindow.RadioOverlayWindow _awacsRadioOverlay; + + private IPAddress _resolvedIp; + private ServerSettingsWindow _serverSettingsWindow; + + private bool _stop = true; + + //used to debounce toggle + private long _toggleShowHide; + + private readonly DispatcherTimer _updateTimer; + private MMDeviceCollection outputDeviceList; + private ServerAddress _serverAddress; + private readonly DelegateCommand _connectCommand; + + private readonly SettingsStore _settings = SettingsStore.Instance; + private readonly ClientStateSingleton _clientStateSingleton = ClientStateSingleton.Instance; + + public MainWindow() + { + GCSettings.LatencyMode = GCLatencyMode.SustainedLowLatency; + + InitializeComponent(); + + DataContext = this; + + var client = ClientStateSingleton.Instance; + + WindowStartupLocation = WindowStartupLocation.Manual; + Left = _settings.GetPositionSetting(SettingsKeys.ClientX).FloatValue; + Top = _settings.GetPositionSetting(SettingsKeys.ClientY).FloatValue; + + + + Title = Title + " - " + UpdaterChecker.VERSION; + + if (_settings.GetClientSetting(SettingsKeys.StartMinimised).BoolValue) + { + Hide(); + WindowState = WindowState.Minimized; + + Logger.Info("Started DCS-SimpleRadio Client " + UpdaterChecker.VERSION + " minimized"); + } + else + { + Logger.Info("Started DCS-SimpleRadio Client " + UpdaterChecker.VERSION); + } + + _guid = _settings.GetClientSetting(SettingsKeys.CliendIdShort).StringValue; + + Analytics.Log("Client", "Startup", _settings.GetClientSetting(SettingsKeys.ClientIdLong).RawValue); + + InitSettingsScreen(); + + InitInput(); + + InitAudioInput(); + + InitAudioOutput(); + InitMicAudioOutput(); + + _connectCommand = new DelegateCommand(Connect, () => ServerAddress != null); + FavouriteServersViewModel = new FavouriteServersViewModel(new CsvFavouriteServerStore()); + + InitDefaultAddress(); + + + SpeakerBoost.Value = _settings.GetClientSetting(SettingsKeys.SpeakerBoost).DoubleValue; + + Speaker_VU.Value = -100; + Mic_VU.Value = -100; + + _audioManager = new AudioManager(_clients); + _audioManager.SpeakerBoost = VolumeConversionHelper.ConvertVolumeSliderToScale((float) SpeakerBoost.Value); + + + if ((SpeakerBoostLabel != null) && (SpeakerBoost != null)) + { + SpeakerBoostLabel.Content = VolumeConversionHelper.ConvertLinearDiffToDB(_audioManager.SpeakerBoost); + } + + UpdaterChecker.CheckForUpdate(); + + + InitFlowDocument(); + + _dcsAutoConnectListener = new DCSAutoConnectListener(AutoConnect); + + _updateTimer = new DispatcherTimer {Interval = TimeSpan.FromMilliseconds(100)}; + _updateTimer.Tick += UpdateClientCount_VUMeters; + _updateTimer.Start(); + } + + private void InitFlowDocument() + { + //make hyperlinks work + var hyperlinks = WPFElementHelper.GetVisuals(AboutFlowDocument).OfType(); + foreach (var link in hyperlinks) + link.RequestNavigate += new System.Windows.Navigation.RequestNavigateEventHandler((sender, args) => + { + Process.Start(new ProcessStartInfo(args.Uri.AbsoluteUri)); + args.Handled = true; + }); + } + + private void InitDefaultAddress() + { + // legacy setting migration + if (!string.IsNullOrEmpty(_settings.GetClientSetting(SettingsKeys.LastServer).StringValue) && + FavouriteServersViewModel.Addresses.Count == 0) + { + var oldAddress = new ServerAddress(_settings.GetClientSetting(SettingsKeys.LastServer).StringValue, + _settings.GetClientSetting(SettingsKeys.LastServer).StringValue, true); + FavouriteServersViewModel.Addresses.Add(oldAddress); + } + + ServerAddress = FavouriteServersViewModel.DefaultServerAddress; + } + + private void InitInput() + { + InputManager = new InputDeviceManager(this, ToggleOverlay); + + Radio1.InputName = "Radio 1"; + Radio1.ControlInputBinding = InputBinding.Switch1; + Radio1.InputDeviceManager = InputManager; + + Radio2.InputName = "Radio 2"; + Radio2.ControlInputBinding = InputBinding.Switch2; + Radio2.InputDeviceManager = InputManager; + + Radio3.InputName = "Radio 3"; + Radio3.ControlInputBinding = InputBinding.Switch3; + Radio3.InputDeviceManager = InputManager; + + PTT.InputName = "Push To Talk - PTT"; + PTT.ControlInputBinding = InputBinding.Ptt; + PTT.InputDeviceManager = InputManager; + + Intercom.InputName = "Intercom Select"; + Intercom.ControlInputBinding = InputBinding.Intercom; + Intercom.InputDeviceManager = InputManager; + + RadioOverlay.InputName = "Overlay Toggle"; + RadioOverlay.ControlInputBinding = InputBinding.OverlayToggle; + RadioOverlay.InputDeviceManager = InputManager; + + Radio4.InputName = "Radio 4"; + Radio4.ControlInputBinding = InputBinding.Switch4; + Radio4.InputDeviceManager = InputManager; + + Radio5.InputName = "Radio 5"; + Radio5.ControlInputBinding = InputBinding.Switch5; + Radio5.InputDeviceManager = InputManager; + + Radio6.InputName = "Radio 6"; + Radio6.ControlInputBinding = InputBinding.Switch6; + Radio6.InputDeviceManager = InputManager; + + Radio7.InputName = "Radio 7"; + Radio7.ControlInputBinding = InputBinding.Switch7; + Radio7.InputDeviceManager = InputManager; + + Radio8.InputName = "Radio 8"; + Radio8.ControlInputBinding = InputBinding.Switch8; + Radio8.InputDeviceManager = InputManager; + + Radio9.InputName = "Radio 9"; + Radio9.ControlInputBinding = InputBinding.Switch9; + Radio9.InputDeviceManager = InputManager; + + Radio10.InputName = "Radio 10"; + Radio10.ControlInputBinding = InputBinding.Switch10; + Radio10.InputDeviceManager = InputManager; + + Up100.InputName = "Up 100MHz"; + Up100.ControlInputBinding = InputBinding.Up100; + Up100.InputDeviceManager = InputManager; + + Up10.InputName = "Up 10MHz"; + Up10.ControlInputBinding = InputBinding.Up10; + Up10.InputDeviceManager = InputManager; + + Up1.InputName = "Up 1MHz"; + Up1.ControlInputBinding = InputBinding.Up1; + Up1.InputDeviceManager = InputManager; + + Up01.InputName = "Up 0.1MHz"; + Up01.ControlInputBinding = InputBinding.Up01; + Up01.InputDeviceManager = InputManager; + + Up001.InputName = "Up 0.01MHz"; + Up001.ControlInputBinding = InputBinding.Up001; + Up001.InputDeviceManager = InputManager; + + Up0001.InputName = "Up 0.001MHz"; + Up0001.ControlInputBinding = InputBinding.Up0001; + Up0001.InputDeviceManager = InputManager; + + + Down100.InputName = "Down 100MHz"; + Down100.ControlInputBinding = InputBinding.Down100; + Down100.InputDeviceManager = InputManager; + + Down10.InputName = "Down 10MHz"; + Down10.ControlInputBinding = InputBinding.Down10; + Down10.InputDeviceManager = InputManager; + + Down1.InputName = "Down 1MHz"; + Down1.ControlInputBinding = InputBinding.Down1; + Down1.InputDeviceManager = InputManager; + + Down01.InputName = "Down 0.1MHz"; + Down01.ControlInputBinding = InputBinding.Down01; + Down01.InputDeviceManager = InputManager; + + Down001.InputName = "Down 0.01MHz"; + Down001.ControlInputBinding = InputBinding.Down001; + Down001.InputDeviceManager = InputManager; + + Down0001.InputName = "Down 0.001MHz"; + Down0001.ControlInputBinding = InputBinding.Down0001; + Down0001.InputDeviceManager = InputManager; + + ToggleGuard.InputName = "Toggle Guard"; + ToggleGuard.ControlInputBinding = InputBinding.ToggleGuard; + ToggleGuard.InputDeviceManager = InputManager; + + NextRadio.InputName = "Select Next Radio"; + NextRadio.ControlInputBinding = InputBinding.NextRadio; + NextRadio.InputDeviceManager = InputManager; + + PreviousRadio.InputName = "Select Previous Radio"; + PreviousRadio.ControlInputBinding = InputBinding.PreviousRadio; + PreviousRadio.InputDeviceManager = InputManager; + + ToggleEncryption.InputName = "Toggle Encryption"; + ToggleEncryption.ControlInputBinding = InputBinding.ToggleEncryption; + ToggleEncryption.InputDeviceManager = InputManager; + + EncryptionKeyIncrease.InputName = "Encryption Key Up"; + EncryptionKeyIncrease.ControlInputBinding = InputBinding.EncryptionKeyIncrease; + EncryptionKeyIncrease.InputDeviceManager = InputManager; + + EncryptionKeyDecrease.InputName = "Encryption Key Down"; + EncryptionKeyDecrease.ControlInputBinding = InputBinding.EncryptionKeyDecrease; + EncryptionKeyDecrease.InputDeviceManager = InputManager; + + RadioChannelUp.InputName = "Radio Channel Up"; + RadioChannelUp.ControlInputBinding = InputBinding.RadioChannelUp; + RadioChannelUp.InputDeviceManager = InputManager; + + RadioChannelDown.InputName = "Radio Channel Down"; + RadioChannelDown.ControlInputBinding = InputBinding.RadioChannelDown; + RadioChannelDown.InputDeviceManager = InputManager; + } + + public InputDeviceManager InputManager { get; set; } + + public FavouriteServersViewModel FavouriteServersViewModel { get; } + + public ServerAddress ServerAddress + { + get { return _serverAddress; } + set + { + _serverAddress = value; + ServerIp.Text = value.Address; + _connectCommand.RaiseCanExecuteChanged(); + } + } + + public ICommand ConnectCommand => _connectCommand; + + private void InitAudioInput() + { + Logger.Info("Audio Input - Saved ID " + + _settings.GetClientSetting(SettingsKeys.AudioInputDeviceId).StringValue); + + for (var i = 0; i < WaveIn.DeviceCount; i++) + { + //first time round + if (i == 0) + { + Mic.SelectedIndex = 0; + } + + var item = WaveIn.GetCapabilities(i); + Mic.Items.Add(new AudioDeviceListItem() + { + Text = item.ProductName, + Value = item + }); + + Logger.Info("Audio Input - " + item.ProductName + " " + item.ProductGuid.ToString() + " - Name GUID" + + item.NameGuid + " - CHN:" + item.Channels); + + if (item.ProductName.Trim().StartsWith(_settings.GetClientSetting(SettingsKeys.AudioInputDeviceId).StringValue.Trim())) + { + Mic.SelectedIndex = i; + Logger.Info("Audio Input - Found Saved "); + } + } + + // No microphone is available - users can still connect/listen, but audio input controls are disabled and sending is prevented + if (WaveIn.DeviceCount == 0 || Mic.SelectedIndex < 0) + { + Logger.Info("Audio Input - No audio input devices available, disabling mic preview"); + + _clientStateSingleton.MicrophoneAvailable = false; + + var noMicAvailableToolTip = new System.Windows.Controls.ToolTip(); + var noMicAvailableToolTipContent = new System.Windows.Controls.StackPanel(); + + noMicAvailableToolTipContent.Children.Add(new System.Windows.Controls.TextBlock + { + Text = "No microphone available", + FontWeight = FontWeights.Bold + }); + noMicAvailableToolTipContent.Children.Add(new System.Windows.Controls.TextBlock + { + Text = "No valid microphone is available - others will not be able to hear you." + }); + noMicAvailableToolTipContent.Children.Add(new System.Windows.Controls.TextBlock + { + Text = "You can still use SRS to listen to radio calls, but will not be able to transmit anything yourself." + }); + + noMicAvailableToolTip.Content = noMicAvailableToolTipContent; + + Preview.IsEnabled = false; + + Preview.ToolTip = noMicAvailableToolTip; + StartStop.ToolTip = noMicAvailableToolTip; + Mic.ToolTip = noMicAvailableToolTip; + Mic_VU.ToolTip = noMicAvailableToolTip; + } + else + { + Logger.Info("Audio Input - " + WaveIn.DeviceCount + " audio input devices available, configuring as usual"); + + _clientStateSingleton.MicrophoneAvailable = true; + + Preview.IsEnabled = true; + + Preview.ToolTip = null; + StartStop.ToolTip = null; + Mic.ToolTip = null; + Mic_VU.ToolTip = null; + } + } + + private void InitAudioOutput() + { + Logger.Info("Audio Output - Saved ID " + + _settings.GetClientSetting(SettingsKeys.AudioOutputDeviceId).RawValue); + + var enumerator = new MMDeviceEnumerator(); + outputDeviceList = enumerator.EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active); + var i = 0; + foreach (var device in outputDeviceList) + { + Speakers.Items.Add(new AudioDeviceListItem() + { + Text = device.FriendlyName, + Value = device + }); + + Logger.Info("Audio Output - " + device.DeviceFriendlyName + " " + device.ID + " CHN:" + + device.AudioClient.MixFormat.Channels + " Rate:" + + device.AudioClient.MixFormat.SampleRate.ToString()); + + //first time round the loop, select first item + if (i == 0) + { + Speakers.SelectedIndex = 0; + } + + if (device.ID == _settings.GetClientSetting(SettingsKeys.AudioOutputDeviceId).RawValue) + { + Speakers.SelectedIndex = i; //this one + } + + i++; + } + } + + private void InitMicAudioOutput() + { + Logger.Info("Mic Audio Output - Saved ID " + + _settings.GetClientSetting(SettingsKeys.MicAudioOutputDeviceId).RawValue); + + var i = 0; + + MicOutput.Items.Add(new AudioDeviceListItem() + { + Text = "NO MIC OUTPUT / PASSTHROUGH", + Value = null + }); + foreach (var device in outputDeviceList) + { + MicOutput.Items.Add(new AudioDeviceListItem() + { + Text = device.FriendlyName, + Value = device + }); + + Logger.Info("Mic Audio Output - " + device.DeviceFriendlyName + " " + device.ID + " CHN:" + + device.AudioClient.MixFormat.Channels + " Rate:" + + device.AudioClient.MixFormat.SampleRate.ToString()); + + //first time round the loop, select first item + if (i == 0) + { + MicOutput.SelectedIndex = 0; + } + + if (device.ID == _settings.GetClientSetting(SettingsKeys.MicAudioOutputDeviceId).RawValue) + { + MicOutput.SelectedIndex = i; //this one + } + + i++; + } + } + + private void UpdateClientCount_VUMeters(object sender, EventArgs e) + { + ClientCount.Content = _clients.Count; + + if (_audioPreview != null) + { + // Only update mic volume output if an audio input device is available - sometimes the value can still change, leaving the user with the impression their mic is working after all + if (_clientStateSingleton.MicrophoneAvailable) + { + Mic_VU.Value = _audioPreview.MicMax; + } + Speaker_VU.Value = _audioPreview.SpeakerMax; + } + else if (_audioManager != null) + { + // Only update mic volume output if an audio input device is available - sometimes the value can still change, leaving the user with the impression their mic is working after all + if (_clientStateSingleton.MicrophoneAvailable) + { + Mic_VU.Value = _audioManager.MicMax; + } + Speaker_VU.Value = _audioManager.SpeakerMax; + } + else + { + Mic_VU.Value = -100; + Speaker_VU.Value = -100; + } + } + + + private void InitSettingsScreen() + { + RadioEncryptionEffectsToggle.IsChecked = + _settings.GetClientSetting(SettingsKeys.RadioEncryptionEffects).BoolValue; + RadioSwitchIsPTT.IsChecked = + _settings.GetClientSetting(SettingsKeys.RadioSwitchIsPTT).BoolValue; + AutoConnectPromptToggle.IsChecked = _settings.GetClientSetting(SettingsKeys.AutoConnectPrompt).BoolValue; + RadioOverlayTaskbarItem.IsChecked = + _settings.GetClientSetting(SettingsKeys.RadioOverlayTaskbarHide).BoolValue; + RefocusDCS.IsChecked = _settings.GetClientSetting(SettingsKeys.RefocusDCS).BoolValue; + ExpandInputDevices.IsChecked = _settings.GetClientSetting(SettingsKeys.ExpandControls).BoolValue; + RadioTxStartToggle.IsChecked = _settings.GetClientSetting(SettingsKeys.RadioTxEffects_Start).BoolValue; + RadioTxEndToggle.IsChecked = _settings.GetClientSetting(SettingsKeys.RadioTxEffects_End).BoolValue; + + RadioRxStartToggle.IsChecked = _settings.GetClientSetting(SettingsKeys.RadioRxEffects_Start).BoolValue; + RadioRxEndToggle.IsChecked = _settings.GetClientSetting(SettingsKeys.RadioRxEffects_Start).BoolValue; + + MinimiseToTray.IsChecked = _settings.GetClientSetting(SettingsKeys.MinimiseToTray).BoolValue; + StartMinimised.IsChecked = _settings.GetClientSetting(SettingsKeys.StartMinimised).BoolValue; + + RadioSoundEffects.IsChecked = _settings.GetClientSetting(SettingsKeys.RadioEffects).BoolValue; + RadioSoundEffectsClipping.IsChecked = _settings.GetClientSetting(SettingsKeys.RadioEffectsClipping).BoolValue; + AutoSelectChannel.IsChecked = _settings.GetClientSetting(SettingsKeys.AutoSelectPresetChannel).BoolValue; + + AlwaysAllowHotas.IsChecked = _settings.GetClientSetting(SettingsKeys.AlwaysAllowHotasControls).BoolValue; + AllowDCSPTT.IsChecked = _settings.GetClientSetting(SettingsKeys.AllowDCSPTT).BoolValue; + } + + private void Connect() + { + if (!_stop) + { + Stop(); + } + else + { + SaveSelectedInputAndOutput(); + + try + { + //process hostname + var ipAddr = Dns.GetHostAddresses(GetAddressFromTextBox()); + + if (ipAddr.Length > 0) + { + _resolvedIp = ipAddr[0]; + _port = GetPortFromTextBox(); + + _client = new ClientSync(_clients, _guid); + _client.TryConnect(new IPEndPoint(_resolvedIp, _port), ConnectCallback); + + StartStop.Content = "Connecting..."; + StartStop.IsEnabled = false; + Mic.IsEnabled = false; + Speakers.IsEnabled = false; + MicOutput.IsEnabled = false; + } + else + { + //invalid ID + MessageBox.Show("Invalid IP or Host Name!", "Host Name Error", MessageBoxButton.OK, + MessageBoxImage.Error); + } + } + catch (Exception ex) when (ex is SocketException || ex is ArgumentException) + { + MessageBox.Show("Invalid IP or Host Name!", "Host Name Error", MessageBoxButton.OK, + MessageBoxImage.Error); + } + } + } + + private string GetAddressFromTextBox() + { + var addr = ServerIp.Text.Trim(); + + if (addr.Contains(":")) + { + return addr.Split(':')[0]; + } + + return addr; + } + + private int GetPortFromTextBox() + { + var addr = ServerIp.Text.Trim(); + + if (addr.Contains(":")) + { + int port; + if (int.TryParse(addr.Split(':')[1], out port)) + { + return port; + } + throw new ArgumentException("specified port is not valid"); + } + + return 5002; + } + + private void Stop() + { + StartStop.Content = "Connect"; + StartStop.IsEnabled = true; + Mic.IsEnabled = true; + Speakers.IsEnabled = true; + MicOutput.IsEnabled = true; + try + { + _audioManager.StopEncoding(); + } + catch (Exception ex) + { + } + + _stop = true; + + if (_client != null) + { + _client.Disconnect(); + _client = null; + } + } + + private void SaveSelectedInputAndOutput() + { + + var output = outputDeviceList[Speakers.SelectedIndex]; + + + //save app settings + // Only save selected microphone if one is actually available, resulting in a crash otherwise + if (_clientStateSingleton.MicrophoneAvailable) + { + _settings.SetClientSetting(SettingsKeys.AudioInputDeviceId, ((WaveInCapabilities)((AudioDeviceListItem)Mic.SelectedItem).Value).ProductName); + } + + _settings.SetClientSetting(SettingsKeys.AudioOutputDeviceId, output.ID); + + //check if we have optional output + if (MicOutput.SelectedIndex - 1 >= 0) + { + var micOutput = outputDeviceList[MicOutput.SelectedIndex - 1]; + //save settings + _settings.SetClientSetting(SettingsKeys.MicAudioOutputDeviceId, micOutput.ID); + } + else + { + //save settings as none + _settings.SetClientSetting(SettingsKeys.MicAudioOutputDeviceId, ""); + } + } + + private void ConnectCallback(bool result) + { + if (result) + { + if (_stop) + try + { + + var inputId = Mic.SelectedIndex; + var output = outputDeviceList[Speakers.SelectedIndex]; + + //check if we have optional output + MMDevice micOutput = null; + if (MicOutput.SelectedIndex - 1 >= 0) { - Process.Start("https://discord.gg/baw7g3t"); - } - } - } - } - else - { - Stop(); - } - } - - - protected override void OnClosing(CancelEventArgs e) - { - _settings.SetPositionSetting(SettingsKeys.ClientX, this.Left); - _settings.SetPositionSetting(SettingsKeys.ClientY, this.Top); - - //save window position - base.OnClosing(e); - - //stop timer - _updateTimer.Stop(); - - Stop(); - - if (_audioPreview != null) - { - _audioPreview.StopEncoding(); - _audioPreview = null; - } - - _radioOverlayWindow?.Close(); - _radioOverlayWindow = null; - - _dcsAutoConnectListener.Stop(); - _dcsAutoConnectListener = null; - } - - protected override void OnStateChanged(EventArgs e) - { - if (WindowState == WindowState.Minimized && _settings.GetClientSetting(SettingsKeys.MinimiseToTray).BoolValue) - { - Hide(); - } - - base.OnStateChanged(e); - } - - private void PreviewAudio(object sender, RoutedEventArgs e) - { - if (_audioPreview == null) - { - if (!_clientStateSingleton.MicrophoneAvailable) - { - Logger.Info("Unable to preview audio, no valid audio input device available or selected"); - return; - } - - //get device - try - { - var inputId = Mic.SelectedIndex; - var output = outputDeviceList[Speakers.SelectedIndex]; - - SaveSelectedInputAndOutput(); - - - _audioPreview = new AudioPreview(); - _audioPreview.SpeakerBoost = VolumeConversionHelper.ConvertVolumeSliderToScale((float)SpeakerBoost.Value); - _audioPreview.StartPreview(inputId, output); - - Preview.Content = "Stop Preview"; - } - catch (Exception ex) - { - Logger.Error(ex, - "Unable to preview audio - likely output device error - Pick another. Error:" + ex.Message); - } - } - else - { - Preview.Content = "Audio Preview"; - _audioPreview.StopEncoding(); - _audioPreview = null; - } - } - - - private void SpeakerBoost_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) - { - var convertedValue = VolumeConversionHelper.ConvertVolumeSliderToScale((float) SpeakerBoost.Value); - - if (_audioPreview != null) - { - _audioPreview.SpeakerBoost = convertedValue; - } - if (_audioManager != null) - { - _audioManager.SpeakerBoost = convertedValue; - } - - _settings.SetClientSetting(SettingsKeys.SpeakerBoost, - SpeakerBoost.Value.ToString(CultureInfo.InvariantCulture)); - - - if ((SpeakerBoostLabel != null) && (SpeakerBoost != null)) - { - SpeakerBoostLabel.Content = VolumeConversionHelper.ConvertLinearDiffToDB(convertedValue); - } - } - - private void RadioEncryptionEffects_Click(object sender, RoutedEventArgs e) - { - _settings.SetClientSetting(SettingsKeys.RadioEncryptionEffects, - (string) RadioEncryptionEffectsToggle.Content); - } - - private void RadioSwitchPTT_Click(object sender, RoutedEventArgs e) - { - _settings.SetClientSetting(SettingsKeys.RadioSwitchIsPTT, (string) RadioSwitchIsPTT.Content); - } - - private void ShowOverlay_OnClick(object sender, RoutedEventArgs e) - { - ToggleOverlay(true); - } - - private void ToggleOverlay(bool uiButton) - { - //debounce show hide (1 tick = 100ns, 6000000 ticks = 600ms debounce) - if ((DateTime.Now.Ticks - _toggleShowHide > 6000000) || uiButton) - { - _toggleShowHide = DateTime.Now.Ticks; - if ((_radioOverlayWindow == null) || !_radioOverlayWindow.IsVisible || - (_radioOverlayWindow.WindowState == WindowState.Minimized)) - { - //hide awacs panel - _awacsRadioOverlay?.Close(); - _awacsRadioOverlay = null; - - _radioOverlayWindow?.Close(); - - _radioOverlayWindow = new Overlay.RadioOverlayWindow(); - - - _radioOverlayWindow.ShowInTaskbar = - !_settings.GetClientSetting(SettingsKeys.RadioOverlayTaskbarHide).BoolValue; - _radioOverlayWindow.Show(); - } - else - { - _radioOverlayWindow?.Close(); - _radioOverlayWindow = null; - } - } - } - - private void ShowAwacsOverlay_OnClick(object sender, RoutedEventArgs e) - { - if ((_awacsRadioOverlay == null) || !_awacsRadioOverlay.IsVisible || - (_awacsRadioOverlay.WindowState == WindowState.Minimized)) - { - //close normal overlay - _radioOverlayWindow?.Close(); - _radioOverlayWindow = null; - - _awacsRadioOverlay?.Close(); - - _awacsRadioOverlay = new AwacsRadioOverlayWindow.RadioOverlayWindow(); - _awacsRadioOverlay.ShowInTaskbar = - !_settings.GetClientSetting(SettingsKeys.RadioOverlayTaskbarHide).BoolValue; - _awacsRadioOverlay.Show(); - } - else - { - _awacsRadioOverlay?.Close(); - _awacsRadioOverlay = null; - } - } - - private void AutoConnect(string address, int port) - { - Logger.Info("Received AutoConnect " + address); - - if (StartStop.Content.ToString().ToLower() == "connect") - { - var autoConnect = _settings.GetClientSetting(SettingsKeys.AutoConnectPrompt).BoolValue; - - var connection = $"{address}:{port}"; - if (autoConnect) - { - WindowHelper.BringProcessToFront(Process.GetCurrentProcess()); - - var result = MessageBox.Show(this, - $"Would you like to try to Auto-Connect to DCS-SRS @ {address}:{port}? ", "Auto Connect", - MessageBoxButton.YesNo, - MessageBoxImage.Question); - - if ((result == MessageBoxResult.Yes) && (StartStop.Content.ToString().ToLower() == "connect")) - { - ServerIp.Text = connection; - Connect(); - } - } - else - { - ServerIp.Text = connection; - Connect(); - } - } - } - - private void ResetRadioWindow_Click(object sender, RoutedEventArgs e) - { - //close overlay - _radioOverlayWindow?.Close(); - _radioOverlayWindow = null; - - - _settings.GetPositionSetting(SettingsKeys.RadioX).DoubleValue = 100; - _settings.GetPositionSetting(SettingsKeys.RadioY).DoubleValue = 100; - - _settings.GetPositionSetting(SettingsKeys.RadioWidth).DoubleValue = 122; - _settings.GetPositionSetting(SettingsKeys.RadioHeight).DoubleValue = 270; - - _settings.GetPositionSetting(SettingsKeys.RadioOpacity).DoubleValue = 1.0; - - _settings.Save(); - } - - private void ToggleServerSettings_OnClick(object sender, RoutedEventArgs e) - { - if ((_serverSettingsWindow == null) || !_serverSettingsWindow.IsVisible || - (_serverSettingsWindow.WindowState == WindowState.Minimized)) - { - _serverSettingsWindow?.Close(); - - _serverSettingsWindow = new ServerSettingsWindow(); - _serverSettingsWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner; - _serverSettingsWindow.Owner = this; - _serverSettingsWindow.ShowDialog(); - } - else - { - _serverSettingsWindow?.Close(); - _serverSettingsWindow = null; - } - } - - private void AutoConnectPromptToggle_Click(object sender, RoutedEventArgs e) - { - _settings.GetClientSetting(SettingsKeys.AutoConnectPrompt).BoolValue = - (bool) AutoConnectPromptToggle.IsChecked; - _settings.Save(); - } - - private void RadioOverlayTaskbarItem_Click(object sender, RoutedEventArgs e) - { - _settings.GetClientSetting(SettingsKeys.RadioOverlayTaskbarHide).BoolValue = - (bool) RadioOverlayTaskbarItem.IsChecked; - _settings.Save(); - - if (_radioOverlayWindow != null) + micOutput = outputDeviceList[MicOutput.SelectedIndex - 1]; + } + + StartStop.Content = "Disconnect"; + StartStop.IsEnabled = true; + + _settings.SetClientSetting(SettingsKeys.LastServer, ServerIp.Text); + + _audioManager.StartEncoding(inputId, output, _guid, InputManager, + _resolvedIp, _port, micOutput); + _stop = false; + } + catch (Exception ex) + { + Logger.Error(ex, + "Unable to get audio device - likely output device error - Pick another. Error:" + + ex.Message); + Stop(); + + var messageBoxResult = CustomMessageBox.ShowYesNo( + "Problem initialising Audio Output!\n\nTry a different Output device and please post your clientlog.txt to the support Discord server.\n\nJoin support Discord server now?", + "Audio Output Error", + "OPEN PRIVACY SETTINGS", + "JOIN DISCORD SERVER", + MessageBoxImage.Error); + + if (messageBoxResult == MessageBoxResult.Yes) Process.Start("https://discord.gg/baw7g3t"); + } + } + else { - _radioOverlayWindow.ShowInTaskbar = !_settings.GetClientSetting(SettingsKeys.RadioOverlayTaskbarHide).BoolValue; + Stop(); + } + } + + + protected override void OnClosing(CancelEventArgs e) + { + _settings.SetPositionSetting(SettingsKeys.ClientX, Left); + _settings.SetPositionSetting(SettingsKeys.ClientY, Top); + + //save window position + base.OnClosing(e); + + //stop timer + _updateTimer.Stop(); + + Stop(); + + if (_audioPreview != null) + { + _audioPreview.StopEncoding(); + _audioPreview = null; + } + + _radioOverlayWindow?.Close(); + _radioOverlayWindow = null; + + _dcsAutoConnectListener.Stop(); + _dcsAutoConnectListener = null; + } + + protected override void OnStateChanged(EventArgs e) + { + if (WindowState == WindowState.Minimized && _settings.GetClientSetting(SettingsKeys.MinimiseToTray).BoolValue) + { + Hide(); + } + + base.OnStateChanged(e); + } + + private void PreviewAudio(object sender, RoutedEventArgs e) + { + if (_audioPreview == null) + { + if (!_clientStateSingleton.MicrophoneAvailable) + { + Logger.Info("Unable to preview audio, no valid audio input device available or selected"); + return; + } + + //get device + try + { + var inputId = Mic.SelectedIndex; + var output = outputDeviceList[Speakers.SelectedIndex]; + + SaveSelectedInputAndOutput(); + + + _audioPreview = new AudioPreview(); + _audioPreview.SpeakerBoost = VolumeConversionHelper.ConvertVolumeSliderToScale((float)SpeakerBoost.Value); + _audioPreview.StartPreview(inputId, output); + + Preview.Content = "Stop Preview"; + } + catch (Exception ex) + { + Logger.Error(ex, + "Unable to preview audio - likely output device error - Pick another. Error:" + ex.Message); + + } + } + else + { + Preview.Content = "Audio Preview"; + _audioPreview.StopEncoding(); + _audioPreview = null; + } + } + + + private void SpeakerBoost_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) + { + var convertedValue = VolumeConversionHelper.ConvertVolumeSliderToScale((float) SpeakerBoost.Value); + + if (_audioPreview != null) + { + _audioPreview.SpeakerBoost = convertedValue; + } + if (_audioManager != null) + { + _audioManager.SpeakerBoost = convertedValue; + } + + _settings.SetClientSetting(SettingsKeys.SpeakerBoost, + SpeakerBoost.Value.ToString(CultureInfo.InvariantCulture)); + + + if ((SpeakerBoostLabel != null) && (SpeakerBoost != null)) + { + SpeakerBoostLabel.Content = VolumeConversionHelper.ConvertLinearDiffToDB(convertedValue); + } + } + + private void RadioEncryptionEffects_Click(object sender, RoutedEventArgs e) + { + _settings.SetClientSetting(SettingsKeys.RadioEncryptionEffects, + (string) RadioEncryptionEffectsToggle.Content); + } + + private void RadioSwitchPTT_Click(object sender, RoutedEventArgs e) + { + _settings.SetClientSetting(SettingsKeys.RadioSwitchIsPTT, (string) RadioSwitchIsPTT.Content); + } + + private void ShowOverlay_OnClick(object sender, RoutedEventArgs e) + { + ToggleOverlay(true); + } + + private void ToggleOverlay(bool uiButton) + { + //debounce show hide (1 tick = 100ns, 6000000 ticks = 600ms debounce) + if ((DateTime.Now.Ticks - _toggleShowHide > 6000000) || uiButton) + { + _toggleShowHide = DateTime.Now.Ticks; + if ((_radioOverlayWindow == null) || !_radioOverlayWindow.IsVisible || + (_radioOverlayWindow.WindowState == WindowState.Minimized)) + { + //hide awacs panel + _awacsRadioOverlay?.Close(); + _awacsRadioOverlay = null; + + _radioOverlayWindow?.Close(); + + _radioOverlayWindow = new Overlay.RadioOverlayWindow(); + + + _radioOverlayWindow.ShowInTaskbar = + !_settings.GetClientSetting(SettingsKeys.RadioOverlayTaskbarHide).BoolValue; + _radioOverlayWindow.Show(); + } + else + { + _radioOverlayWindow?.Close(); + _radioOverlayWindow = null; + } + } + } + + private void ShowAwacsOverlay_OnClick(object sender, RoutedEventArgs e) + { + if ((_awacsRadioOverlay == null) || !_awacsRadioOverlay.IsVisible || + (_awacsRadioOverlay.WindowState == WindowState.Minimized)) + { + //close normal overlay + _radioOverlayWindow?.Close(); + _radioOverlayWindow = null; + + _awacsRadioOverlay?.Close(); + + _awacsRadioOverlay = new AwacsRadioOverlayWindow.RadioOverlayWindow(); + _awacsRadioOverlay.ShowInTaskbar = + !_settings.GetClientSetting(SettingsKeys.RadioOverlayTaskbarHide).BoolValue; + _awacsRadioOverlay.Show(); + } + else + { + _awacsRadioOverlay?.Close(); + _awacsRadioOverlay = null; + } + } + + private void AutoConnect(string address, int port) + { + Logger.Info("Received AutoConnect " + address); + + if (StartStop.Content.ToString().ToLower() == "connect") + { + var autoConnect = _settings.GetClientSetting(SettingsKeys.AutoConnectPrompt).BoolValue; + + var connection = $"{address}:{port}"; + if (autoConnect) + { + WindowHelper.BringProcessToFront(Process.GetCurrentProcess()); + + var result = MessageBox.Show(this, + $"Would you like to try to Auto-Connect to DCS-SRS @ {address}:{port}? ", "Auto Connect", + MessageBoxButton.YesNo, + MessageBoxImage.Question); + + if ((result == MessageBoxResult.Yes) && (StartStop.Content.ToString().ToLower() == "connect")) + { + ServerIp.Text = connection; + Connect(); + } + } + else + { + ServerIp.Text = connection; + Connect(); + } } - else if (_awacsRadioOverlay != null) + } + + private void ResetRadioWindow_Click(object sender, RoutedEventArgs e) + { + //close overlay + _radioOverlayWindow?.Close(); + _radioOverlayWindow = null; + + + _settings.GetPositionSetting(SettingsKeys.RadioX).DoubleValue = 100; + _settings.GetPositionSetting(SettingsKeys.RadioY).DoubleValue = 100; + + _settings.GetPositionSetting(SettingsKeys.RadioWidth).DoubleValue = 122; + _settings.GetPositionSetting(SettingsKeys.RadioHeight).DoubleValue = 270; + + _settings.GetPositionSetting(SettingsKeys.RadioOpacity).DoubleValue = 1.0; + + _settings.Save(); + } + + private void ToggleServerSettings_OnClick(object sender, RoutedEventArgs e) + { + if ((_serverSettingsWindow == null) || !_serverSettingsWindow.IsVisible || + (_serverSettingsWindow.WindowState == WindowState.Minimized)) { - _awacsRadioOverlay.ShowInTaskbar = !_settings.GetClientSetting(SettingsKeys.RadioOverlayTaskbarHide).BoolValue; - } - } - - - private void DCSRefocus_OnClick_Click(object sender, RoutedEventArgs e) - { - _settings.GetClientSetting(SettingsKeys.RefocusDCS).BoolValue = - (bool) RefocusDCS.IsChecked; - _settings.Save(); - } - - private void ExpandInputDevices_OnClick_Click(object sender, RoutedEventArgs e) - { - MessageBox.Show( - "You must restart SRS for this setting to take effect.\n\nTurning this on will allow almost any DirectX device to be used as input expect a Mouse but may cause issues with other devices being detected", - "Restart SimpleRadio Standalone", MessageBoxButton.OK, - MessageBoxImage.Warning); - - _settings.GetClientSetting(SettingsKeys.ExpandControls).BoolValue = - (bool) ExpandInputDevices.IsChecked; - _settings.Save(); - } - - private void LaunchAddressTab(object sender, RoutedEventArgs e) - { - TabControl.SelectedItem = FavouritesSeversTab; - } - - private void RadioSoundEffects_OnClick(object sender, RoutedEventArgs e) - { - _settings.GetClientSetting(SettingsKeys.RadioEffects).BoolValue = - (bool) RadioSoundEffects.IsChecked; - _settings.Save(); - } - - private void RadioTxStart_Click(object sender, RoutedEventArgs e) - { - _settings.GetClientSetting(SettingsKeys.RadioTxEffects_Start).BoolValue = - (bool) RadioTxStartToggle.IsChecked; - _settings.Save(); - } - - private void RadioTxEnd_Click(object sender, RoutedEventArgs e) - { - _settings.GetClientSetting(SettingsKeys.RadioTxEffects_End).BoolValue = - (bool) RadioTxEndToggle.IsChecked; - _settings.Save(); - } - - private void RadioRxStart_Click(object sender, RoutedEventArgs e) - { - _settings.GetClientSetting(SettingsKeys.RadioRxEffects_Start).BoolValue = - (bool) RadioRxStartToggle.IsChecked; - _settings.Save(); - } - - private void RadioRxEnd_Click(object sender, RoutedEventArgs e) - { - _settings.GetClientSetting(SettingsKeys.RadioRxEffects_End).BoolValue = - (bool) RadioRxEndToggle.IsChecked; - _settings.Save(); - } - - private void AudioSelectChannel_OnClick(object sender, RoutedEventArgs e) - { - _settings.GetClientSetting(SettingsKeys.AutoSelectPresetChannel).BoolValue = - (bool) AutoSelectChannel.IsChecked; - _settings.Save(); - } - - private void RadioSoundEffectsClipping_OnClick(object sender, RoutedEventArgs e) - { - _settings.GetClientSetting(SettingsKeys.RadioEffectsClipping).BoolValue = - (bool)RadioSoundEffectsClipping.IsChecked; - _settings.Save(); - - } - - private void MinimiseToTray_OnClick(object sender, RoutedEventArgs e) - { - _settings.GetClientSetting(SettingsKeys.MinimiseToTray).BoolValue = - (bool)MinimiseToTray.IsChecked; - _settings.Save(); - } - - private void StartMinimised_OnClick(object sender, RoutedEventArgs e) - { - _settings.GetClientSetting(SettingsKeys.StartMinimised).BoolValue = - (bool)StartMinimised.IsChecked; - _settings.Save(); - } - - private void AllowDCSPTT_OnClick(object sender, RoutedEventArgs e) - { - _settings.GetClientSetting(SettingsKeys.AllowDCSPTT).BoolValue = - (bool)AllowDCSPTT.IsChecked; - _settings.Save(); - } - - private void AlwaysAllowHotas_OnClick(object sender, RoutedEventArgs e) - { - _settings.GetClientSetting(SettingsKeys.AlwaysAllowHotasControls).BoolValue = - (bool)AlwaysAllowHotas.IsChecked; - _settings.Save(); - - } - } + _serverSettingsWindow?.Close(); + + _serverSettingsWindow = new ServerSettingsWindow(); + _serverSettingsWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner; + _serverSettingsWindow.Owner = this; + _serverSettingsWindow.ShowDialog(); + } + else + { + _serverSettingsWindow?.Close(); + _serverSettingsWindow = null; + } + } + + private void AutoConnectPromptToggle_Click(object sender, RoutedEventArgs e) + { + _settings.GetClientSetting(SettingsKeys.AutoConnectPrompt).BoolValue = + (bool) AutoConnectPromptToggle.IsChecked; + _settings.Save(); + } + + private void RadioOverlayTaskbarItem_Click(object sender, RoutedEventArgs e) + { + _settings.GetClientSetting(SettingsKeys.RadioOverlayTaskbarHide).BoolValue = + (bool) RadioOverlayTaskbarItem.IsChecked; + _settings.Save(); + + if (_radioOverlayWindow != null) + _radioOverlayWindow.ShowInTaskbar = !_settings.GetClientSetting(SettingsKeys.RadioOverlayTaskbarHide).BoolValue; + else if (_awacsRadioOverlay != null) _awacsRadioOverlay.ShowInTaskbar = !_settings.GetClientSetting(SettingsKeys.RadioOverlayTaskbarHide).BoolValue; + } + + + private void DCSRefocus_OnClick_Click(object sender, RoutedEventArgs e) + { + _settings.GetClientSetting(SettingsKeys.RefocusDCS).BoolValue = + (bool) RefocusDCS.IsChecked; + _settings.Save(); + } + + private void ExpandInputDevices_OnClick_Click(object sender, RoutedEventArgs e) + { + MessageBox.Show( + "You must restart SRS for this setting to take effect.\n\nTurning this on will allow almost any DirectX device to be used as input expect a Mouse but may cause issues with other devices being detected", + "Restart SimpleRadio Standalone", MessageBoxButton.OK, + MessageBoxImage.Warning); + + _settings.GetClientSetting(SettingsKeys.ExpandControls).BoolValue = + (bool) ExpandInputDevices.IsChecked; + _settings.Save(); + } + + private void LaunchAddressTab(object sender, RoutedEventArgs e) + { + TabControl.SelectedItem = FavouritesSeversTab; + } + + private void RadioSoundEffects_OnClick(object sender, RoutedEventArgs e) + { + _settings.GetClientSetting(SettingsKeys.RadioEffects).BoolValue = + (bool) RadioSoundEffects.IsChecked; + _settings.Save(); + } + + private void RadioTxStart_Click(object sender, RoutedEventArgs e) + { + _settings.GetClientSetting(SettingsKeys.RadioTxEffects_Start).BoolValue = + (bool) RadioTxStartToggle.IsChecked; + _settings.Save(); + } + + private void RadioTxEnd_Click(object sender, RoutedEventArgs e) + { + _settings.GetClientSetting(SettingsKeys.RadioTxEffects_End).BoolValue = + (bool) RadioTxEndToggle.IsChecked; + _settings.Save(); + } + + private void RadioRxStart_Click(object sender, RoutedEventArgs e) + { + _settings.GetClientSetting(SettingsKeys.RadioRxEffects_Start).BoolValue = + (bool) RadioRxStartToggle.IsChecked; + _settings.Save(); + } + + private void RadioRxEnd_Click(object sender, RoutedEventArgs e) + { + _settings.GetClientSetting(SettingsKeys.RadioRxEffects_End).BoolValue = + (bool) RadioRxEndToggle.IsChecked; + _settings.Save(); + } + + private void AudioSelectChannel_OnClick(object sender, RoutedEventArgs e) + { + _settings.GetClientSetting(SettingsKeys.AutoSelectPresetChannel).BoolValue = + (bool) AutoSelectChannel.IsChecked; + _settings.Save(); + } + + private void RadioSoundEffectsClipping_OnClick(object sender, RoutedEventArgs e) + { + _settings.GetClientSetting(SettingsKeys.RadioEffectsClipping).BoolValue = + (bool)RadioSoundEffectsClipping.IsChecked; + _settings.Save(); + + } + + private void MinimiseToTray_OnClick(object sender, RoutedEventArgs e) + { + _settings.GetClientSetting(SettingsKeys.MinimiseToTray).BoolValue = + (bool)MinimiseToTray.IsChecked; + _settings.Save(); + } + + private void StartMinimised_OnClick(object sender, RoutedEventArgs e) + { + _settings.GetClientSetting(SettingsKeys.StartMinimised).BoolValue = + (bool)StartMinimised.IsChecked; + _settings.Save(); + } + + private void AllowDCSPTT_OnClick(object sender, RoutedEventArgs e) + { + _settings.GetClientSetting(SettingsKeys.AllowDCSPTT).BoolValue = + (bool)AllowDCSPTT.IsChecked; + _settings.Save(); + } + + private void AlwaysAllowHotas_OnClick(object sender, RoutedEventArgs e) + { + _settings.GetClientSetting(SettingsKeys.AlwaysAllowHotasControls).BoolValue = + (bool)AlwaysAllowHotas.IsChecked; + _settings.Save(); + + } + } } \ No newline at end of file diff --git a/DCS-SR-Common/Network/UpdaterChecker.cs b/DCS-SR-Common/Network/UpdaterChecker.cs index afe8e0db3..286cdb269 100644 --- a/DCS-SR-Common/Network/UpdaterChecker.cs +++ b/DCS-SR-Common/Network/UpdaterChecker.cs @@ -13,7 +13,7 @@ public class UpdaterChecker { public static readonly string MINIMUM_PROTOCOL_VERSION = "1.5.0.0"; - public static readonly string VERSION = "1.5.0.0"; + public static readonly string VERSION = "1.5.1.0"; public static async void CheckForUpdate() { diff --git a/DCS-SimpleRadio Server/Properties/AssemblyInfo.cs b/DCS-SimpleRadio Server/Properties/AssemblyInfo.cs index 259297074..e0ea79dbe 100644 --- a/DCS-SimpleRadio Server/Properties/AssemblyInfo.cs +++ b/DCS-SimpleRadio Server/Properties/AssemblyInfo.cs @@ -52,5 +52,5 @@ // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.5.0.0")] -[assembly: AssemblyFileVersion("1.5.0.0")] \ No newline at end of file +[assembly: AssemblyVersion("1.5.1.0")] +[assembly: AssemblyFileVersion("1.5.1.0")] \ No newline at end of file diff --git a/Installer/Properties/AssemblyInfo.cs b/Installer/Properties/AssemblyInfo.cs index f8ea09966..98014a9dd 100644 --- a/Installer/Properties/AssemblyInfo.cs +++ b/Installer/Properties/AssemblyInfo.cs @@ -52,5 +52,5 @@ // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.5.0.0")] -[assembly: AssemblyFileVersion("1.5.0.0")] \ No newline at end of file +[assembly: AssemblyVersion("1.5.1.0")] +[assembly: AssemblyFileVersion("1.5.1.0")] \ No newline at end of file diff --git a/Scripts/DCS-SRS-AutoConnectGameGUI.lua b/Scripts/DCS-SRS-AutoConnectGameGUI.lua index d2eb96030..29f57c624 100644 --- a/Scripts/DCS-SRS-AutoConnectGameGUI.lua +++ b/Scripts/DCS-SRS-AutoConnectGameGUI.lua @@ -1,4 +1,4 @@ --- Version 1.5.0.0 +-- Version 1.5.1.0 -- ONLY COPY THIS FILE IS YOU ARE GOING TO HOST A SERVER! -- The file must be in Saved Games\DCS\Scripts\Hooks or Saved Games\DCS.openalpha\Scripts\Hooks -- Make sure you enter the correct address into SERVER_SRS_HOST below. @@ -57,4 +57,4 @@ SRSAuto.onPlayerChangeSlot = function(id) end DCS.setUserCallbacks(SRSAuto) -net.log("Loaded - DCS-SRS-AutoConnect") +net.log("Loaded - DCS-SRS-AutoConnect 1.5.1.0") diff --git a/Scripts/DCS-SRS-OverlayGameGUI.lua b/Scripts/DCS-SRS-OverlayGameGUI.lua index 0946d6c05..7ad170406 100644 --- a/Scripts/DCS-SRS-OverlayGameGUI.lua +++ b/Scripts/DCS-SRS-OverlayGameGUI.lua @@ -1,4 +1,4 @@ --- Version 1.5.0.0 +-- Version 1.5.1.0 -- Make sure you COPY this file to the same location as the Export.lua as well! -- Otherwise the Overlay will not work @@ -456,4 +456,4 @@ end DCS.setUserCallbacks(srsOverlay) -net.log("Loaded - DCS-SRS Overlay GameGUI - Ciribob") \ No newline at end of file +net.log("Loaded - DCS-SRS Overlay GameGUI - Ciribob - 1.5.1.0 ") \ No newline at end of file diff --git a/Scripts/DCS-SRSGameGUI.lua b/Scripts/DCS-SRSGameGUI.lua index 37b09dc36..9064423b5 100644 --- a/Scripts/DCS-SRSGameGUI.lua +++ b/Scripts/DCS-SRSGameGUI.lua @@ -1,4 +1,4 @@ --- Version 1.5.0.0 +-- Version 1.5.1.0 -- Make sure you COPY this file to the same location as the Export.lua as well! -- Otherwise the Radio Might not work @@ -127,4 +127,4 @@ end DCS.setUserCallbacks(SRS) -net.log("Loaded - DCS-SRS GameGUI") \ No newline at end of file +net.log("Loaded - DCS-SRS GameGUI - Ciribob - 1.5.1.0") \ No newline at end of file diff --git a/Scripts/DCS-SimpleRadioStandalone.lua b/Scripts/DCS-SimpleRadioStandalone.lua index 3dc8a113c..23d23dc0b 100644 --- a/Scripts/DCS-SimpleRadioStandalone.lua +++ b/Scripts/DCS-SimpleRadioStandalone.lua @@ -1,4 +1,4 @@ --- Version 1.5.0.0 +-- Version 1.5.1.0 -- Special thanks to Cap. Zeen, Tarres and Splash for all the help -- with getting the radio information :) -- Add (without the --) To the END OF your Export.lua to enable Simple Radio Standalone : @@ -1789,10 +1789,16 @@ end function SR.getRadioModulation(_deviceId) local _device = GetDevice(_deviceId) - if _device then - return _device:get_modulation() + local _modulation = 0 + + if _device then + + pcall(function() + _modulation = _device:get_modulation() + end) + end - return 0 + return _modulation end function SR.rerange(_val,_minMax,_limitMinMax) diff --git a/install-build/DCS-SRS-AutoConnectGameGUI.lua b/install-build/DCS-SRS-AutoConnectGameGUI.lua index d2eb96030..29f57c624 100644 --- a/install-build/DCS-SRS-AutoConnectGameGUI.lua +++ b/install-build/DCS-SRS-AutoConnectGameGUI.lua @@ -1,4 +1,4 @@ --- Version 1.5.0.0 +-- Version 1.5.1.0 -- ONLY COPY THIS FILE IS YOU ARE GOING TO HOST A SERVER! -- The file must be in Saved Games\DCS\Scripts\Hooks or Saved Games\DCS.openalpha\Scripts\Hooks -- Make sure you enter the correct address into SERVER_SRS_HOST below. @@ -57,4 +57,4 @@ SRSAuto.onPlayerChangeSlot = function(id) end DCS.setUserCallbacks(SRSAuto) -net.log("Loaded - DCS-SRS-AutoConnect") +net.log("Loaded - DCS-SRS-AutoConnect 1.5.1.0") diff --git a/install-build/DCS-SRS-OverlayGameGUI.lua b/install-build/DCS-SRS-OverlayGameGUI.lua index 0946d6c05..7ad170406 100644 --- a/install-build/DCS-SRS-OverlayGameGUI.lua +++ b/install-build/DCS-SRS-OverlayGameGUI.lua @@ -1,4 +1,4 @@ --- Version 1.5.0.0 +-- Version 1.5.1.0 -- Make sure you COPY this file to the same location as the Export.lua as well! -- Otherwise the Overlay will not work @@ -456,4 +456,4 @@ end DCS.setUserCallbacks(srsOverlay) -net.log("Loaded - DCS-SRS Overlay GameGUI - Ciribob") \ No newline at end of file +net.log("Loaded - DCS-SRS Overlay GameGUI - Ciribob - 1.5.1.0 ") \ No newline at end of file diff --git a/install-build/DCS-SRSGameGUI.lua b/install-build/DCS-SRSGameGUI.lua index 37b09dc36..9064423b5 100644 --- a/install-build/DCS-SRSGameGUI.lua +++ b/install-build/DCS-SRSGameGUI.lua @@ -1,4 +1,4 @@ --- Version 1.5.0.0 +-- Version 1.5.1.0 -- Make sure you COPY this file to the same location as the Export.lua as well! -- Otherwise the Radio Might not work @@ -127,4 +127,4 @@ end DCS.setUserCallbacks(SRS) -net.log("Loaded - DCS-SRS GameGUI") \ No newline at end of file +net.log("Loaded - DCS-SRS GameGUI - Ciribob - 1.5.1.0") \ No newline at end of file diff --git a/install-build/DCS-SimpleRadioStandalone-1.5.1.0.zip b/install-build/DCS-SimpleRadioStandalone-1.5.1.0.zip new file mode 100644 index 000000000..46ef4d93a Binary files /dev/null and b/install-build/DCS-SimpleRadioStandalone-1.5.1.0.zip differ diff --git a/install-build/Installer.exe b/install-build/Installer.exe index 845895f26..46db1bd27 100644 Binary files a/install-build/Installer.exe and b/install-build/Installer.exe differ diff --git a/install-build/KY-58-RX-1600.wav b/install-build/KY-58-RX-1600.wav index 5bc781ea2..1818e8225 100644 Binary files a/install-build/KY-58-RX-1600.wav and b/install-build/KY-58-RX-1600.wav differ diff --git a/install-build/KY-58-TX-1600.wav b/install-build/KY-58-TX-1600.wav index 78efb7863..f39b536e4 100644 Binary files a/install-build/KY-58-TX-1600.wav and b/install-build/KY-58-TX-1600.wav differ diff --git a/install-build/Readme.txt b/install-build/Readme.txt index 2dd56926a..df81219a7 100644 --- a/install-build/Readme.txt +++ b/install-build/Readme.txt @@ -27,6 +27,8 @@ Copy the rest of the zip file where ever you like and then run, don't forget to Thread on Forums: http://forums.eagle.ru/showthread.php?t=169387 +Create a folder called AudioEffects where ever your SR-ClientRadio.exe is - Drag all 4 sound files into that folder or you wont have sound effects + *** To Install AutoConnect System for SERVERS only *** To enable SRS clients to be prompted automatically to connect just add the DCS-SRS-AutoConnectGameGUI.lua diff --git a/install-build/SR-ClientRadio.exe b/install-build/SR-ClientRadio.exe index efdd493b7..e6ac1ff48 100644 Binary files a/install-build/SR-ClientRadio.exe and b/install-build/SR-ClientRadio.exe differ diff --git a/install-build/SR-Server.exe b/install-build/SR-Server.exe index ddbfa1181..ff44f486e 100644 Binary files a/install-build/SR-Server.exe and b/install-build/SR-Server.exe differ diff --git a/install-build/opus.dll b/install-build/opus.dll index a962869fd..5659a1041 100644 Binary files a/install-build/opus.dll and b/install-build/opus.dll differ diff --git a/install-build/speexdsp.dll b/install-build/speexdsp.dll index b5db53286..360a2b32c 100644 Binary files a/install-build/speexdsp.dll and b/install-build/speexdsp.dll differ