From 8ad36377f2b8c1bdb21a2be103ef0c3d3934e4ef Mon Sep 17 00:00:00 2001 From: Mark Hamann Date: Fri, 26 Feb 2016 08:08:53 -0800 Subject: [PATCH] Improved design and made the changes to tone characteristics update on next change. --- MorseTrainer/Form1.cs | 33 +++++++++----- MorseTrainer/SoundPlayerAsync.cs | 75 +++++++++++++++++++++----------- MorseTrainer/ToneGenerator.cs | 65 +++++++++++++++++++++------ 3 files changed, 125 insertions(+), 48 deletions(-) diff --git a/MorseTrainer/Form1.cs b/MorseTrainer/Form1.cs index c135f4d..949761c 100644 --- a/MorseTrainer/Form1.cs +++ b/MorseTrainer/Form1.cs @@ -119,8 +119,9 @@ namespace MorseTrainer _runner.StopDelayEnter += _runner_StopDelayEnter; _runner.StopDelayExit += _runner_StopDelayExit; _runner.Abort += _runner_Abort; - _player.QueueEmpty += _player_QueueEmpty; + _player.Dequeued += _player_Dequeued; _player.PlayingFinished += _player_PlayingFinished; + _toneGenerator.UpdateRequired += _toneGenerator_UpdateRequired; cmbKoch.Items.Clear(); for (int i = 0; i < Koch.Length; ++i) @@ -216,8 +217,7 @@ namespace MorseTrainer { if (_runner.IsRunning) { - _pendingWavestream = null; - _player.Clear(); + _player.ClearQueue(); btnStartStop.Enabled = false; _runner.RequestStop(); } @@ -234,13 +234,18 @@ namespace MorseTrainer private void FirstWaveReadyCallback(IAsyncResult result) { - _pendingWavestream = (WaveStream)result.AsyncState; + WaveStream waveStream = (WaveStream)result.AsyncState; + _player.Enqueue(waveStream); _runner.RequestStart(); } private void WaveReadyCallback(IAsyncResult result) { - _pendingWavestream = (WaveStream)result.AsyncState; + WaveStream waveStream = (WaveStream)result.AsyncState; + if (waveStream != null) + { + _player.Enqueue(waveStream); + } } private void _runner_StartDelayEnter(object sender, EventArgs e) @@ -253,21 +258,28 @@ namespace MorseTrainer private void _runner_MorseEnter(object sender, EventArgs e) { - _player.Start(_pendingWavestream); - _pendingWavestream = null; + _player.Start(); String word = _charGenerator.CreateRandomString(); _builder.StartBuildAsync(word, new AsyncCallback(WaveReadyCallback)); } - private void _player_QueueEmpty(object sender, EventArgs e) + private void _player_Dequeued(object sender, EventArgs e) { if (_runner.ContinueMorse) { - _player.Enqueue(_pendingWavestream); _builder.StartBuildAsync(_charGenerator.CreateRandomString(), new AsyncCallback(WaveReadyCallback)); } } + private void _toneGenerator_UpdateRequired(object sender, EventArgs e) + { + // This indicates that the tone has changed. If we are currently sending code, + // we need to remove any queued tones and in progress tones and send new tones instead + _toneGenerator.Update(); + _player.ClearQueue(); + _builder.StartBuildAsync(_charGenerator.CreateRandomString(), new AsyncCallback(WaveReadyCallback)); + } + private void _player_PlayingFinished(object sender, EventArgs e) { _runner.AcknowledgeSendEnd(); @@ -275,6 +287,8 @@ namespace MorseTrainer private void _runner_MorseExit(object sender, EventArgs e) { + _player.Stop(); + _player.ClearQueue(); } private void _runner_StopDelayEnter(object sender, EventArgs e) @@ -1251,7 +1265,6 @@ namespace MorseTrainer private Runner _runner; private Analyzer _analyzer; private StringBuilder _recorded; - private WaveStream _pendingWavestream; #endregion } diff --git a/MorseTrainer/SoundPlayerAsync.cs b/MorseTrainer/SoundPlayerAsync.cs index 06338f0..ebe1101 100644 --- a/MorseTrainer/SoundPlayerAsync.cs +++ b/MorseTrainer/SoundPlayerAsync.cs @@ -36,6 +36,7 @@ namespace MorseTrainer /// public SoundPlayerAsync() { + _sending = false; _mediaSoundPlayer = new System.Media.SoundPlayer(); _queue = new Queue(); _sentString = new StringBuilder(); @@ -50,24 +51,36 @@ namespace MorseTrainer { while (!_stopThread) { - WaveStream waveToPlay = Dequeue(); - if (waveToPlay != null) + bool go = false; + lock (this) { - _mediaSoundPlayer.Stream = waveToPlay.Stream; - _mediaSoundPlayer.Load(); - _mediaSoundPlayer.PlaySync(); - _sentString.Append(waveToPlay.Text); - _sentString.Append(' '); - // All done - if (Count == 0) + if (!_sending) { - OnPlayingFinished(); + System.Threading.Monitor.Wait(this); + } + go = _sending && !_stopThread; + } + if (go) + { + WaveStream waveToPlay = Dequeue(); + if (waveToPlay != null) + { + _mediaSoundPlayer.Stream = waveToPlay.Stream; + _mediaSoundPlayer.Load(); + _mediaSoundPlayer.PlaySync(); + _sentString.Append(waveToPlay.Text); + _sentString.Append(' '); + // All done + if (Count == 0) + { + OnPlayingFinished(); + } } } } lock(this) { - _stopped = true; + _threadStopped = true; System.Threading.Monitor.Pulse(this); } } @@ -86,7 +99,7 @@ namespace MorseTrainer wave = _queue.Dequeue(); if (_queue.Count == 0) { - OnQueueEmpty(); + OnDequeued(); } } } @@ -96,11 +109,21 @@ namespace MorseTrainer /// /// Puts a WAV onto the queue and resets the strings /// - /// A WaveStream - public void Start(WaveStream wave) + public void Start() { - Enqueue(wave); - _sentString.Clear(); + lock(this) + { + _sending = true; + _sentString.Clear(); + } + } + + public void Stop() + { + lock(this) + { + _sending = false; + } } /// @@ -129,9 +152,9 @@ namespace MorseTrainer } /// - /// Clear all waves from the queue when aborting + /// ClearQueue all waves from the queue when aborting /// - public void Clear() + public void ClearQueue() { lock(this) { @@ -168,12 +191,12 @@ namespace MorseTrainer } /// - /// The queue has been emptied. Action in this should be quick + /// The queue has been dequeued. Action in this should be quick /// - public event EventHandler QueueEmpty; - protected void OnQueueEmpty() + public event EventHandler Dequeued; + protected void OnDequeued() { - EventHandler handler = QueueEmpty; + EventHandler handler = Dequeued; if (handler != null) { handler(this, EventArgs.Empty); @@ -190,10 +213,10 @@ namespace MorseTrainer if (_thread != null && !_stopThread) { _stopThread = true; - _stopped = false; + _threadStopped = false; _queue.Clear(); System.Threading.Monitor.Pulse(this); - if (!_stopped) + if (!_threadStopped) { System.Threading.Monitor.Wait(this); } @@ -206,7 +229,9 @@ namespace MorseTrainer private System.Threading.Thread _thread; private bool _stopThread; - private bool _stopped; + private bool _threadStopped; + + private bool _sending; private Queue _queue; private System.Media.SoundPlayer _mediaSoundPlayer; private StringBuilder _sentString; diff --git a/MorseTrainer/ToneGenerator.cs b/MorseTrainer/ToneGenerator.cs index 22b2a6c..f970d80 100644 --- a/MorseTrainer/ToneGenerator.cs +++ b/MorseTrainer/ToneGenerator.cs @@ -85,6 +85,7 @@ namespace MorseTrainer _WPM = 0; _farnsworthWPM = 0; _volume = 0.0f; + _updateRequired = true; } private float WpmToMillesecPerElement(float wpm) @@ -102,24 +103,57 @@ namespace MorseTrainer /// public void Update() { - // changing tone frequency or WPM will cause the tones to be regenerated - if (_frequency == 0 || _frequency != _newFrequency || _newWPM != _WPM || _newVolume != _volume) + if (_updateRequired) { - _volume = _newVolume; - _frequency = _newFrequency; - _WPM = _newWPM; - // ms per element = WPM * 1000 ms per sec / 60 words per second / 50 elements - _msPerElement = WpmToMillesecPerElement(_WPM); + _updateRequired = false; - float samplesPerCycle = SAMPLES_PER_SECOND / _frequency; + // changing tone frequency or WPM will cause the tones to be regenerated + if (_frequency == 0 || _frequency != _newFrequency || _newWPM != _WPM || _newVolume != _volume) + { + _volume = _newVolume; + _frequency = _newFrequency; + _WPM = _newWPM; + // ms per element = WPM * 1000 ms per sec / 60 words per second / 50 elements + _msPerElement = WpmToMillesecPerElement(_WPM); - _samplesPerCycle = (UInt16)(samplesPerCycle + 0.5); - CreateTones(); + float samplesPerCycle = SAMPLES_PER_SECOND / _frequency; + + _samplesPerCycle = (UInt16)(samplesPerCycle + 0.5); + CreateTones(); + } + if (_farnsworthWPM == 0 || _farnsworthWPM != _newFarnsworthWPM) + { + _farnsworthWPM = _newFarnsworthWPM; + _msPerFarnsworthElement = WpmToMillesecPerElement(_farnsworthWPM); + } } - if (_farnsworthWPM == 0 || _farnsworthWPM != _newFarnsworthWPM) + } + + /// + /// The user has made a change that will require an update + /// + public event EventHandler UpdateRequired; + protected void OnUpdateRequired() + { + if (!_updateRequired) { - _farnsworthWPM = _newFarnsworthWPM; - _msPerFarnsworthElement = WpmToMillesecPerElement(_farnsworthWPM); + _updateRequired = true; + EventHandler handler = UpdateRequired; + if (handler != null) + { + handler(this, EventArgs.Empty); + } + } + } + + /// + /// Gets if there is a required update to generated tones + /// + public bool IsUpdateRequired + { + get + { + return _updateRequired; } } @@ -150,6 +184,7 @@ namespace MorseTrainer throw new ArgumentOutOfRangeException("Frequency"); } _newFrequency = value; + OnUpdateRequired(); } } @@ -169,6 +204,7 @@ namespace MorseTrainer throw new ArgumentOutOfRangeException("Volume"); } _newVolume = value; + OnUpdateRequired(); } } @@ -199,6 +235,7 @@ namespace MorseTrainer throw new ArgumentOutOfRangeException("WPM"); } _newWPM = (float)Math.Round(value * 2) / 2; + OnUpdateRequired(); } } @@ -229,6 +266,7 @@ namespace MorseTrainer throw new ArgumentOutOfRangeException("WPM"); } _newFarnsworthWPM = (float)Math.Round(value * 2) / 2; + OnUpdateRequired(); } } @@ -382,6 +420,7 @@ namespace MorseTrainer return CreateSpace(difference); } + private bool _updateRequired; private UInt16 _frequency; private UInt16 _newFrequency; private float _WPM;