* Control score sounds from UpdateGame instead of the thread pool
_PlayProgressBarSound started a Task that waited for the progress bar
duration and then closed whatever stream was stored in
_ProgressBarSoundStream. When switching rounds on the score screen
before that time had passed, an old task closed the progress bar
sound of the new round too early and played the applause of the
previous round, while the applause of the new round never played.
Remember when the progress bar sound was started and check it in
UpdateGame on the main thread instead. Starting a new progress bar
sound and leaving the screen now also stop a running applause.
* Sync progress bar sound with the score bar animation
The sound duration was computed from the points (10,000 points =
5 seconds), while the bar animation duration depends on the bar size
in the theme and only starts when the bar is first drawn. Both only
matched approximately and drifted apart with other themes.
Add CProgressBar.AnimatesMovement and CProgressBar.IsAnimating. The
score screen now stops the progress bar sound and starts the applause
as soon as no visible score bar is animating anymore. If the theme
has no movement animation, the previous duration is used.
* Make stream id allocation in CPlaybackBase thread-safe
Load assigned stream ids with _NextId++ outside of the lock. Load is
also called from other threads, so two streams could get the same id
and a later Close or Play could hit the wrong stream.
Use Interlocked.Increment instead. Ids still start at 0.
* Do not play the progress bar sound for 0 points
With 0 points the score bars do not move, so the progress bar sound
started and stopped again right away, which could be heard as a short
click. Do not start it at all if the highest score is 0.
* Apply R# clean-up for reliability
- Migrate to NUnit4 Assert.That
- Update dependencise versions
- Fix LangVersion to 9.0 (until net core migration)
- Reformat code
- Apply 'var' style (easier for refactoring)
- Remove redundant parentheses
- Add braces on every condition blocks (1 line)
- Uses new null checking patterns
- Uses AutoProperties when possible
- Uses string scalar type instead of class
- Clean usings
- Add .orig files in git ignore
- Rename some properties to respect defined conventions and US format definition
- ID -> Id
- BPM -> Bpm
- Finish -> End
* Update R# settings according to the clean-up made