I am trying to create a convolution reverb algorithm that takes a sound input signal and convolves it in the frequency domain with an impulse response. I have been trying to debug the code for a week and I cannot seem to find where the error is. The output of the algorithm is a .wav file that for some reason is very loud in the beginning but still sounds echoey at the end. I am using Aforge .net libraryLink to compute the FFT and the fault does not lie within the coding of that algorithm. Here is my code:
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using AForge.Math;
using NWaves.Operations.Convolution;
using UnityEngine;
public class ConvolutionReverbTest : MonoBehaviour {
[SerializeField] private AudioClip input;
[SerializeField] private AudioClip impulseResponse;
// ir
private float[] irData;
private Complex[] irc;
// input
private float[] inputData;
private Complex[] inputc;
// conv. reverb values
private int fftSize = 0; // F = N + M - 1 --> Also to the power of 2
// muliplication in freq domain
private Complex[] multiplication;
// output signal
private float[] output;
private void Start() {
GetAudioData();
Convolve();
}
private void Convolve() {
// calculate the fft of input data
FourierTransform.FFT(inputc, FourierTransform.Direction.Forward);
// calculate fft of impulse response
FourierTransform.FFT(irc, FourierTransform.Direction.Forward);
// multiply them in frequency domain
multiplication = new Complex[fftSize];
for (int i = 0; i < fftSize; i++) {
multiplication[i] = inputc[i] * irc[i];
multiplication[i] *= fftSize; // normalize it
}
// calculate the ifft of the multiplication
FourierTransform.FFT(multiplication, FourierTransform.Direction.Backward);
// save output signal as wave file
output = new float[fftSize];
for (int i = 0; i < fftSize; i++) {
output[i] = (float) multiplication[i].Re;
}
SaveOutputSignal(output);
}
private Complex ComplexMultiply(Complex a, Complex b) {
return new Complex(a.Re * b.Re - a.Im * b.Im, a.Re * b.Im + a.Im * b.Re);
}
private void CalculateFftSize() {
fftSize = Mathf.NextPowerOfTwo(inputData.Length + irData.Length - 1);
}
private void GetAudioData() {
// get data of ir signal
irData = new float[impulseResponse.samples];
impulseResponse.GetData(irData, 0);
// get data of input signal
inputData = new float[input.samples];
input.GetData(inputData, 0);
CalculateFftSize();
// change values accordingly of both signals --> zero padding + complex numbers
irData = ZeroPadding(irData);
irc = new Complex[irData.Length];
for (int i = 0; i < irData.Length; i++) {
irc[i].Re = irData[i];
}
inputData = ZeroPadding(inputData);
inputc = new Complex[inputData.Length];
for (int i = 0; i < inputData.Length; i++) {
inputc[i].Re = inputData[i];
}
}
private float[] ZeroPadding(float[] data) {
List<float> newData = new List<float>(data);
int N = data.Length;
for (int i = 0; i < (fftSize - N); i++) {
newData.Add(0);
}
return newData.ToArray();
}
private void SaveOutputSignal(float[] outputSignal) {
string filename = "outputSignal.txt";
// save the output signal to a txt file
File.WriteAllLines(Path.Combine(Application.streamingAssetsPath, filename),
outputSignal.Select(d => d.ToString()));
new WaveFile().SaveAudio(Path.Combine(Application.streamingAssetsPath, "output.wav"), outputSignal.Length, 1, outputSignal, input.frequency);
}
}
Here is also a picture of the output signal in audacity:
Any help is greatly appreciated.
Related
I've created a procedural mesh script while watching one of Freya Holmér's improvised live course vods, and re purposed the code to create a procedural tube mesh with subdivision and plenty of other niche features.
But, after looking over the code and the lesson, I still cannot for the life of me figure out why sometimes I will get an:
argument out of range exception
...and sometimes I won't depending on the level of subdivision; also, entire faces wont be generated by the script.
TL;DR Problems list:
Argument out of range exception (Depending on level of subdivision).
Entire sides/faces of the tube mesh will not be generated, even when no errors are provided.
These problems are stemming from line 154 in the UpdateTris() method.
Code
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
using OpenNexus.ExtraGizmos;
#endif
using OpenNexus.BaseMath;
using OpenNexus.MeshFormat;
namespace OpenNexus.Procedurals
{
[RequireComponent(typeof(MeshFilter))]
public class TubeMesh : MonoBehaviour
{
private MeshFilter filter;
private Mesh mesh;
public List<Vector3> Points = new List<Vector3>()
{
new Vector3(0, 0, 0),
new Vector3(0, 1, 1),
new Vector3(0, 0, 2)
};
[Header("Bezier Data")]
public Precision BezierPrecision = Precision.Fast;
[Header("Tube Data")]
[Range(0.01f, 1f)]public float Radius = 0.01f;
[Range(3, 32)] public int Segments = 8;
[Range(3, 6)] public int Subdivisions = 3;
[Header("Mesh Data")]
public ProceduralMesh2D BaseProceduralMesh = new ProceduralMesh2D();
public List<Vector3> Vertices;
public List<int> Tris;
private void OnEnable()
{
if (!GetComponent<MeshRenderer>()) // Check for and add missing mesh renderer.
gameObject.AddComponent<MeshRenderer>();
if (filter == null) // Check for and assign mesh filter variable.
filter = GetComponent<MeshFilter>();
if (mesh == null) // Check for and instantiate a new mesh object.
mesh = new Mesh();
mesh.name = "TubeMesh"; // Set name to mesh object.
filter.sharedMesh = mesh; // Set MeshFilter's shared mesh variable to new mesh object.
}
private void Update()
{
/*
Data reset
------------------------------
*/
// Reset base mesh data.
BaseProceduralMesh.Vertices = new List<Vertex>();
BaseProceduralMesh.LineIndex = new List<int>();
// Reset mesh data.
Vertices = new List<Vector3>();
Tris = new List<int>();
/*
Data update
------------------------------
*/
// Update base mesh.
UpdateBaseMesh();
// Update mesh data.
UpdateVertices();
UpdateTris();
}
private void LateUpdate() => UpdateMesh();
private BezierPoint GetBezierPoint(int index)
{
float _t = index / (Segments - 1f);
BezierPoint _bp = BezierMath.QuadraticBezier(Points, BezierPrecision, _t);
return _bp;
}
private void UpdateBaseMesh()
{
// Generate base vertices.
for (int i = 0; i < Subdivisions; i++)
{
float _point = i / (float)Subdivisions;
float _radius = _point * Floats.TAU;
Vertex _vertex = new Vertex(VectorThrees.UnitVectorByAngle(_radius) * Radius);
BaseProceduralMesh.Vertices.Add(_vertex);
}
// Generate base LineIndexes.
for (int i = 0; i < BaseProceduralMesh.VertexCount; i++)
{
BaseProceduralMesh.LineIndex.Add(i);
}
BaseProceduralMesh.LineIndex.Add(0);
}
private void UpdateVertices()
{
for (int i = 0; i < Segments; i++)
{
BezierPoint _point = GetBezierPoint(i);
for (int j = 0; j < BaseProceduralMesh.VertexCount; j++)
{
Vertices.Add(_point.LocalToWorldPosition(BaseProceduralMesh.Vertices[j].Point));
}
}
}
private void UpdateTris()
{
for (int s = 0; s < Segments - 1; s++)
{
int _root = s * BaseProceduralMesh.VertexCount;
int _rootNext = (s + 1) * BaseProceduralMesh.VertexCount;
for (int i = 0; i < BaseProceduralMesh.EdgeCount; i+=2)
{
int _lineA = BaseProceduralMesh.LineIndex[i];
int _lineB = BaseProceduralMesh.LineIndex[i + 1];
int _currentA = _root + _lineA;
int _currentB = _root + _lineB;
int _nextA = _rootNext + _lineA;
int _nextB = _rootNext + _lineB;
Tris.Add(_currentA);
Tris.Add(_nextA);
Tris.Add(_nextB);
Tris.Add(_currentA);
Tris.Add(_nextB);
Tris.Add(_currentB);
}
}
}
private void UpdateMesh()
{
mesh.Clear();
mesh.SetVertices(Vertices);
mesh.SetTriangles(Tris, 0);
mesh.RecalculateNormals();
}
#if UNITY_EDITOR
private void OnDrawGizmos()
{
// Draw psudo mesh with gizmos.
/*
Draw segment/edge loops
-------------------------------------
*/
for (int i = 0; i < Segments; i++) // Debug each segment, and what their 2D mesh segment should look like.
{
BezierPoint _point = GetBezierPoint(i);
WireGizmos.DrawWireCircle(_point.Position, _point.Rotation, Radius, Subdivisions);
}
Gizmos.color = Color.red;
for (int i = 0; i < Vertices.Count; i++) // Debug each vertex.
{
Gizmos.DrawSphere(Vertices[i], 0.01f);
Handles.Label(Vertices[i], "\n\nVertex: " + i + "\n\nVertex position:\n" + Vertices[i].ToString());
}
for (int i = 0; i < Tris.Count; i++)
{
Gizmos.DrawLine(Vertices[Tris[i]], Vertices[Tris[(i + 1) % Tris.Count]]);
}
}
#endif
}
}
I've already looked over my code compared to the code in the video, it's fundamentally the same, with the main differences being, how I'm creating the 2D mesh format for the script to work with, and the structure of the code.
After looking back at what they had done compared to mine, I just don't see how I'm running into this issue.
Things I've tried
Change the loop iteration i+=2 -to- i++ (This spits out the exception, but generates the first section of the tube before getting stuck between the second vertex in the next segment, and vert 0).
Every suggestion I try from this question, I will update the "Things I've tried..." list.
Please let me know if I need to clarify anything in this post, and if you wish to see the other classes/structs referenced in this script.
After a day of resting, and the a couple of hours of testing different values and questioning the code. I've found the problem.
The real issue
The nested for loop in the UpdateTris() method was going up by twos assuming I was making a hard edge mesh with pared vertices. This caused the loop to skip over an entire face of the mesh, and causing the
ArgumentOutOfRangeException
...when the Subdivision value was an even number.
My solution
I had to bring it back to the default single iteration (since I was trying to make a smooth lighting mesh), the second issue with the for loop was the iteration limit BaseProceduralMesh.LineCount needing to be subtracted by one since that was causing another
ArgumentOutOfRangeException
I'm working on a unity project involving deformable terrain based on marching-cubes. It works by generating a map of density over the 3-dimensional coordinates of a terrain chunk and using that data to create a mesh representing the surface of the terrain. It has been working, however the process is very slow. I'm attempting to introduce multithreading to improve performance, but I've run into a problem that's left me scratching my head.
When I run CreateMeshData() and try to pass my density map terrainMap into the MarchCubeJob struct, it recognizes it as a reference type, not a value type. I've seemed to whittle down the errors to this one, but I've tried to introduce the data in every way I know how and I'm stumped. I thought passing a reference like this was supposed to create a copy of the data disconnected from the reference, but my understanding must be flawed. My goal is to pass each marchingcube cube into a job and have them run concurrently.
I'm brand new to multithreading, so I've probably made some newbie mistakes here and I'd appreciate if someone would help me out with a second look. Cheers!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Jobs;
using Unity.Collections;
using Unity.Burst;
public class Chunk
{
List<Vector3> vertices = new List<Vector3>();
List<int> triangles = new List<int>();
public GameObject chunkObject;
MeshFilter meshFilter;
MeshCollider meshCollider;
MeshRenderer meshRenderer;
Vector3Int chunkPosition;
public float[,,] terrainMap;
// Job system
NativeList<Vector3> marchVerts;
NativeList<Vector3> marchTris;
MarchCubeJob instanceMarchCube;
JobHandle instanceJobHandle;
int width { get { return Terrain_Data.chunkWidth;}}
int height { get { return Terrain_Data.chunkHeight;}}
static float terrainSurface { get { return Terrain_Data.terrainSurface;}}
public Chunk (Vector3Int _position){ // Constructor
chunkObject = new GameObject();
chunkObject.name = string.Format("Chunk x{0}, y{1}, z{2}", _position.x, _position.y, _position.z);
chunkPosition = _position;
chunkObject.transform.position = chunkPosition;
meshRenderer = chunkObject.AddComponent<MeshRenderer>();
meshFilter = chunkObject.AddComponent<MeshFilter>();
meshCollider = chunkObject.AddComponent<MeshCollider>();
chunkObject.transform.tag = "Terrain";
terrainMap = new float[width + 1, height + 1, width + 1]; // Weight of each point
meshRenderer.material = Resources.Load<Material>("Materials/Terrain");
// Generate chunk
PopulateTerrainMap();
CreateMeshData();
}
void PopulateTerrainMap(){
...
}
void CreateMeshData(){
ClearMeshData();
vertices = new List<Vector3>();
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
for (int z = 0; z < width; z++) {
Debug.Log(x + ", " + y + ", " + z + ", begin");
Vector3Int position = new Vector3Int(x, y, z);
// Set up memory pointers
NativeList<Vector3> marchVerts = new NativeList<Vector3>(Allocator.TempJob);
NativeList<int> marchTris = new NativeList<int>(Allocator.TempJob);
NativeList<float> mapSample = new NativeList<float>(Allocator.TempJob);
// Split marchcube into jobs by cube
instanceMarchCube = new MarchCubeJob(){
position = position,
marchVerts = marchVerts,
marchTris = marchTris,
mapSample = terrainMap
};
// Run job for each cube in a chunk
instanceJobHandle = instanceMarchCube.Schedule();
instanceJobHandle.Complete();
// Copy data from job to mesh data
//instanceMarchCube.marchVerts.CopyTo(vertices);
vertices.AddRange(marchVerts);
triangles.AddRange(marchTris);
// Dispose of memory pointers
marchVerts.Dispose();
marchTris.Dispose();
mapSample.Dispose();
Debug.Log(x + ", " + y + ", " + z + ", end");
}
}
}
BuildMesh();
}
public void PlaceTerrain (Vector3 pos, int radius, float speed){
...
CreateMeshData();
}
public void RemoveTerrain (Vector3 pos, int radius, float speed){
...
CreateMeshData();
}
void ClearMeshData(){
vertices.Clear();
triangles.Clear();
}
void BuildMesh(){
Mesh mesh = new Mesh();
mesh.vertices = vertices.ToArray();
mesh.triangles = triangles.ToArray();
mesh.RecalculateNormals();
meshFilter.mesh = mesh;
meshCollider.sharedMesh = mesh;
}
private void OnDestroy(){
marchVerts.Dispose();
marchTris.Dispose();
}
}
// Build a cube as a job
[BurstCompile]
public struct MarchCubeJob: IJob{
static float terrainSurface { get { return Terrain_Data.terrainSurface;}}
public Vector3Int position;
public NativeList<Vector3> marchVerts;
public NativeList<int> marchTris;
public float[,,] mapSample;
public void Execute(){
//Sample terrain values at each corner of cube
float[] cube = new float[8];
for (int i = 0; i < 8; i++){
cube[i] = SampleTerrain(position + Terrain_Data.CornerTable[i]);
}
int configIndex = GetCubeConfiguration(cube);
// If done (-1 means there are no more vertices)
if (configIndex == 0 || configIndex == 255){
return;
}
int edgeIndex = 0;
for (int i = 0; i < 5; i++){ // Triangles
for (int p = 0; p < 3; p++){ // Tri Vertices
int indice = Terrain_Data.TriangleTable[configIndex, edgeIndex];
if (indice == -1){
return;
}
// Get 2 points of edge
Vector3 vert1 = position + Terrain_Data.CornerTable[Terrain_Data.EdgeIndexes[indice, 0]];
Vector3 vert2 = position + Terrain_Data.CornerTable[Terrain_Data.EdgeIndexes[indice, 1]];
Vector3 vertPosition;
// Smooth terrain
// Sample terrain values at either end of current edge
float vert1Sample = cube[Terrain_Data.EdgeIndexes[indice, 0]];
float vert2Sample = cube[Terrain_Data.EdgeIndexes[indice, 1]];
// Calculate difference between terrain values
float difference = vert2Sample - vert1Sample;
if (difference == 0){
difference = terrainSurface;
}
else{
difference = (terrainSurface - vert1Sample) / difference;
}
vertPosition = vert1 + ((vert2 - vert1) * difference);
marchVerts.Add(vertPosition);
marchTris.Add(marchVerts.Length - 1);
edgeIndex++;
}
}
}
static int GetCubeConfiguration(float[] cube){
int configurationIndex = 0;
for (int i = 0; i < 8; i++){
if (cube[i] > terrainSurface){
configurationIndex |= 1 << i;
}
}
return configurationIndex;
}
public float SampleTerrain(Vector3Int point){
return mapSample[point.x, point.y, point.z];
}
}
I have a code that takes a microphone input(when pressing "T)" and returns it as a float number (one final float number). However, when I yell or blow into microphone the maximum float it prints is 1 and no matter how I yell or say quietly after it printed 1, it keeps printing 1 only. How to change it to a bigger number. Let's say if I yell it would return a bigger number than if I just say quietly.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
public class MicInputs : MonoBehaviour
{
public enum micActivation
{
HoldToSpeak,
}
public micActivation micControl;
private float sample_max = 0.0f;
public static float MicLoudFloat;
public List<float> recorded_values = new List<float>();
private string theMicroDevice;
AudioClip recordOfClips;
//microphone initialization
void MicroInitialization()
{
if (theMicroDevice == null) theMicroDevice = Microphone.devices[0];
recordOfClips = Microphone.Start(theMicroDevice, true, 999, 44100);
}
void StopMicrophone()
{
Microphone.End(theMicroDevice);
Maximum_Level(); // Collect the final sample
MicLoudFloat = sample_max;
print(MicLoudFloat);
}
void Awake()
{
recordOfClips = AudioClip.Create("nameOfClip", 128, 2, 1000, true);
}
//AudioClip clip = myRecordedOrOwnClip;
//reate(string name, int lengthSamples, int channels, int frequency,
bool stream,
//_sampleWindow = clip.samples;
//AudioClip _clipRecord = new AudioClip();
//AudioClip _clipRecord = AudioClip.Create("nameOfClip", 128, 2, 1,
true);
int samplesWindows = 128;
//=========THIS IS THE START OF THE METHOD========
// get data from microphone into audioclip
float Maximum_Level()
{
float[] waveData = new float[samplesWindows];
int micPosition = Microphone.GetPosition(null) - (samplesWindows
+1); // null means the first microphone
if (micPosition < 0) return 0;
recordOfClips.GetData(waveData, micPosition);
// Getting a peak on the last 128 samples
for (int i = 0; i < samplesWindows; i++)
{
float wavePeak = waveData[i] * waveData[i];
if (wavePeak > sample_max)
{
sample_max = wavePeak;
}
}
return sample_max;
//float maximum_level = 0;
//float[] waveData = new float[samplesWindows];
//int micPosition = Microphone.GetPosition(null) - (samplesWindows
+ 1); // null means the first microphone
//if (micPosition < 0) return 0;
//recordOfClips.GetData(waveData, micPosition);
//// Getting a peak on the last 128 samples
//for (int i = 0; i < samplesWindows; i++)
//{
// float wavePeak = waveData[i] * waveData[i];
// if (maximum_level < wavePeak)
// {
// maximum_level = wavePeak;
// recorded_values.Add(maximum_level);
// }
//}
//float max = recorded_values.Max();
////print(max);
//return max;
//print(maximum_level);
//return maximum_level;
}
//=========THIS IS THE END OF THE METHOD========
void Update()
{
if (micControl == micActivation.HoldToSpeak)
{
//if (Microphone.IsRecording(theMicroDevice) &&
Input.GetKey(KeyCode.T) == false)
//StopMicrophone();
//
if (Input.GetKeyDown(KeyCode.T)){ //Push to talk
MicroInitialization();
}
//
if (Input.GetKeyUp(KeyCode.T)){
StopMicrophone();
}
}
Maximum_Level();
// levelMax equals to the highest normalized value power 2, a small
number because < 1
// pass the value to a static var so we can access it from anywhere
//print(MicLoudFloat);
}
//bool isItInitialized;
//// start mic when scene starts
//void OnEnable()
//{
// MicroInitialization();
// isItInitialized = true;
//}
////stop mic when loading a new level or quit application
//void OnDisable()
//{
// StopMicrophone();
//}
//void OnDestroy()
//{
// StopMicrophone();
//}
// detects if the mic gets started & stopped when application gets
focused
//void OnApplicationFocus(bool focus)
//{
// if (focus)
// {
// //Debug.Log("Focus");
// if (!isItInitialized)
// {
// //Debug.Log("Init Mic");
// MicroInitialization();
// isItInitialized = true;
// }
// }
// if (!focus)
// {
// //Debug.Log("Pause");
// StopMicrophone();
// //Debug.Log("Stop Mic");
// isItInitialized = false;
// }
//}
}
The sensor returns only a 0 or a 1, it's not 0->inf, if you want to something else, like checking how load is the voice from 0 to 100, you could record it and check the audio file highest bump and return that.
I am following this example but it is not that useful:
https://github.com/xamarin/urho-samples/tree/master/FeatureSamples/Core/29_SoundSynthesis
anyhow I am getting an run time error that says: The application is not configured yet.
but I made an application object .
the error happen a node = new Node();
what am I missing
this is my class:
using System;
using Urho.Audio;
using Urho;
using Urho.Resources;
using Urho.Gui;
using System.Diagnostics;
using System.Globalization;
namespace Brain_Entrainment
{
public class IsochronicTones : Urho.Application
{
/// Scene node for the sound component.
Node node;
/// Sound stream that we update.
BufferedSoundStream soundStream;
public double Frequency { get; set; }
public double Beat { get; set; }
public double Amplitude { get; set; }
public float Bufferlength { get; set; }
const int numBuffers = 3;
public IsochronicTones(ApplicationOptions AppOption) : base(AppOption)
{
Amplitude = 1;
Frequency = 100;
Beat = 0;
Bufferlength = Int32.MaxValue;
}
public void play()
{
Start();
}
protected override void OnUpdate(float timeStep)
{
UpdateSound();
base.OnUpdate(timeStep);
}
protected override void Start()
{
base.Start();
CreateSound();
}
void CreateSound()
{
// Sound source needs a node so that it is considered enabled
node = new Node();
SoundSource source = node.CreateComponent();
soundStream = new BufferedSoundStream();
// Set format: 44100 Hz, sixteen bit, mono
soundStream.SetFormat(44100, true, false);
// Start playback. We don't have data in the stream yet, but the
//SoundSource will wait until there is data
// as the stream is by default in the "don't stop at end" mode
source.Play(soundStream);
}
void UpdateSound()
{
// Try to keep 1/10 seconds of sound in the buffer, to avoid both
//dropouts and unnecessary latency
float targetLength = 1.0f / 10.0f;
float requiredLength = targetLength -
Bufferlength;//soundStream.BufferLength;
float w = 0;
if (requiredLength < 0.0f)
return;
uint numSamples = (uint)(soundStream.Frequency * requiredLength);
if (numSamples == 0)
return;
// Allocate a new buffer and fill it with a simple two-oscillator
//algorithm.The sound is over - amplified
// (distorted), clamped to the 16-bit range, and finally lowpass -
//filtered according to the coefficient
var newData = new short[numSamples];
for (int i = 0; i < numSamples; ++i)
{
float newValue = 0;
if (Beat == 0)
{
newValue = (float)(Amplitude * Math.Sin(Math.PI * Frequency * i / 44100D));
}
else
{
w = (float)(1D * Math.Sin(i * Math.PI * Beat / 44100D));
if (w < 0)
{
w = 0;
}
newValue = (float)(Amplitude * Math.Sin(Math.PI * Frequency * i / 44100D));
}
//accumulator = MathHelper.Lerp(accumulator, newValue, filter);
newData[i] = (short)newValue;
}
// Queue buffer to the stream for playback
soundStream.AddData(newData, 0, newData.Length);
}
}
}
I am trying to use the GA to create stronger waves of enemies in a tower defence game similar to plants vs zombies. Each population represents a wave and each chromosome represents an enemy(1011 = basic enemy, 1111 = enemy 2 etc.) I am using the AForge library, however i was wondering if its possible to set the initial population instead of it being randomly generated and how I can set the values for x and y (distance and damage) for each chromosome in a population. I am also not entirely sure as to what the Range represents.
Sorry if im missing something simple or if I am misunderstanding how it works, this is what I have so far, thanks:
public class UserFunction : OptimizationFunction2D
{
private double distanceMax = 1;
private double damageMax = 1;
private double w1 = 1;
private double w2 = 1;
public UserFunction() :
base(new Range(0, 100), new Range(0, 100)) { }
public override double OptimizationFunction(double distance, double damage)
{
return (w1*distance/distanceMax) + (w2*damage/damageMax);
}
}
class RunProgram
{
private Population[] populationArray = new Population[5];
private UserFunction userFunction = new UserFunction();
private BinaryChromosome chromosome = new BinaryChromosome(4);
private int popSize = 20;
private double mutateRate = 0.3;
private double damage;
private double distance;
private double limit = 100;
public RunProgram()
{
Pop();
}
private void Pop()
{
// create genetic population
for (int i = 0; i < 5; i++)
{
populationArray[i] = new Population(popSize,
chromosome,
userFunction,
new RouletteWheelSelection());
}
foreach (var pop in populationArray)
{
pop.MutationRate = mutateRate;
}
for (int i = 0; i < 50; i++)
{
foreach (var pop in populationArray)
{
pop.RunEpoch();
}
}
Array.Sort(populationArray, delegate(Population population1, Population population2) {
return population1.FitnessAvg.CompareTo(population2.FitnessAvg);
});
EnemyList(populationArray[4]);
Console.ReadLine();
}
void EnemyList(Population population)
{
for (int i = 0; i < popSize; i++)
{
Console.WriteLine("enemy "+ i +" is: "+population[i]);
}
}
}
I think all populations are approaching to an optimal wave. So representing a wave with population is useless unless each wave has unique conditions.