untested visualizer complete.

This commit is contained in:
Harrison Deng 2018-11-19 21:07:23 -06:00
parent 02d814b4f7
commit 57e76be95d
6 changed files with 143 additions and 21 deletions

View File

@ -51,6 +51,7 @@
<Compile Include="Zer01HD\Audio\MusicList.cs" /> <Compile Include="Zer01HD\Audio\MusicList.cs" />
<Compile Include="Zer01HD\Audio\SupportedFormats.cs" /> <Compile Include="Zer01HD\Audio\SupportedFormats.cs" />
<Compile Include="Zer01HD\Audio\TransparentSampleProvider.cs" /> <Compile Include="Zer01HD\Audio\TransparentSampleProvider.cs" />
<Compile Include="Zer01HD\Audio\Visualizer\HorizontalVisualizer.cs" />
<Compile Include="Zer01HD\Screens\MainMenu\MainPage.cs" /> <Compile Include="Zer01HD\Screens\MainMenu\MainPage.cs" />
<Compile Include="Zer01HD\Screens\Transitions\FadeAwayTransition.cs" /> <Compile Include="Zer01HD\Screens\Transitions\FadeAwayTransition.cs" />
<Compile Include="Zer01HD\Utilities\Camera\Camera2D.cs" /> <Compile Include="Zer01HD\Utilities\Camera\Camera2D.cs" />
@ -151,9 +152,7 @@
<None Include="app.manifest" /> <None Include="app.manifest" />
<None Include="packages.config" /> <None Include="packages.config" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup />
<Folder Include="Zer01HD\Audio\Visualizer\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildExtensionsPath)\MonoGame\v3.0\MonoGame.Content.Builder.targets" /> <Import Project="$(MSBuildExtensionsPath)\MonoGame\v3.0\MonoGame.Content.Builder.targets" />
<Import Project="..\packages\BulletSharp.0.11.1\build\net40-client\BulletSharp.targets" Condition="Exists('..\packages\BulletSharp.0.11.1\build\net40-client\BulletSharp.targets')" /> <Import Project="..\packages\BulletSharp.0.11.1\build\net40-client\BulletSharp.targets" Condition="Exists('..\packages\BulletSharp.0.11.1\build\net40-client\BulletSharp.targets')" />

View File

@ -9,7 +9,7 @@ using System.Threading.Tasks;
namespace RhythmBullet.Zer01HD.Audio namespace RhythmBullet.Zer01HD.Audio
{ {
internal class MusicController internal class MusicController : IDisposable
{ {
MusicList musicList; MusicList musicList;
WaveOutEvent outputDevice; WaveOutEvent outputDevice;
@ -105,5 +105,10 @@ namespace RhythmBullet.Zer01HD.Audio
return audioInput.CurrentTime; return audioInput.CurrentTime;
} }
public void Dispose()
{
audioInput?.Dispose();
outputDevice?.Dispose();
}
} }
} }

View File

@ -15,7 +15,8 @@ namespace RhythmBullet.Zer01HD.Audio
private readonly int m; private readonly int m;
private ISampleProvider source; private ISampleProvider source;
private Complex[] fftBuffer; private Complex[] fftBuffer;
private float[] spectrum; private volatile bool updatedSpectrum;
private readonly float[] currentSpectrum;
private int channelCount; private int channelCount;
public bool performFFT; public bool performFFT;
@ -27,7 +28,7 @@ namespace RhythmBullet.Zer01HD.Audio
source = sampleProvider; source = sampleProvider;
channelCount = sampleProvider.WaveFormat.Channels; channelCount = sampleProvider.WaveFormat.Channels;
fftBuffer = new Complex[FFT_SIZE]; fftBuffer = new Complex[FFT_SIZE];
spectrum = new float[FFT_SIZE]; currentSpectrum = new float[FFT_SIZE];
} }
public WaveFormat WaveFormat 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) public int Read(float[] buffer, int offset, int count)
@ -57,14 +66,11 @@ namespace RhythmBullet.Zer01HD.Audio
{ {
if (s >= FFT_SIZE) if (s >= FFT_SIZE)
{ {
FastFourierTransform.FFT(true, m, fftBuffer); lock (fftBuffer)
lock (spectrum)
{ {
for (int binID = 0; binID < spectrum.Length; binID++) FastFourierTransform.FFT(true, m, fftBuffer);
{
spectrum[binID] = fftBuffer[binID].X;
}
} }
updatedSpectrum = true;
fftOffset -= FFT_SIZE; fftOffset -= FFT_SIZE;
} }
float greatestVal = 0; float greatestVal = 0;

View File

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

View File

@ -32,11 +32,14 @@ namespace RhythmBullet.Zer01HD.Utilities.Camera
Matrix.CreateTranslation(new Vector3(bounds.Width * 0.5f, bounds.Height * 0.5f, 0f)); 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."); if (alpha <= 0 && alpha > 1f) throw new ArgumentException("Alpha can't be greater than 1f, less than or equal to 0.");
Position.X = MathHelper.Lerp(Position.X, targetPosition.X, alpha);
Position.Y = MathHelper.Lerp(Position.Y, targetPosition.Y, alpha); Vector2 distance = targetPosition - Position;
distance *= (float)(1.0f - Math.Pow(1 - alpha, delta / 0.02f));
Position += distance;
} }
} }
} }

View File

@ -45,7 +45,7 @@ namespace RhythmBullet.Zer01HD.UI.Book
Rectangle targetBounds = targetPage.Bounds; Rectangle targetBounds = targetPage.Bounds;
position.X = targetBounds.X + (targetBounds.Width * 0.5f); position.X = targetBounds.X + (targetBounds.Width * 0.5f);
position.Y = targetBounds.Y + (targetBounds.Height * 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) if (camera.Position == position)
{ {
targetPage = null; targetPage = null;