diff --git a/MusicalChart/MusicalChart.sln b/MusicalChart/MusicalChart.sln
new file mode 100644
index 0000000..1852f5b
--- /dev/null
+++ b/MusicalChart/MusicalChart.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.14.36202.13 d17.14
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MusicalChart", "MusicalChart\MusicalChart.csproj", "{1323368D-AE6D-4844-8E2D-879B8534BC5B}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {1323368D-AE6D-4844-8E2D-879B8534BC5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1323368D-AE6D-4844-8E2D-879B8534BC5B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1323368D-AE6D-4844-8E2D-879B8534BC5B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1323368D-AE6D-4844-8E2D-879B8534BC5B}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {3138D91D-F6B1-4BE8-9CB9-C9D651DFD410}
+ EndGlobalSection
+EndGlobal
diff --git a/MusicalChart/MusicalChart/App.xaml b/MusicalChart/MusicalChart/App.xaml
new file mode 100644
index 0000000..bd01178
--- /dev/null
+++ b/MusicalChart/MusicalChart/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/MusicalChart/MusicalChart/App.xaml.cs b/MusicalChart/MusicalChart/App.xaml.cs
new file mode 100644
index 0000000..2c270ca
--- /dev/null
+++ b/MusicalChart/MusicalChart/App.xaml.cs
@@ -0,0 +1,14 @@
+using System.Configuration;
+using System.Data;
+using System.Windows;
+
+namespace MusicalChart
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+
+}
diff --git a/MusicalChart/MusicalChart/AssemblyInfo.cs b/MusicalChart/MusicalChart/AssemblyInfo.cs
new file mode 100644
index 0000000..b0ec827
--- /dev/null
+++ b/MusicalChart/MusicalChart/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/MusicalChart/MusicalChart/Audio/bass.mp3 b/MusicalChart/MusicalChart/Audio/bass.mp3
new file mode 100644
index 0000000..7973b0d
Binary files /dev/null and b/MusicalChart/MusicalChart/Audio/bass.mp3 differ
diff --git a/MusicalChart/MusicalChart/Audio/drums.mp3 b/MusicalChart/MusicalChart/Audio/drums.mp3
new file mode 100644
index 0000000..a3106a9
Binary files /dev/null and b/MusicalChart/MusicalChart/Audio/drums.mp3 differ
diff --git a/MusicalChart/MusicalChart/Audio/others.mp3 b/MusicalChart/MusicalChart/Audio/others.mp3
new file mode 100644
index 0000000..9e460d4
Binary files /dev/null and b/MusicalChart/MusicalChart/Audio/others.mp3 differ
diff --git a/MusicalChart/MusicalChart/Audio/vocals.mp3 b/MusicalChart/MusicalChart/Audio/vocals.mp3
new file mode 100644
index 0000000..f0f949c
Binary files /dev/null and b/MusicalChart/MusicalChart/Audio/vocals.mp3 differ
diff --git a/MusicalChart/MusicalChart/MainWindow.xaml b/MusicalChart/MusicalChart/MainWindow.xaml
new file mode 100644
index 0000000..6716df8
--- /dev/null
+++ b/MusicalChart/MusicalChart/MainWindow.xaml
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ M0,25.400002L9.7000122,25.400002 9.7000122,29.4 0,29.4z M0,17.299997L13.700012,17.299997 13.700012,21.299997 0,21.299997z M0,9.4999973L17.799988,9.4999973 17.799988,13.500011 0,13.500011z M0,1.8000026L19.299988,1.8000026 19.299988,5.8000013 0,5.8000013z M21.100037,0L25.5,0 31.400024,5.9000074C31.400024,5.9000074 32,6.4999978 32,6.9999978 32,7.70001 31.400024,8.3000003 30.700012,8.3000003 30.600037,8.3000003 29.799988,7.9000064 29.700012,7.8000008L25.5,4.3000018 25.5,26.100014C25.5,29.000006 23.100037,31.4 20.200012,31.4 17.299988,31.4 14.900024,29.000006 14.900024,26.100014 14.900024,23.199989 17.299988,20.799997 20.200012,20.799997 20.5,20.799997 20.799988,20.799997 21.100037,20.900004z
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MusicalChart/MusicalChart/MainWindow.xaml.cs b/MusicalChart/MusicalChart/MainWindow.xaml.cs
new file mode 100644
index 0000000..889cf73
--- /dev/null
+++ b/MusicalChart/MusicalChart/MainWindow.xaml.cs
@@ -0,0 +1,19 @@
+using System.Windows;
+
+namespace MusicalChart
+{
+ public partial class MainWindow : Window
+ {
+ public MainWindow()
+ {
+ InitializeComponent();
+ Loaded += OnLoaded;
+ }
+
+ private void OnLoaded(object sender, RoutedEventArgs e)
+ {
+ playbackLine.X1 = 0;
+ _ = viewModel.InitializeAsync(playbackLine);
+ }
+ }
+}
\ No newline at end of file
diff --git a/MusicalChart/MusicalChart/Model/RelayCommand.cs b/MusicalChart/MusicalChart/Model/RelayCommand.cs
new file mode 100644
index 0000000..eb46cbd
--- /dev/null
+++ b/MusicalChart/MusicalChart/Model/RelayCommand.cs
@@ -0,0 +1,32 @@
+using System.Windows.Input;
+
+namespace MusicalChart
+{
+ public class RelayCommand : ICommand
+ {
+ private readonly Action _execute;
+ private readonly Func? _canExecute;
+
+ public RelayCommand(Action execute, Func? canExecute = null)
+ {
+ _execute = execute ?? throw new ArgumentNullException(nameof(execute));
+ _canExecute = canExecute;
+ }
+
+ public event EventHandler? CanExecuteChanged
+ {
+ add { CommandManager.RequerySuggested += value; }
+ remove { CommandManager.RequerySuggested -= value; }
+ }
+
+ public bool CanExecute(object? parameter)
+ {
+ return _canExecute?.Invoke() ?? true;
+ }
+
+ public void Execute(object? parameter)
+ {
+ _execute();
+ }
+ }
+}
\ No newline at end of file
diff --git a/MusicalChart/MusicalChart/Model/Track.cs b/MusicalChart/MusicalChart/Model/Track.cs
new file mode 100644
index 0000000..ee87d78
--- /dev/null
+++ b/MusicalChart/MusicalChart/Model/Track.cs
@@ -0,0 +1,19 @@
+using System.Windows.Media;
+
+namespace MusicalChart
+{
+ public class Track
+ {
+ public double X { get; set; }
+ public double Y { get; set; }
+
+ public Brush ColorPath { get; set; }
+
+ public Track(double x, double y, Color color)
+ {
+ X = x;
+ Y = y;
+ ColorPath = new SolidColorBrush(color);
+ }
+ }
+}
\ No newline at end of file
diff --git a/MusicalChart/MusicalChart/MusicalChart.csproj b/MusicalChart/MusicalChart/MusicalChart.csproj
new file mode 100644
index 0000000..a424fa4
--- /dev/null
+++ b/MusicalChart/MusicalChart/MusicalChart.csproj
@@ -0,0 +1,36 @@
+
+
+
+ WinExe
+ net9.0-windows
+ enable
+ enable
+ true
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ Always
+
+
+ Always
+
+
+
+
+
+
+
+
diff --git a/MusicalChart/MusicalChart/MusicalChart.csproj.user b/MusicalChart/MusicalChart/MusicalChart.csproj.user
new file mode 100644
index 0000000..88a5509
--- /dev/null
+++ b/MusicalChart/MusicalChart/MusicalChart.csproj.user
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/MusicalChart/MusicalChart/MusicalChart.sln b/MusicalChart/MusicalChart/MusicalChart.sln
new file mode 100644
index 0000000..2a9b8d2
--- /dev/null
+++ b/MusicalChart/MusicalChart/MusicalChart.sln
@@ -0,0 +1,24 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.5.2.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MusicalChart", "MusicalChart.csproj", "{5CF13559-512F-6438-09B7-0474744F4189}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {5CF13559-512F-6438-09B7-0474744F4189}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5CF13559-512F-6438-09B7-0474744F4189}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5CF13559-512F-6438-09B7-0474744F4189}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5CF13559-512F-6438-09B7-0474744F4189}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {AB460BA2-B39A-477B-9A39-036D6F1E4D1C}
+ EndGlobalSection
+EndGlobal
diff --git a/MusicalChart/MusicalChart/ViewModel/AudioService.cs b/MusicalChart/MusicalChart/ViewModel/AudioService.cs
new file mode 100644
index 0000000..84ee706
--- /dev/null
+++ b/MusicalChart/MusicalChart/ViewModel/AudioService.cs
@@ -0,0 +1,127 @@
+using System.IO;
+using System.Windows.Media;
+using System.Windows.Threading;
+
+namespace MusicalChart
+{
+ public class AudioService
+ {
+ #region Fields
+
+ private readonly Dictionary _players = new();
+ private readonly Dispatcher _dispatcher = Dispatcher.CurrentDispatcher;
+ public Action? OnStartPlayback;
+ public Action? OnStopPlayback;
+ private MusicViewModel? _viewModel;
+ private bool _isLoopingEnabled = true;
+
+ #endregion
+
+ #region Methods
+
+ public void SetViewModel(MusicViewModel viewModel)
+ {
+ _viewModel = viewModel;
+ }
+
+ public async Task LoadAudioFile(string instrumentName, string audioFilePath)
+ {
+ var player = new MediaPlayer();
+ var tcs = new TaskCompletionSource();
+
+ player.MediaEnded += Player_MediaEnded;
+
+ var fullPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Audio", audioFilePath);
+ bool fileExists = File.Exists(fullPath);
+
+ if (fileExists)
+ {
+ player.MediaOpened += (s, e) => tcs.SetResult(true);
+ player.MediaFailed += (s, e) => tcs.SetResult(false);
+
+ player.Open(new Uri(fullPath));
+ player.Volume = 0.7;
+
+ await tcs.Task;
+ }
+
+ if (_players.TryGetValue(instrumentName, out var existingPlayer))
+ {
+ existingPlayer.MediaEnded -= Player_MediaEnded;
+ existingPlayer.Close();
+ }
+
+ // Store the new player
+ _players[instrumentName] = player;
+ }
+
+ public async Task PlayAudioWithFade(string instrumentName)
+ {
+ if (!_players.TryGetValue(instrumentName, out var player) || player == null)
+ return;
+
+ bool isEnabled = false;
+ if (_viewModel != null)
+ {
+ isEnabled = instrumentName switch
+ {
+ "Drums" => _viewModel.IsDrumsEnabled,
+ "Bass" => _viewModel.IsBassEnabled,
+ "Others" => _viewModel.IsOthersEnabled,
+ "Vocals" => _viewModel.IsVocalsEnabled,
+ _ => false
+ };
+ }
+
+ if (!isEnabled)
+ {
+ await StopAudioWithFade(instrumentName);
+ return;
+ }
+ await _dispatcher.InvokeAsync(() =>
+ {
+ player.Stop();
+ player.Position = TimeSpan.Zero;
+ player.MediaEnded += Player_MediaEnded;
+ player.Volume = 0.7;
+ player.Play();
+ });
+ }
+
+ public async Task StopAudioWithFade(string instrumentName)
+ {
+ if (!_players.TryGetValue(instrumentName, out var player))
+ return;
+
+ await _dispatcher.InvokeAsync(() =>
+ {
+ player.Stop();
+ });
+ }
+
+ public async Task StopAllWithFade()
+ {
+ foreach (var key in _players.Keys)
+ {
+ await StopAudioWithFade(key);
+ }
+
+ OnStopPlayback?.Invoke();
+ }
+
+ private void Player_MediaEnded(object? sender, EventArgs e)
+ {
+ if (_isLoopingEnabled && sender is MediaPlayer player)
+ {
+ player.Position = TimeSpan.Zero;
+ player.Play();
+ }
+ }
+
+ public void EnableLooping() => _isLoopingEnabled = true;
+ public void DisableLooping() => _isLoopingEnabled = false;
+
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/MusicalChart/MusicalChart/ViewModel/MusicViewModel.cs b/MusicalChart/MusicalChart/ViewModel/MusicViewModel.cs
new file mode 100644
index 0000000..a7b6be3
--- /dev/null
+++ b/MusicalChart/MusicalChart/ViewModel/MusicViewModel.cs
@@ -0,0 +1,369 @@
+using Syncfusion.UI.Xaml.Charts;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Threading;
+
+namespace MusicalChart
+{
+ public class MusicViewModel : INotifyPropertyChanged
+ {
+ #region Fields
+
+ private readonly AudioService _audioService = new();
+ private readonly Random _random = new();
+ private DispatcherTimer? _playbackTimer;
+ private double _currentX;
+ private const double MaxX = 10.5;
+ private VerticalLineAnnotation? _playbackLine;
+
+ private readonly string[] _instrumentNames = {
+ "Drums", "Bass", "Others", "Vocals"
+ };
+
+ private readonly Dictionary _enabledStatus = new();
+ private readonly Dictionary> _chartData = new();
+
+ #endregion
+
+ #region Properties
+
+ private bool _isChartPlaybackActive;
+ public bool IsChartPlaybackActive
+ {
+ get => _isChartPlaybackActive;
+ set { _isChartPlaybackActive = value; OnPropertyChanged(nameof(IsChartPlaybackActive)); }
+ }
+
+ private double _playbackPosition;
+ public double PlaybackPosition
+ {
+ get => _playbackPosition;
+ set { _playbackPosition = value; OnPropertyChanged(nameof(PlaybackPosition)); }
+ }
+
+ private bool _isPlayButtonEnabled = true;
+ public bool IsPlayButtonEnabled
+ {
+ get => _isPlayButtonEnabled;
+ set { _isPlayButtonEnabled = value; OnPropertyChanged(nameof(IsPlayButtonEnabled)); }
+ }
+
+ private bool _isStopButtonEnabled;
+ public bool IsStopButtonEnabled
+ {
+ get => _isStopButtonEnabled;
+ set { _isStopButtonEnabled = value; OnPropertyChanged(nameof(IsStopButtonEnabled)); }
+ }
+
+ public bool IsDrumsEnabled { get => _enabledStatus["Drums"]; set => SetInstrumentEnabled("Drums", value); }
+ public bool IsBassEnabled { get => _enabledStatus["Bass"]; set => SetInstrumentEnabled("Bass", value); }
+ public bool IsOthersEnabled { get => _enabledStatus["Others"]; set => SetInstrumentEnabled("Others", value); }
+ public bool IsVocalsEnabled { get => _enabledStatus["Vocals"]; set => SetInstrumentEnabled("Vocals", value); }
+
+ public ObservableCollection