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; }
}
}