Refactored AudioPlayer code, added seek ability to AwcForm player

This commit is contained in:
dexy 2020-03-06 20:30:41 +11:00
parent 93784bbc49
commit 56902d4f8b
3 changed files with 330 additions and 166 deletions

View File

@ -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<FastColoredTextBoxNS.TextChangedEventArgs>(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);

View File

@ -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)

View File

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