<commit_before>﻿using DequeNet;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ListenMoeClient
{
	class Visualiser
	{
		DateTime anchor;
		Deque<short> sampleBuffer = new Deque<short>();

		float[] lastFftPoints;
		bool logarithmic = false;
		float bias = 0.3f;
		float ScaleFactor = 1f;
		float normalisationFactor = 1.0f;

		int resolutionFactor = Settings.Get<int>("VisualiserResolutionFactor"); //higher = lower resolution, number is the number of samples to skip
		float barWidth = Settings.Get<float>("VisualiserBarWidth");

		bool bars = true;

		bool stopped = false;

		public Rectangle Bounds;

		int fftSize = 2048;

		public void AddSamples(short[] samples)
		{
			if (stopped)
				return;

			if (anchor == DateTime.MinValue)
				anchor = DateTime.Now;

			foreach (var s in samples)
				sampleBuffer.PushRight(s);

			if (sampleBuffer.Count > fftSize * 4)
			{
				DateTime now = DateTime.Now;
				int currentPos = (int)(Globals.SAMPLE_RATE * ((now - anchor).TotalMilliseconds / 1000)) * 2;
				anchor = now;
				for (int i = 0; i < currentPos && sampleBuffer.Count > 0; i++)
					sampleBuffer.PopLeft();
			}
		}

		public void ClearBuffers()
		{
			anchor = DateTime.MinValue;
			sampleBuffer.Clear();
		}

		public void Stop()
		{
			stopped = true;
			sampleBuffer.Clear();
			anchor = DateTime.MinValue;
		}

		public void Start()
		{
			stopped = false;
		}

		public void IncreaseBarWidth(float amount)
		{
			barWidth += amount;
			Settings.Set("VisualiserBarWidth", barWidth);
			Settings.WriteSettings();
		}

		public void IncreaseResolution(int amount)
		{
			if (resolutionFactor - amount > 0)
			{
				resolutionFactor -= amount;
				Settings.Set("VisualiserResolutionFactor", resolutionFactor);
				Settings.WriteSettings();
			}
		}

		private float[] CalculateNextFftFrame()
		{
			if (sampleBuffer == null)
				return null;

			int currentPos = (int)(Globals.SAMPLE_RATE * ((DateTime.Now - anchor).TotalMilliseconds / 1000)) * 2;
			if (sampleBuffer.Count - currentPos < fftSize || currentPos < 0)
				return null;

			short[] window = new short[fftSize];
			for (int i = 0; i < fftSize; i++)
				window[i] = sampleBuffer[currentPos + i];
			
			applyWindowFunction(window);
			float[] bins = FFT.Fft(window);
			bins = bins.Take(bins.Length / 4).ToArray();
			bins = bins.Select(f => (float)Math.Log10(f * 10) * 2 + 1).Select(f => ((f - 0.3f) * 1.5f) + 1.5f).ToArray();
			if (lastFftPoints == null)
				lastFftPoints = bins;
			return bins;
		}
		
		private void applyWindowFunction(short[] data)
		{
			for (int i = 0; i < data.Length; i++)
			{
				data[i] = (short)(data[i] * 0.5 * (1 - Math.Cos(2 * Math.PI * i / data.Length)));
			}
		}

		public void Render(Graphics g)
		{
			float[] fftPoints;
			if (stopped)
			{
				fftPoints = new float[lastFftPoints.Length];
			}
			else
			{
				fftPoints = CalculateNextFftFrame();
				if (fftPoints == null)
				{
					if (lastFftPoints == null)
						return;
					fftPoints = lastFftPoints;
				}
			}

			//Process points
			int noPoints = fftPoints.Length / resolutionFactor;
			PointF[] points = new PointF[noPoints];

			for (int i = 1; i < fftPoints.Length; i++)
				fftPoints[i] = fftPoints[i] * bias + lastFftPoints[i] * (1 - bias);

			int j = 0;
			if (logarithmic)
			{
				float binWidth = (Globals.SAMPLE_RATE / 2) / fftPoints.Length;
				float maxNote = (float)(12 * Math.Log(((fftPoints.Length - 1) * binWidth) / 16.35, 10));<commit_msg>Fixed NullPointerException when pausing before music begins<commit_after>﻿using DequeNet;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ListenMoeClient
{
	class Visualiser
	{
		DateTime anchor;
		Deque<short> sampleBuffer = new Deque<short>();

		const int fftSize = 2048;

		float[] lastFftPoints = new float[fftSize];
		bool logarithmic = false;
		float bias = 0.3f;
		float ScaleFactor = 1f;
		float normalisationFactor = 1.0f;

		int resolutionFactor = Settings.Get<int>("VisualiserResolutionFactor"); //higher = lower resolution, number is the number of samples to skip
		float barWidth = Settings.Get<float>("VisualiserBarWidth");

		bool bars = true;

		bool stopped = false;

		public Rectangle Bounds;

		public void AddSamples(short[] samples)
		{
			if (stopped)
				return;

			if (anchor == DateTime.MinValue)
				anchor = DateTime.Now;

			foreach (var s in samples)
				sampleBuffer.PushRight(s);

			if (sampleBuffer.Count > fftSize * 4)
			{
				DateTime now = DateTime.Now;
				int currentPos = (int)(Globals.SAMPLE_RATE * ((now - anchor).TotalMilliseconds / 1000)) * 2;
				anchor = now;
				for (int i = 0; i < currentPos && sampleBuffer.Count > 0; i++)
					sampleBuffer.PopLeft();
			}
		}

		public void ClearBuffers()
		{
			anchor = DateTime.MinValue;
			sampleBuffer.Clear();
		}

		public void Stop()
		{
			stopped = true;
			sampleBuffer.Clear();
			anchor = DateTime.MinValue;
		}

		public void Start()
		{
			stopped = false;
		}

		public void IncreaseBarWidth(float amount)
		{
			barWidth += amount;
			Settings.Set("VisualiserBarWidth", barWidth);
			Settings.WriteSettings();
		}

		public void IncreaseResolution(int amount)
		{
			if (resolutionFactor - amount > 0)
			{
				resolutionFactor -= amount;
				Settings.Set("VisualiserResolutionFactor", resolutionFactor);
				Settings.WriteSettings();
			}
		}

		private float[] CalculateNextFftFrame()
		{
			if (sampleBuffer == null)
				return null;

			int currentPos = (int)(Globals.SAMPLE_RATE * ((DateTime.Now - anchor).TotalMilliseconds / 1000)) * 2;
			if (sampleBuffer.Count - currentPos < fftSize || currentPos < 0)
				return null;

			short[] window = new short[fftSize];
			for (int i = 0; i < fftSize; i++)
				window[i] = sampleBuffer[currentPos + i];
			
			applyWindowFunction(window);
			float[] bins = FFT.Fft(window);
			bins = bins.Take(bins.Length / 4).ToArray();
			bins = bins.Select(f => (float)Math.Log10(f * 10) * 2 + 1).Select(f => ((f - 0.3f) * 1.5f) + 1.5f).ToArray();
			return bins;
		}
		
		private void applyWindowFunction(short[] data)
		{
			for (int i = 0; i < data.Length; i++)
			{
				data[i] = (short)(data[i] * 0.5 * (1 - Math.Cos(2 * Math.PI * i / data.Length)));
			}
		}

		public void Render(Graphics g)
		{
			float[] fftPoints;
			if (stopped)
			{
				fftPoints = new float[lastFftPoints.Length];
			}
			else
			{
				fftPoints = CalculateNextFftFrame();
				if (fftPoints == null)
				{
					fftPoints = lastFftPoints;
				}
			}

			//Process points
			int noPoints = fftPoints.Length / resolutionFactor;
			PointF[] points = new PointF[noPoints];

			for (int i = 1; i < fftPoints.Length; i++)
				fftPoints[i] = fftPoints[i] * bias + lastFftPoints[i] * (1 - bias);

			int j = 0;
			if (logarithmic)
			{
				float binWidth = (Globals.SAMPLE_RATE / 2) / fftPoints.Length;
				float maxNote = (float)(12 * Math.Log(((fftPoints.Length - 1) * binWidth) / 16.35, 10));
{'fraction_non_alphanumeric': 0.13193570929419987, 'fraction_numerical': 0.015653389238294898, 'mean_word_length': 3.1677344205008735, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5793478', 'n_tokens_mistral': 2984, 'n_tokens_neox': 2386, 'n_words': 599}