From 56902d4f8b19981d1b0c3b411a1c356050b3eb3b Mon Sep 17 00:00:00 2001 From: dexy Date: Fri, 6 Mar 2020 20:30:41 +1100 Subject: [PATCH] Refactored AudioPlayer code, added seek ability to AwcForm player --- CodeWalker/Forms/AwcForm.Designer.cs | 39 ++--- CodeWalker/Forms/AwcForm.cs | 234 ++++++++++----------------- CodeWalker/Utils/AudioUtils.cs | 223 +++++++++++++++++++++++++ 3 files changed, 330 insertions(+), 166 deletions(-) create mode 100644 CodeWalker/Utils/AudioUtils.cs diff --git a/CodeWalker/Forms/AwcForm.Designer.cs b/CodeWalker/Forms/AwcForm.Designer.cs index 50ef71f..3e565e5 100644 --- a/CodeWalker/Forms/AwcForm.Designer.cs +++ b/CodeWalker/Forms/AwcForm.Designer.cs @@ -51,10 +51,10 @@ this.PositionTrackBar = new System.Windows.Forms.TrackBar(); this.DetailsTabPage = new System.Windows.Forms.TabPage(); this.DetailsPropertyGrid = new CodeWalker.WinForms.PropertyGridFix(); - this.Timer = new System.Windows.Forms.Timer(this.components); - this.saveFileDialog = new System.Windows.Forms.SaveFileDialog(); this.XmlTabPage = new System.Windows.Forms.TabPage(); this.XmlTextBox = new FastColoredTextBoxNS.FastColoredTextBox(); + this.Timer = new System.Windows.Forms.Timer(this.components); + this.saveFileDialog = new System.Windows.Forms.SaveFileDialog(); this.MainTabControl.SuspendLayout(); this.PlayerTabPage.SuspendLayout(); this.contextMenuStrip.SuspendLayout(); @@ -135,7 +135,7 @@ this.VolumeLabel.AutoSize = true; this.VolumeLabel.Location = new System.Drawing.Point(538, 395); this.VolumeLabel.Name = "VolumeLabel"; - this.VolumeLabel.Size = new System.Drawing.Size(60, 13); + this.VolumeLabel.Size = new System.Drawing.Size(56, 13); this.VolumeLabel.TabIndex = 9; this.VolumeLabel.Text = "🕩 Volume"; // @@ -245,12 +245,13 @@ // VolumeTrackBar // this.VolumeTrackBar.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.VolumeTrackBar.AutoSize = false; this.VolumeTrackBar.BackColor = System.Drawing.SystemColors.ControlLightLight; this.VolumeTrackBar.LargeChange = 10; this.VolumeTrackBar.Location = new System.Drawing.Point(588, 391); this.VolumeTrackBar.Maximum = 100; this.VolumeTrackBar.Name = "VolumeTrackBar"; - this.VolumeTrackBar.Size = new System.Drawing.Size(105, 45); + this.VolumeTrackBar.Size = new System.Drawing.Size(105, 28); this.VolumeTrackBar.TabIndex = 6; this.VolumeTrackBar.TickStyle = System.Windows.Forms.TickStyle.None; this.VolumeTrackBar.Value = 50; @@ -260,15 +261,17 @@ // this.PositionTrackBar.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); + this.PositionTrackBar.AutoSize = false; this.PositionTrackBar.BackColor = System.Drawing.SystemColors.ControlLightLight; - this.PositionTrackBar.LargeChange = 1000; + this.PositionTrackBar.LargeChange = 0; this.PositionTrackBar.Location = new System.Drawing.Point(6, 353); this.PositionTrackBar.Maximum = 1000; this.PositionTrackBar.Name = "PositionTrackBar"; - this.PositionTrackBar.Size = new System.Drawing.Size(687, 45); + this.PositionTrackBar.Size = new System.Drawing.Size(687, 32); this.PositionTrackBar.TabIndex = 1; this.PositionTrackBar.TickStyle = System.Windows.Forms.TickStyle.None; this.PositionTrackBar.Scroll += new System.EventHandler(this.PositionTrackBar_Scroll); + this.PositionTrackBar.MouseUp += new System.Windows.Forms.MouseEventHandler(this.PositionTrackBar_MouseUp); // // DetailsTabPage // @@ -290,17 +293,6 @@ this.DetailsPropertyGrid.Size = new System.Drawing.Size(695, 416); this.DetailsPropertyGrid.TabIndex = 0; // - // Timer - // - this.Timer.Enabled = true; - this.Timer.Interval = 25; - this.Timer.Tick += new System.EventHandler(this.Timer_Tick); - // - // saveFileDialog - // - this.saveFileDialog.DefaultExt = "wav"; - this.saveFileDialog.Filter = "Wave files (*.wav)|*.wav"; - // // XmlTabPage // this.XmlTabPage.Controls.Add(this.XmlTextBox); @@ -327,7 +319,7 @@ this.XmlTextBox.AutoIndentChars = false; this.XmlTextBox.AutoIndentCharsPatterns = ""; this.XmlTextBox.AutoIndentExistingLines = false; - this.XmlTextBox.AutoScrollMinSize = new System.Drawing.Size(27, 14); + this.XmlTextBox.AutoScrollMinSize = new System.Drawing.Size(2, 14); this.XmlTextBox.BackBrush = null; this.XmlTextBox.CharHeight = 14; this.XmlTextBox.CharWidth = 8; @@ -353,6 +345,17 @@ this.XmlTextBox.TextChanged += new System.EventHandler(this.XmlTextBox_TextChanged); this.XmlTextBox.VisibleRangeChangedDelayed += new System.EventHandler(this.XmlTextBox_VisibleRangeChangedDelayed); // + // Timer + // + this.Timer.Enabled = true; + this.Timer.Interval = 25; + this.Timer.Tick += new System.EventHandler(this.Timer_Tick); + // + // saveFileDialog + // + this.saveFileDialog.DefaultExt = "wav"; + this.saveFileDialog.Filter = "Wave files (*.wav)|*.wav"; + // // AwcForm // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); diff --git a/CodeWalker/Forms/AwcForm.cs b/CodeWalker/Forms/AwcForm.cs index 1e8b681..498b3cb 100644 --- a/CodeWalker/Forms/AwcForm.cs +++ b/CodeWalker/Forms/AwcForm.cs @@ -1,7 +1,6 @@ using CodeWalker.GameFiles; +using CodeWalker.Utils; using FastColoredTextBoxNS; -using SharpDX.Multimedia; -using SharpDX.XAudio2; using System; using System.IO; using System.Diagnostics; @@ -16,12 +15,6 @@ namespace CodeWalker.Forms { public AwcFile Awc { get; set; } - private AwcStream currentAudio; - private XAudio2 xAudio2; - private MasteringVoice masteringVoice; - private AudioBuffer audioBuffer; - private SourceVoice sourceVoice; - private string fileName; public string FileName { @@ -34,23 +27,16 @@ namespace CodeWalker.Forms } public string FilePath { get; set; } - private enum PlayerState { Stopped, Playing, Paused }; - private PlayerState playerState = PlayerState.Stopped; - - private Stopwatch playtime; - private int playBeginMs; - private float trackLength; - private bool trackFinished; - private bool LoadingXml = false; private bool DelayHighlight = false; + private AudioPlayer Player = new AudioPlayer(); + + private bool PositionScrolled = false; public AwcForm() { InitializeComponent(); - - playtime = new Stopwatch(); } private void UpdateFormTitle() @@ -168,89 +154,12 @@ namespace CodeWalker.Forms { } } + private void Stop() { - if (playerState != PlayerState.Stopped) - { - sourceVoice.DestroyVoice(); - sourceVoice.Dispose(); - audioBuffer.Stream.Dispose(); - SetPlayerState(PlayerState.Stopped); - } - } - - private void SetPlayerState(PlayerState newState) - { - if (playerState != newState) - { - switch (newState) - { - case PlayerState.Playing: - if (playerState == PlayerState.Stopped) - playtime.Reset(); - playtime.Start(); - - PlayButton.Text = "\u275A\u275A"; - StopButton.Enabled = true; - LabelTime.Visible = true; - break; - case PlayerState.Paused: - playtime.Stop(); - PlayButton.Text = "\u25B6"; - StopButton.Enabled = true; - LabelTime.Visible = true; - break; - case PlayerState.Stopped: - playtime.Stop(); - PlayButton.Text = "\u25B6"; - LabelTime.Visible = false; - StopButton.Enabled = true; - break; - } - - playerState = newState; - UpdateUI(); - } - } - - private void InitializeAudio(AwcStream audio, float playBegin = 0) - { - currentAudio = audio; - trackLength = audio.Length; - - if (xAudio2 == null) - { - xAudio2 = new XAudio2(); - masteringVoice = new MasteringVoice(xAudio2); - } - - Stream wavStream = audio.GetWavStream(); - SoundStream soundStream = new SoundStream(wavStream); - audioBuffer = new AudioBuffer - { - Stream = soundStream.ToDataStream(), - AudioBytes = (int)soundStream.Length, - Flags = BufferFlags.EndOfStream - }; - if (playBegin > 0) - { - audioBuffer.PlayBegin = (int)(soundStream.Format.SampleRate * playBegin) / 128 * 128; - if (playtime.IsRunning) - playtime.Restart(); - else - playtime.Reset(); - playBeginMs = (int)(playBegin * 1000); - } - else - playBeginMs = 0; - soundStream.Close(); - wavStream.Close(); - - trackFinished = false; - sourceVoice = new SourceVoice(xAudio2, soundStream.Format, true); - sourceVoice.SubmitSourceBuffer(audioBuffer, soundStream.DecodedPacketsInfo); - sourceVoice.BufferEnd += (context) => trackFinished = true; - sourceVoice.SetVolume((float)VolumeTrackBar.Value / 100); + Player.Stop(); + UpdateUI(); + UpdatePlayerButtons(); } private void Play() @@ -264,15 +173,18 @@ namespace CodeWalker.Forms if ((audio?.FormatChunk != null) || (audio?.StreamFormat != null)) { - InitializeAudio(audio); - sourceVoice.Start(); - SetPlayerState(PlayerState.Playing); + Player.SetVolume(VolumeTrackBar.Value / 100.0f); + Player.LoadAudio(audio); + Player.Play(); } else if (audio.MidiChunk != null) { //todo: play MIDI? } } + + UpdateUI(); + UpdatePlayerButtons(); } private void PlayPrevious() @@ -307,41 +219,76 @@ namespace CodeWalker.Forms private void Pause() { - if (playerState == PlayerState.Playing) - { - sourceVoice.Stop(); - SetPlayerState(PlayerState.Paused); - } + Player.Pause(); + UpdatePlayerButtons(); } private void Resume() { - if (playerState == PlayerState.Paused) + Player.Resume(); + UpdatePlayerButtons(); + } + + private void UpdateUI() + { + if ((Player.State != AudioPlayer.PlayerState.Stopped) && Player.trackFinished) { - sourceVoice.Start(); - SetPlayerState(PlayerState.Playing); + if (chbAutoJump.Checked) + PlayNext(); + else + Stop(); + } + + if (Player.State != AudioPlayer.PlayerState.Stopped) + { + int playedMs = Player.PlayTimeMS; + int totalMs = Player.TotalTimeMS; + PositionTrackBar.Maximum = totalMs; + PositionTrackBar.Value = playedMs < totalMs ? playedMs : totalMs; + + LabelTime.Text = TimeSpan.FromSeconds(playedMs / 1000).ToString("m\\:ss") + + " / " + TimeSpan.FromSeconds(totalMs / 1000).ToString("m\\:ss"); + } + else + { + PositionTrackBar.Value = 0; } } - private void PositionTrackBar_Scroll(object sender, EventArgs e) + private void UpdatePlayerButtons() { - - //sourceVoice.Stop(); - //InitializeAudio(currentAudio, PositionTrackBar.Value / 1000); - //sourceVoice.Start(); + switch (Player.State) + { + case AudioPlayer.PlayerState.Playing: + PlayButton.Text = "\u275A\u275A"; + StopButton.Enabled = true; + LabelTime.Visible = true; + break; + case AudioPlayer.PlayerState.Paused: + PlayButton.Text = "\u25B6"; + StopButton.Enabled = true; + LabelTime.Visible = true; + break; + case AudioPlayer.PlayerState.Stopped: + PlayButton.Text = "\u25B6"; + StopButton.Enabled = true; + LabelTime.Visible = false; + break; + } } + private void PlayButton_Click(object sender, EventArgs e) { - switch (playerState) + switch (Player.State) { - case PlayerState.Stopped: + case AudioPlayer.PlayerState.Stopped: Play(); break; - case PlayerState.Playing: + case AudioPlayer.PlayerState.Playing: Pause(); break; - case PlayerState.Paused: + case AudioPlayer.PlayerState.Paused: Resume(); break; } @@ -362,36 +309,33 @@ namespace CodeWalker.Forms PlayNext(); } - private void VolumeTrackBar_Scroll(object sender, EventArgs e) + private void PositionTrackBar_Scroll(object sender, EventArgs e) { - if (playerState == PlayerState.Playing) - sourceVoice.SetVolume((float)VolumeTrackBar.Value / 100); + PositionScrolled = true; + + var t = PositionTrackBar.Value / 1000.0f; + + Player.Seek(t); } - private void UpdateUI() + private void PositionTrackBar_MouseUp(object sender, MouseEventArgs e) { - if (playerState != PlayerState.Stopped && trackFinished) + if (PositionScrolled) { - if (chbAutoJump.Checked) - PlayNext(); - else - Stop(); + PositionScrolled = false; + return; } + PositionScrolled = false; - if (playerState != PlayerState.Stopped) - { - int playedMs = (int)playtime.Elapsed.TotalMilliseconds + playBeginMs; - int totalMs = (int)(trackLength * 1000); - PositionTrackBar.Maximum = totalMs; - PositionTrackBar.Value = playedMs < totalMs ? playedMs : totalMs; + var f = Math.Min(Math.Max((e.X-13.0f) / (PositionTrackBar.Width-26.0f), 0.0f), 1.0f); + var v = f * (PositionTrackBar.Maximum / 1000.0f); - LabelTime.Text = TimeSpan.FromSeconds(playedMs / 1000).ToString("m\\:ss") - + " / " + TimeSpan.FromSeconds(totalMs / 1000).ToString("m\\:ss"); - } - else - { - PositionTrackBar.Value = 0; - } + Player.Seek(v); + } + + private void VolumeTrackBar_Scroll(object sender, EventArgs e) + { + Player.SetVolume((float)VolumeTrackBar.Value / 100); } private void Timer_Tick(object sender, EventArgs e) @@ -401,19 +345,13 @@ namespace CodeWalker.Forms private void PlayListView_DoubleClick(object sender, EventArgs e) { - if (playerState == PlayerState.Playing) - Stop(); Play(); } private void AwcForm_FormClosing(object sender, FormClosingEventArgs e) { Stop(); - if (xAudio2 != null) - { - masteringVoice.Dispose(); - xAudio2.Dispose(); - } + Player.DisposeAudio(); } private void ExportAsWav_Click(object sender, EventArgs e) diff --git a/CodeWalker/Utils/AudioUtils.cs b/CodeWalker/Utils/AudioUtils.cs new file mode 100644 index 0000000..a572e2f --- /dev/null +++ b/CodeWalker/Utils/AudioUtils.cs @@ -0,0 +1,223 @@ +using CodeWalker.GameFiles; +using SharpDX.Multimedia; +using SharpDX.XAudio2; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CodeWalker.Utils +{ + + + + public class AudioPlayer + { + + private AwcStream currentAudio; + private Stream wavStream; + private SoundStream soundStream; + private XAudio2 xAudio2; + private MasteringVoice masteringVoice; + private AudioBuffer audioBuffer; + private SourceVoice sourceVoice; + + public enum PlayerState { Stopped, Playing, Paused }; + public PlayerState State { get; private set; } = PlayerState.Stopped; + + private Stopwatch playtimer = new Stopwatch(); + private int playBeginMs; + private float trackLength; + public bool trackFinished; + + private float volume = 1.0f; + + + public int PlayTimeMS + { + get + { + return (int)playtimer.Elapsed.TotalMilliseconds + playBeginMs; + } + } + public int TotalTimeMS + { + get + { + return (int)(trackLength * 1000); + } + } + + + public void LoadAudio(AwcStream audio) + { + if (audio != currentAudio) + { + CloseStreams(); + + currentAudio = audio; + trackLength = audio.Length; + wavStream = audio.GetWavStream(); + soundStream = new SoundStream(wavStream); + } + } + + private void CloseStreams() + { + if (soundStream != null) + { + soundStream.Close(); + } + if (wavStream != null) + { + wavStream.Close();//is this necessary? + } + } + + private void InitializeAudio(float playBegin = 0) + { + if (xAudio2 == null) + { + xAudio2 = new XAudio2(); + masteringVoice = new MasteringVoice(xAudio2); + } + + wavStream.Position = 0; + soundStream.Position = 0; + audioBuffer = new AudioBuffer + { + Stream = soundStream.ToDataStream(), + AudioBytes = (int)soundStream.Length, + Flags = BufferFlags.EndOfStream + }; + if (playBegin > 0) + { + audioBuffer.PlayBegin = (int)(soundStream.Format.SampleRate * playBegin) / 128 * 128; + if (playtimer.IsRunning) + { + playtimer.Restart(); + } + else + { + playtimer.Reset(); + } + playBeginMs = (int)(playBegin * 1000); + } + else + { + playBeginMs = 0; + } + + trackFinished = false; + sourceVoice = new SourceVoice(xAudio2, soundStream.Format, true); + sourceVoice.SubmitSourceBuffer(audioBuffer, soundStream.DecodedPacketsInfo); + sourceVoice.BufferEnd += (context) => trackFinished = true; + sourceVoice.SetVolume(volume); + } + + private void SetPlayerState(PlayerState newState) + { + if (State != newState) + { + switch (newState) + { + case PlayerState.Playing: + if (State == PlayerState.Stopped) + { + playtimer.Reset(); + } + playtimer.Start(); + break; + case PlayerState.Paused: + playtimer.Stop(); + break; + case PlayerState.Stopped: + playtimer.Stop(); + break; + } + + State = newState; + } + } + + public void SetVolume(float v) + { + volume = v; + if (State == PlayerState.Playing) + { + sourceVoice.SetVolume(v); + } + } + + public void DisposeAudio() + { + CloseStreams(); + if (xAudio2 != null) + { + masteringVoice.Dispose(); + xAudio2.Dispose(); + } + } + + + public void Stop() + { + if (State != PlayerState.Stopped) + { + sourceVoice.DestroyVoice(); + sourceVoice.Dispose(); + audioBuffer.Stream.Dispose(); + SetPlayerState(PlayerState.Stopped); + } + } + + public void Play(float playBegin = 0) + { + Stop(); + InitializeAudio(playBegin); + sourceVoice.Start(); + SetPlayerState(PlayerState.Playing); + } + + public void Seek(float playBegin = 0) + { + if (State == PlayerState.Playing) + { + Play(playBegin); + } + else if (State == PlayerState.Paused) + { + var state = State; + Stop(); + InitializeAudio(playBegin); + State = state; + } + } + + public void Pause() + { + if (State == PlayerState.Playing) + { + sourceVoice.Stop(); + SetPlayerState(PlayerState.Paused); + } + } + + public void Resume() + { + if (State == PlayerState.Paused) + { + sourceVoice.Start(); + SetPlayerState(PlayerState.Playing); + } + } + + + } + + + +}