using System; using Chernobyl.App.Core; using Chernobyl.Collections.Generic; using Chernobyl.Extensions; using Chernobyl.Generation; using Chernobyl.Generation.Old; using Chernobyl.Mathematics.Space; using GalaSoft.MvvmLight.CommandWpf; using LiveCharts; using LiveCharts.Wpf; namespace Chernobyl.App.Audio { /// /// A view model representing a song. /// public class SongViewModel : ViewModel { /// /// Constructor. /// public SongViewModel(IWavePlayerFactory wavePlayerFactory) { // TODO: SongViewModel should take the IPlayer, not the factory. Need a View/Model that handles creation. Player = wavePlayerFactory.Create(CreateWavePlayerConfig()); CreateMusicCommand = new RelayCommand(CreateMusic); PlayMusicCommand = new RelayCommand(Play); StopMusicCommand = new RelayCommand(Stop); TimeAxisFormatter = value => (value * TimeInterval.TotalSeconds).ToString(); CreateMusic(); } WavePlayerConfig CreateWavePlayerConfig() { var sampleRate = 16000; var amplitude = 0.25f; var halfSecond = sampleRate / 6; var available = new IPattern[] { new BasicPattern(200), new BasicPattern(225), new BasicPattern(250), new BasicPattern(275), new BasicPattern(300), new BasicPattern(325), new BasicPattern(350), new BasicPattern(375), new BasicPattern(400), }; // Give each frequency 'beat' a position in the wave. var frequencyPattern = available.CreateParent(new Random()).Data .GetEnumerator().AsFunc().Select(freq => Tuple.Create(freq, halfSecond)); var waveFunc = Generation.Audio.CreateSinOscillator(frequencyPattern.Square(), () => sampleRate, () => amplitude) .Select(Convert.ToSingle); return new WavePlayerConfig(waveFunc, sampleRate); } /// public override void Cleanup() { Player.Dispose(); base.Cleanup(); } /// /// The instance that plays music. /// public IPlayer Player { get; set; } /// /// The time between each beat of the song in seconds. /// public TimeSpan TimeInterval => TimeSpan.FromMilliseconds(500); /// /// The indicators current location as an floating point index. For example, 0 is the first /// data point, 1 is the second, .5 is halfway between the first and second data points. /// public double CurrentDatumIndex => Player.Position.TotalSeconds * 2; void CreateMusic() { Stop(); MusicSeries.Clear(); MusicSeries.Add(new LineSeries { Title = "Beat 1", //Values = new ChartValues(Wave.Sin.Step(.1).Select(value => (value + 37 * 2)).AsEnumerable().Take(150).ToArray()) Values = new ChartValues() }); Reset(); } void Play() { Player.Play(); } void Stop() { Player.Stop(); } /// /// Sets the player to the start of the song. /// void Reset() { } /// /// The command to create music. /// public RelayCommand CreateMusicCommand { get; } /// /// Plays the current music when invoked. /// public RelayCommand PlayMusicCommand { get; } /// /// Stops the current music when invoked. /// public RelayCommand StopMusicCommand { get; } /// /// The data representing the music. /// public SeriesCollection MusicSeries { get; } = new SeriesCollection(); /// /// The instance that formats the time series. /// public Func TimeAxisFormatter { get; } } }