diff --git a/RhythmBullet/RhythmBullet.csproj b/RhythmBullet/RhythmBullet.csproj
index 54065a1..1c078e2 100644
--- a/RhythmBullet/RhythmBullet.csproj
+++ b/RhythmBullet/RhythmBullet.csproj
@@ -51,6 +51,7 @@
+
@@ -151,9 +152,7 @@
-
-
-
+
diff --git a/RhythmBullet/Zer01HD/Audio/MusicController.cs b/RhythmBullet/Zer01HD/Audio/MusicController.cs
index 4e3252b..982b270 100644
--- a/RhythmBullet/Zer01HD/Audio/MusicController.cs
+++ b/RhythmBullet/Zer01HD/Audio/MusicController.cs
@@ -9,7 +9,7 @@ using System.Threading.Tasks;
namespace RhythmBullet.Zer01HD.Audio
{
- internal class MusicController
+ internal class MusicController : IDisposable
{
MusicList musicList;
WaveOutEvent outputDevice;
@@ -105,5 +105,10 @@ namespace RhythmBullet.Zer01HD.Audio
return audioInput.CurrentTime;
}
+ public void Dispose()
+ {
+ audioInput?.Dispose();
+ outputDevice?.Dispose();
+ }
}
}
diff --git a/RhythmBullet/Zer01HD/Audio/TransparentSampleProvider.cs b/RhythmBullet/Zer01HD/Audio/TransparentSampleProvider.cs
index 19606b6..7e530c5 100644
--- a/RhythmBullet/Zer01HD/Audio/TransparentSampleProvider.cs
+++ b/RhythmBullet/Zer01HD/Audio/TransparentSampleProvider.cs
@@ -12,10 +12,11 @@ namespace RhythmBullet.Zer01HD.Audio
public class TransparentSampleProvider : ISampleProvider
{
private const int FFT_SIZE = 2048;
- private readonly int m;
+ private readonly int m;
private ISampleProvider source;
private Complex[] fftBuffer;
- private float[] spectrum;
+ private volatile bool updatedSpectrum;
+ private readonly float[] currentSpectrum;
private int channelCount;
public bool performFFT;
@@ -27,7 +28,7 @@ namespace RhythmBullet.Zer01HD.Audio
source = sampleProvider;
channelCount = sampleProvider.WaveFormat.Channels;
fftBuffer = new Complex[FFT_SIZE];
- spectrum = new float[FFT_SIZE];
+ currentSpectrum = new float[FFT_SIZE];
}
public WaveFormat WaveFormat
@@ -38,12 +39,20 @@ namespace RhythmBullet.Zer01HD.Audio
}
}
- public float GetSpectrumBin(int bin)
+ public float[] GetCurrentSpectrum()
{
- lock (spectrum)
+ if (updatedSpectrum)
{
- return spectrum[bin];
+ lock (fftBuffer)
+ {
+ for (int binID = 0; binID < currentSpectrum.Length; binID++)
+ {
+ currentSpectrum[binID] = fftBuffer[binID].X;
+ }
+ }
}
+
+ return currentSpectrum;
}
public int Read(float[] buffer, int offset, int count)
@@ -57,14 +66,11 @@ namespace RhythmBullet.Zer01HD.Audio
{
if (s >= FFT_SIZE)
{
- FastFourierTransform.FFT(true, m, fftBuffer);
- lock (spectrum)
+ lock (fftBuffer)
{
- for (int binID = 0; binID < spectrum.Length; binID++)
- {
- spectrum[binID] = fftBuffer[binID].X;
- }
+ FastFourierTransform.FFT(true, m, fftBuffer);
}
+ updatedSpectrum = true;
fftOffset -= FFT_SIZE;
}
float greatestVal = 0;
diff --git a/RhythmBullet/Zer01HD/Audio/Visualizer/HorizontalVisualizer.cs b/RhythmBullet/Zer01HD/Audio/Visualizer/HorizontalVisualizer.cs
new file mode 100644
index 0000000..0e786b3
--- /dev/null
+++ b/RhythmBullet/Zer01HD/Audio/Visualizer/HorizontalVisualizer.cs
@@ -0,0 +1,109 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using RhythmBullet.Zer01HD.UI.Modular;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace RhythmBullet.Zer01HD.Audio.Visualizer
+{
+ internal class HorizontalVisualizer : UIModule, IDisposable
+ {
+ GraphicsDevice graphicsDevice;
+ Texture2D barTexture;
+ private const int BAR_COUNT = 70;
+ private const int SMOOTH_RANGE = 3;
+ private readonly int binsPerBar;
+ private readonly int spaceBetweenBars;
+ private Rectangle bar;
+ private TransparentSampleProvider tsp;
+ private int[] barValue;
+
+ internal HorizontalVisualizer(TransparentSampleProvider transparentSampleProvider, GraphicsDevice graphicsDevice)
+ {
+ this.graphicsDevice = graphicsDevice;
+ tsp = transparentSampleProvider;
+ bar.Width = (int)(graphicsDevice.Viewport.Width / 70f);
+ spaceBetweenBars = (int)(0.25f * bar.Width);
+ bar.Width -= spaceBetweenBars;
+
+ barTexture = new Texture2D(graphicsDevice, 1, 1);
+ barTexture.SetData(new[] { Color.White });
+ binsPerBar = tsp.GetCurrentSpectrum().Length / BAR_COUNT;
+ barValue = new int[BAR_COUNT];
+ }
+
+ public override void Update(GameTime gameTime)
+ {
+ UpdateBars((float)gameTime.ElapsedGameTime.TotalSeconds);
+ AverageBars();
+ base.Update(gameTime);
+ }
+
+ public override void Draw(SpriteBatch batch)
+ {
+ for (int i = 0; i < BAR_COUNT; i++)
+ {
+ bar.X = (i * (bar.Width + spaceBetweenBars)) + Bounds.X;
+ bar.Y = Bounds.Y;
+
+ bar.Height = barValue[i];
+ batch.Draw(barTexture, bar, Color);
+
+ bar.Height = -barValue[BAR_COUNT - i - 1];
+ batch.Draw(barTexture, bar, Color);
+ }
+ base.Draw(batch);
+ }
+
+ private void UpdateBars(float delta)
+ {
+ const float ALPHA = 0.5f;
+ float[] spectrum = tsp.GetCurrentSpectrum();
+
+ for (int barID = 0; barID < BAR_COUNT; barID++)
+ {
+ int targetBarHeight = 0;
+
+ for (int bin = barID * binsPerBar; bin < (barID + 1) * (binsPerBar); bin++)
+ {
+ targetBarHeight += (int)spectrum[bin];
+ }
+ targetBarHeight /= binsPerBar;
+
+ int distance = targetBarHeight - barValue[barID];
+ distance *= (int)Math.Round((1.0f - Math.Pow(1 - ALPHA, delta / 0.02f)));
+ barValue[barID] += distance;
+ }
+ }
+
+ private void AverageBars()
+ {
+ for (int barID = 0; barID < BAR_COUNT; barID++)
+ {
+ int terms = 0;
+ for (int pos = 0; pos < SMOOTH_RANGE; pos++)
+ {
+ if (barID + pos < BAR_COUNT)
+ {
+ barValue[barID] += barValue[barID + pos];
+ terms++;
+ }
+ if (barID - pos > 0)
+ {
+ barValue[barID] += barValue[barID - pos];
+ terms++;
+ }
+ }
+ barValue[barID] /= terms;
+ }
+ }
+
+ public void Dispose()
+ {
+ barTexture.Dispose();
+ }
+ }
+}
diff --git a/RhythmBullet/Zer01HD/Utilities/Camera/Camera2D.cs b/RhythmBullet/Zer01HD/Utilities/Camera/Camera2D.cs
index 4c302d3..efe9039 100644
--- a/RhythmBullet/Zer01HD/Utilities/Camera/Camera2D.cs
+++ b/RhythmBullet/Zer01HD/Utilities/Camera/Camera2D.cs
@@ -32,11 +32,14 @@ namespace RhythmBullet.Zer01HD.Utilities.Camera
Matrix.CreateTranslation(new Vector3(bounds.Width * 0.5f, bounds.Height * 0.5f, 0f));
}
- public void LinearInterpolationToPosition(float alpha, Vector2 targetPosition)
+ public void LinearInterpolationToPosition(float alpha, Vector2 targetPosition, float delta)
{
- if (alpha < 0 && alpha > 1f) throw new ArgumentException("Alpha can't be greater than 1f, or less than 0.");
- Position.X = MathHelper.Lerp(Position.X, targetPosition.X, alpha);
- Position.Y = MathHelper.Lerp(Position.Y, targetPosition.Y, alpha);
+ if (alpha <= 0 && alpha > 1f) throw new ArgumentException("Alpha can't be greater than 1f, less than or equal to 0.");
+
+ Vector2 distance = targetPosition - Position;
+ distance *= (float)(1.0f - Math.Pow(1 - alpha, delta / 0.02f));
+
+ Position += distance;
}
}
}
diff --git a/RhythmBullet/Zer01HD/Utilities/UI/Book/Book.cs b/RhythmBullet/Zer01HD/Utilities/UI/Book/Book.cs
index 1e7730c..b6b87fa 100644
--- a/RhythmBullet/Zer01HD/Utilities/UI/Book/Book.cs
+++ b/RhythmBullet/Zer01HD/Utilities/UI/Book/Book.cs
@@ -45,7 +45,7 @@ namespace RhythmBullet.Zer01HD.UI.Book
Rectangle targetBounds = targetPage.Bounds;
position.X = targetBounds.X + (targetBounds.Width * 0.5f);
position.Y = targetBounds.Y + (targetBounds.Height * 0.5f);
- camera.LinearInterpolationToPosition(0.4f, position);
+ camera.LinearInterpolationToPosition(0.4f, position, (float)gameTime.ElapsedGameTime.TotalSeconds);
if (camera.Position == position)
{
targetPage = null;