using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Input;
using System.Windows.Threading;
using Chernobyl.App.Core;
using Chernobyl.Collections.Generic;
using Chernobyl.Extensions;
using Chernobyl.Generation;
using Chernobyl.Mathematics;
using Chernobyl.Mathematics.Space;
using GalaSoft.MvvmLight.CommandWpf;
namespace Chernobyl.App.Patterns
{
///
/// Represents the view of an .
///
public class PatternControl : ViewModel
{
/// The to be displayed.
/// The instance that
public PatternControl(IPattern pattern, IWavePlayerFactory wavePlayerFactory)
{
_pattern = pattern;
_wavePlayerFactory = wavePlayerFactory;
// Create the timer that will display the current time of the music.
var timeSpan = TimeSpan.FromMilliseconds(50);
_dispatcherTimer = new DispatcherTimer();
_dispatcherTimer.Tick += (sender, args) => Seconds += (float)timeSpan.TotalSeconds;
_dispatcherTimer.Interval = timeSpan;
// Normalize and time the pattern provided so that it sounds like music.
const int beatLength = SampleRate / 6; // half second (approximate)
//const int beatLength = SampleRate / 3; // full second (approximate)
const double minFreq = 200.0;
const double maxFreq = 400.0;
// Note that we intersperse 0s between each normalized value. This is done to create a
// staccato like sound (like that of a drum).
_frequency = _pattern.Data
.Select(value => value.Normalize(Range.Unit, Tuple.Create(minFreq, maxFreq)))
.Intersperse(0)
.Select(freq => Tuple.Create(freq, beatLength));
Reset();
// Note: execution method must be a named method due to use of WeakReference, see
// RelayCommand for more info.
PlayCommand = new RelayCommand(Play, () => _player != null);
StopCommand = new RelayCommand(Stop, () => _player != null);
ResetCommand = new RelayCommand(Reset);
}
///
/// Starts the audible playing of the pattern that was given to this instance in the constructor.
///
public ICommand PlayCommand { get; }
///
/// Stops the audible playing of the pattern that was given to this instance in the constructor.
///
public ICommand StopCommand { get; }
///
/// Returns the music back to the start.
///
public ICommand ResetCommand { get; }
///
/// The amount of time that has passed since the beginning of the song.
///
public float Seconds
{
get => _seconds;
private set
{
_seconds = value;
RaisePropertyChanged();
}
}
///
/// The state of the song being played.
///
public PlayerState State => _player?.State ?? PlayerState.Stopped;
void Play()
{
_player.Play();
_dispatcherTimer.Start();
RaisePropertyChanged(nameof(State));
}
void Stop()
{
_player.Stop();
_dispatcherTimer.Stop();
RaisePropertyChanged(nameof(State));
}
void Reset()
{
_dispatcherTimer.Stop();
_player?.Dispose();
_player = null;
Seconds = 0;
const float amplitude = 0.25f;
_frequencyEnumerator?.Dispose();
_frequencyEnumerator = _frequency.GetEnumerator();
var square = _frequencyEnumerator.AsFunc(Tuple.Create(0.0, 1)).Square();
var waveFunc = Generation.Audio.CreateSinOscillator(square, () => SampleRate, () => amplitude)
.Select(Convert.ToSingle);
_player = _wavePlayerFactory.Create(new WavePlayerConfig(waveFunc, SampleRate));
RaisePropertyChanged(nameof(State));
}
///
public override void Cleanup()
{
_player?.Dispose();
_frequencyEnumerator?.Dispose();
}
const int SampleRate = 16000;
readonly IPattern _pattern;
readonly IWavePlayerFactory _wavePlayerFactory;
readonly DispatcherTimer _dispatcherTimer;
readonly IEnumerable> _frequency;
IEnumerator> _frequencyEnumerator;
IPlayer _player;
float _seconds;
}
}