using NAudio.Wave; using System; using System.Windows.Forms; using YoutubeExplode; using YoutubeExplode.Videos; using YoutubeExplode.Videos.Streams; namespace Projector { public partial class FrmMain : Form { private Video? _video = null; private StreamManifest? _streamManifest = null; FourChannelToStereoSampleProvider? mixer = null; string musicPath = "C:\\Hymnal\\Music"; string editsPath = "C:\\Hymnal\\Edits"; string LyricsPath = "C:\\Hymnal\\Lyrics"; FrmPreview frmPreview = new FrmPreview(); public FrmMain() { InitializeComponent(); DgvHymns.CellContentClick += DgvHymns_CellContentClick; } private void FrmMain_Load(object sender, EventArgs e) { FrmMain_Resize(null, null); if (!System.IO.Directory.Exists(musicPath)) { MessageBox.Show("Hymns directory does not exist: " + musicPath); return; } var hymns = System.IO.Directory.GetFiles(musicPath, "*.m4a"); foreach (var hymn in hymns) { var fileName = System.IO.Path.GetFileNameWithoutExtension(hymn); DgvHymns.Rows.Add(fileName, ""); } } private void DgvHymns_CellPainting(object sender, DataGridViewCellPaintingEventArgs e) { if (DgvHymns.Columns[e.ColumnIndex].Name == "CnPlay" && e.RowIndex >= 0) { e.Paint(e.CellBounds, DataGridViewPaintParts.All); var icon = Properties.Resources.Play; // Assuming you have a play icon in resources var iconRect = new Rectangle(e.CellBounds.X + 2, e.CellBounds.Y + 2, e.CellBounds.Height - 4, e.CellBounds.Height - 4); e.Graphics?.DrawImage(icon, iconRect); e.Handled = true; // Prevent default painting } } // 3. Handle the Play button click: private void DgvHymns_CellContentClick(object sender, DataGridViewCellEventArgs e) { // Check if PlayButton column and not header row if (e.RowIndex >= 0 && DgvHymns.Columns[e.ColumnIndex].Name == "CnPlay") { var hymnName = DgvHymns.Rows[e.RowIndex].Cells["CnHymn"].Value?.ToString(); if (!string.IsNullOrEmpty(hymnName)) { PlayHymn(hymnName); } } } private void PlayHymn(string name) { string audioFilePath = Path.Combine(editsPath, name + ".m4a"); if (!File.Exists(audioFilePath)) { audioFilePath = Path.Combine(musicPath, name + ".m4a"); } string number = name.Substring(0, 3).Trim(); // Extract the first 3 characters try { // Open the audio file var reader = new MediaFoundationReader(audioFilePath); // Check for 4 channels if (reader.WaveFormat.Channels != 4) { MessageBox.Show("Audio file does not have 4 channels."); reader.Dispose(); return; } // Create a custom sample provider to mix channels 0+2 to left, 1+3 to right mixer = new FourChannelToStereoSampleProvider(reader.ToSampleProvider()); // Search the folder in LyricsPath that starts with the hymn number string lyricsFilePath = Directory.GetDirectories(LyricsPath).ToList().ConvertAll(d => Path.GetFileName(d)) .FirstOrDefault(dir => dir.StartsWith(number, StringComparison.OrdinalIgnoreCase)); // Get all the jpg files in the lyrics folder if (lyricsFilePath != null) { string lyricsFolderPath = Path.Combine(LyricsPath, lyricsFilePath); var jpgFiles = Directory.GetFiles(lyricsFolderPath, "*.jpg"); // Order the jpg files by name Array.Sort(jpgFiles, StringComparer.OrdinalIgnoreCase); // Display the first jpg file in a PictureBox or similar control if (jpgFiles.Length > 0) { // Assuming you have a PictureBox named pictureBoxLyrics frmPreview.SetImages(jpgFiles, mixer); } } var waveOut = new WaveOutEvent(); waveOut.Init(mixer); waveOut.Play(); frmPreview.ShowDialog(this); TbFilter.SelectAll(); TbFilter.Focus(); // Crate a task to handle the transition Task.Run(() => { mixer.SetOff(); while (!mixer.IsOff) { Thread.Sleep(100); // Check every 100ms } waveOut.Stop(); }); // Optionally, store waveOut and reader as fields to stop/dispose later } catch (Exception ex) { MessageBox.Show("Error playing audio: " + ex.Message); } } private void TbFilter_TextChanged(object sender, EventArgs e) { // Filter the DataGridView based on the text in the filter TextBox string filterText = TbFilter.Text.ToLower(); foreach (DataGridViewRow row in DgvHymns.Rows) { if (row.Cells["CnHymn"].Value != null) { row.Visible = row.Cells["CnHymn"].Value.ToString().ToLower().Contains(filterText); } } var visibleRows = DgvHymns.Rows .Cast() .Where(row => row.Visible) .ToList(); if (visibleRows.Count > 0) { int firstVisibleRowIndex = visibleRows[0].Index; DgvHymns.Rows[firstVisibleRowIndex].Selected = true; DgvHymns.FirstDisplayedScrollingRowIndex = firstVisibleRowIndex; } } private void FrmMain_Resize(object sender, EventArgs e) { DgvHymns.Columns["CnHymn"].Width = Width - 80; } protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { if (keyData == Keys.Down) { var visibleRows = DgvHymns.Rows .Cast() .Where(row => row.Visible) .ToList(); if (visibleRows.Count > 0) { int selectedRowIndex = visibleRows.FindIndex(row => row.Selected); selectedRowIndex++; if (selectedRowIndex < visibleRows.Count) { visibleRows[selectedRowIndex].Selected = true; int firstDisplayed = DgvHymns.FirstDisplayedScrollingRowIndex; int displayedCount = DgvHymns.DisplayedRowCount(false); // false = only fully visible rows int lastDisplayed = firstDisplayed + displayedCount - 1; if (selectedRowIndex >= lastDisplayed) { DgvHymns.FirstDisplayedScrollingRowIndex = visibleRows[selectedRowIndex - displayedCount + 1].Index; } } } return true; } else if (keyData == Keys.Up) { var visibleRows = DgvHymns.Rows .Cast() .Where(row => row.Visible) .ToList(); if (visibleRows.Count > 0) { int selectedRowIndex = visibleRows.FindIndex(row => row.Selected); selectedRowIndex--; if (selectedRowIndex >= 0) { visibleRows[selectedRowIndex].Selected = true; int firstDisplayed = DgvHymns.FirstDisplayedScrollingRowIndex; int displayedCount = DgvHymns.DisplayedRowCount(false); // false = only fully visible rows if (selectedRowIndex < firstDisplayed) { DgvHymns.FirstDisplayedScrollingRowIndex = visibleRows[selectedRowIndex].Index; } } } return true; } else if (keyData == Keys.Enter) { // Handle Enter key to simulate Play button click var selectedRow = DgvHymns.SelectedRows.Cast().FirstOrDefault(); if (selectedRow != null && selectedRow.Cells["CnPlay"].Value != null) { DgvHymns_CellContentClick(sender: null, e: new DataGridViewCellEventArgs(1, selectedRow.Index)); return true; // Indicate that the key press has been handled } } else if (keyData == Keys.Home) { var visibleRows = DgvHymns.Rows .Cast() .Where(row => row.Visible) .ToList(); if (visibleRows.Count > 0) { visibleRows.First().Selected = true; int selectedRowIndex = visibleRows.First().Index; int firstDisplayed = DgvHymns.FirstDisplayedScrollingRowIndex; int displayedCount = DgvHymns.DisplayedRowCount(false); // false = only fully visible rows if (selectedRowIndex < firstDisplayed) { DgvHymns.FirstDisplayedScrollingRowIndex = visibleRows[selectedRowIndex].Index; } } return true; // Indicate that the key press has been handled } else if (keyData == Keys.End) { var visibleRows = DgvHymns.Rows .Cast() .Where(row => row.Visible) .ToList(); if (visibleRows.Count > 0) { visibleRows.Last().Selected = true; int selectedRowIndex = visibleRows.Last().Index; int firstDisplayed = DgvHymns.FirstDisplayedScrollingRowIndex; int displayedCount = DgvHymns.DisplayedRowCount(false); // false = only fully visible rows int lastDisplayed = firstDisplayed + displayedCount - 1; if (selectedRowIndex >= lastDisplayed) { DgvHymns.FirstDisplayedScrollingRowIndex = visibleRows[selectedRowIndex - displayedCount + 1].Index; } } return true; // Indicate that the key press has been handled } return base.ProcessCmdKey(ref msg, keyData); // Allow default processing if not an arrow key } } }