C# Perceptron Algorithm (Reading in a file) - c#

I'm currently creating a simple single layer perceptron algorithm. It should take in a .txt file and the algorithm runs over it. At the moment, I have the algorithm and just hard coded sample data values to test if it works (which it does), but I need it to feed off existing data values from a file. I have tried to make it work, but due to my inexperience in coding, I haven't succeeded. It would be awesome if someone could help me get this code working with external data as I've been pulling my hair out. The data is formatted like below (obviously without the bullet numbers).
0.651769
0.651604
0.651609
0.651679
0.651667
0.651699
Current Code:
using System;
using System.IO;
using System.Collections.Generic;
namespace Network
{
public class Network
{
static void Main()
{
// Load sample input patterns.
double[,] inputs = new double[,] {
{0.99, 0.99}, {0.99, 0.99}, {0.99, 0.99}, {0.99, 095}};
// Load sample output patterns.
int[] outputs = new int[] {0, 1, 0, 0 };
int patternCount = inputs.GetUpperBound(0) + 1;
// Randomise weights.
Random rdm = new Random();
// Setting randomly generated weights between -0.5 and +0.5
double[] weights = {rdm.NextDouble()-0.5, rdm.NextDouble()-0.5, rdm.NextDouble()};
// Set learning rate.
double learningRate = 1;
// Start iteration at 0
int iteration = 1;
double ErrorRate;
do
{
// Global error set to 0
ErrorRate = 0;
for (int j = 0; j < patternCount; j++)
{
// Calculate output.
int output = Output(weights, inputs[j, 0], inputs[j, 1]);
// Calculate error.
double localError = outputs[j] - output;
//if the localError is not equal to zero
if (localError != 0)
{
// Update weights.
for (int i = 0; i < 2; i++)
{
weights[i] += learningRate * localError * inputs[j, i] / 2;
}
}
// Convert error to absolute value.
ErrorRate += (localError);
}
Console.WriteLine("Iteration {0}\tError {1}", iteration, ErrorRate);
iteration++;
// If the Error is equal to zero then calculate
} while (ErrorRate != 0);
// Convergence
Console.WriteLine();
Console.WriteLine("[Input1] [Input2] [Output]");
// Input1 values
for (double input1 = 0; input1 <= 1; input1 += 1)
{
// Input2 values
for (double input2 = 0; input2 <= 1; input2 += 1)
{
// Calculate output with the inputs and the randomly generated weights
int output = Output(weights, input1, input2);
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine(" {0} {1} {2}", input1, input2, (output == 1) ? "1" : "0");
}
}
Console.Read();
}
private static int Output(double[] weights, double input1, double input2)
{
// Output = input1 * weight1 + input2 * weight2 + bias * weight3
double sum = input1 * weights[0] + input2 * weights[1] + 1 * weights[2];
// If the first condition is true, then it becomes the result. If not, the second condition becomes the result
return (sum >= 0) ? 1 : 0;
}
}
}

Are you looking for
using System.Globalization;
using System.IO;
using System.Linq
...
double[] data = File
.ReadLines(#"C:\MyFile.txt") //TODO: put actual file here
.Select(line => Double.Parse(line, CultureInfo.InvariantCulture))
.ToArray();

Related

How can I generate random text file with random content and size?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Tests
{
class RandomTests
{
private static Random random = new Random();
public static string RandomString(int length)
{
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
}
static List<string> listv;
public static void GenerateRandomStrings()
{
for (int i = 0; i < random.Next(20, 1000); i++)
{
string rand = RandomString(20);
ListViewCostumControl.lvnf.Items.Add(rand);
}
listv = ListViewCostumControl.lvnf.Items.Cast<ListViewItem>()
.Select(item => item.Text)
.ToList();
}
private void UsageExample()
{
GenerateRandom(5, 1, 6);
}
public static List<int> GenerateRandom(int count)
{
return GenerateRandom(count, 0, Int32.MaxValue);
}
// Note, max is exclusive here!
public static List<int> GenerateRandom(int count, int min, int max)
{
// initialize set S to empty
// for J := N-M + 1 to N do
// T := RandInt(1, J)
// if T is not in S then
// insert T in S
// else
// insert J in S
//
// adapted for C# which does not have an inclusive Next(..)
// and to make it from configurable range not just 1.
if (max <= min || count < 0 ||
// max - min > 0 required to avoid overflow
(count > max - min && max - min > 0))
{
// need to use 64-bit to support big ranges (negative min, positive max)
throw new ArgumentOutOfRangeException("Range " + min + " to " + max +
" (" + ((Int64)max - (Int64)min) + " values), or count " + count + " is illegal");
}
// generate count random values.
HashSet<int> candidates = new HashSet<int>();
// start count values before max, and end at max
for (int top = max - count; top < max; top++)
{
// May strike a duplicate.
// Need to add +1 to make inclusive generator
// +1 is safe even for MaxVal max value because top < max
if (!candidates.Add(random.Next(min, top + 1)))
{
// collision, add inclusive max.
// which could not possibly have been added before.
candidates.Add(top);
}
}
// load them in to a list, to sort
List<int> result = candidates.ToList();
// shuffle the results because HashSet has messed
// with the order, and the algorithm does not produce
// random-ordered results (e.g. max-1 will never be the first value)
for (int i = result.Count - 1; i > 0; i--)
{
int k = random.Next(i + 1);
int tmp = result[k];
result[k] = result[i];
result[i] = tmp;
}
return result;
}
}
}
For now I'm using only the GenerateRandomStrings() in Form1.
but now I ant to generate random text files with random content and size.
for example 100MB or 100GB files sizes with random content.
The files sizes should be from bytes to giga bytes. can be 1b file or 100GB file.
and then add the files to the listview as items like doing in the GenerateRandomStrings() function.
ListViewCostumControl.lvnf.Items.Add(rand);
the same idea to add generated text files with random content and sizes.
This code creates a file, with size defined by the user and filled with random text,
Console.WriteLine("Give file size in bytes");
var fileSize = int.Parse(Console.ReadLine());
var buffer = new byte[fileSize];
new Random().NextBytes(buffer);
var text = System.Text.Encoding.Default.GetString(buffer);
File.WriteAllText("/home/path/to/file/randomtext.txt", text);
Note: I am using Linux so you wanna change that path.

Get set of random numbers from input List having fixed sum using C#

I am looking for a C# algorithm that would give me a set of random integers from input List, such that the sum of obtained random integers is N.
For example:
If the list is {1,2,3,4,5,6...100} and N is 20, then the algorithm should return a set of random numbers like {5,6,9} or {9,11} or {1,2,3,4,10} etc.
Note that the count of integers in result set need not be fixed. Also, the input list can have duplicate integers. Performance is one of my priority as the input list can be large (around 1000 integers) and I need to randomize about 2-3 times in a single web request. I am flexible with not sticking to List as datatype if there is a performance issue with Lists.
I have tried below method which is very rudimentary and performance inefficient:
Use the Random class to get a random index from the input list
Get the integer from input list present at index obtained in #1. Lets call this integer X.
Sum = Sum + X.
Remove X from input list so that it does not get selected next.
If Sum is less than required total N, add X to outputList and go back to #1.
If the Sum is more than required total N, reinitialize everything and restart the process.
If the Sum is equal to required total N, return outputList
while(!reachedTotal)
{
//Initialize everything
inputList.AddRange(originalInputList);
outputList = new List<int>();
while (!reachedTotal)
{
random = r.Next(inputList.Count);
sum += inputList.ElementAt(random);
if(sum<N)
{
outputList.Add(inputList.ElementAt(random));
inputList.RemoveAt(random);
}
else if(sum>N)
break;
else
reachedTotal = true;
}
}
This is a stochastical approach that gives you a solution within a 10% range of N - Assuming one exists
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace StackOverflowSnippets
{
class Program
{
static void Main(string[] args)
{
// ----------------------------------------------------------------------------------
// The code you are interested in starts below this line
const Int32 N = 100;
Int32 nLowerBound = (90 * N) / 100; Int32 nUpperBound = (110 * N) / 100;
Random rnd = new Random();
Int32 runningSum = 0;
Int32 nextIndex = 0;
List<Int32> inputList = GenerateRandomList( /* entries = */ 1000);
List<Int32> o = new List<Int32>();
while (runningSum < nLowerBound)
{
nextIndex = rnd.Next(inputList.Count); if (nUpperBound < (runningSum + inputList[nextIndex])) continue;
runningSum += inputList[nextIndex];
o.Add(inputList[nextIndex]);
inputList.RemoveAt(nextIndex);
}
// The code you are interested in ends above this line
// ----------------------------------------------------------------------------------
StringBuilder b = new StringBuilder();
for(Int32 i = 0; i < o.Count;i++)
{
if (b.Length != 0) b.Append(",");
b.Append(o[i].ToString());
}
Console.WriteLine("Exact N : " + N);
Console.WriteLine("Upper Bound: " + nUpperBound);
Console.WriteLine("Lower Bound: " + nLowerBound);
Console.WriteLine();
Console.WriteLine("sum(" + b.ToString() + ")=" + GetSum(o).ToString());
Console.ReadLine();
}
// -------------------------------------------------------------------
#region Helper methods
private static object GetSum(List<int> o)
{
Int32 sum = 0;
foreach (Int32 i in o) sum += i;
return sum;
}
private static List<Int32> GenerateRandomList(Int32 entries)
{
List<Int32> l = new List<Int32>();
for(Int32 i = 1; i < entries; i++)
{
l.Add(i);
}
return l;
}
#endregion
}
}
EDIT
Forgot to remove the element from the input-list so it cannot be selected twice
Fixed the 'remove element' insertion

How to multiply all elements from array?

I am beginning to learn C# and am writing a program that will first ask a user to enter a list of numbers. When the user finishes entering the input, I would like to square every number the user provided in the input. An example of user input is 2 3 5.
Here is the code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Program
{
class Third
{
public static void Main()
{
Console.WriteLine("Enter how much numbers");
int howMuch = int.Parse(Console.ReadLine());
int[] num = new int[howMuch];
int sum = 0;
for (int i = 0; i < num.Length; i++ )
{
sum = num[i] * num[i]; // this is what i did but it does not work?
}
Console.WriteLine(sum);
Console.ReadLine();
}
}
}
Specifically, I would first like the user input to be captured in the numbers array. And then I would like to square each number in the num array that was created. What's wrong with my program?
First, you need to get input from user and fill the array:
for (int i = 0; i < num.Length; i++)
{
//TODO: Look into int.TryParse method to validate user input
num[i] = int.Parse(Console.ReadLine());
}
And instead of overwriting sum use sum += num[i] * num[i] in your second loop. Or if you are looking for the multiplication of all numbers just use sum = sum * num[i]; and start sum from 1.
Your code does not initialize the array - I added
Console.WriteLine("Enter number " + (i + 1));
num[i] = int.Parse(Console.ReadLine());
for that.
Also corrected the summarisation: sum += ...
for (int i = 0; i < num.Length; i++ )
{
Console.WriteLine("Enter number " + (i + 1));
num[i] = int.Parse(Console.ReadLine());
sum += (num[i] * num[i]);
}
This might be a LITTLE better, although there is still quite a bit of tidying up to do!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Program
{
class Third
{
public static void Main()
{
Console.WriteLine("Enter how many numbers");
int howMuch = int.Parse(Console.ReadLine());
int[] num = new int[howMuch];
int sum = 1;
for(int i=0;i<num.Length;i++) //allows you to fill the array
{
Console.WriteLine("Please enter an integer");
sum *= int.Parse(Console.ReadLine()); // this will now multiply the value to sum, as your comment suggests
}
Console.WriteLine(sum);
Console.ReadLine();
}
}
}
EDIT
sum *= num[i];
should do what you want!
Take a look at the math that you are doing in the loop.
sum = num[i] * num[i];
Each time through, you're setting sum equal to the square of the indexed integer.
Taking your example of 2, 3, 5, the first time through the loop, you will set sum = 2 * 2 (or 4), the second time through, you'll set sum = 3 * 3 (or 9) and the last time it will be sum = 5 * 5 (or 25). What you really want is 2 * 3 * 5, right?
All you would need to do is to initialize int sum = 1, and change the statement in your loop to be:
sum = sum * num[i];
This will yield sum = 1 * 2 the first time through, sum = 2 * 3 the second time through, and sum = 4 * 5 the third time through.
I've assumed that the sum that you want to do (if the input is 2,3,5) is 2*3*5. If this is the case, then naming your variable sum is a little misleading as that would tend to imply 2+3+5.
The for loop where you multiply the numbers had the line
sum = num[i]*num[i];
Which, following with the example, when i == 0, would do sum = 2*2, and then overwrite it as you increment the loop, so sum would end at being 25 (5*5), and discount all other values.
If you did want to sum the squares of the numbers, initializing sum to 0 and then using the line
sum += num[i] * num[i];
would work. Having said that, unless you specifically need to store it for any reason, processing the values when they are read would be better, as the program would have 1 fewer for loop.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Program
{
class Third
{
public static void Main()
{
Console.WriteLine("Enter how much numbers");
int howMuch = int.Parse(Console.ReadLine());
int[] num = new int[howMuch];
for(int i = 0; i < howMuch; ++i)
{
//This is assuming that the user will enter an int value.
//Ideally, verify this using int.TryParse or something similar.
num[i] = int.Parse(Console.ReadLine());
}
int sum = 1; //Set to 1 so that the PRODUCT is not always zero
for (int i = 0; i < num.Length; i++ )
{
sum *= num[i]; //Multiply the value
}
Console.WriteLine(sum);
Console.ReadLine();
}
}
If the input is 2, 3, 5, the value in sum will be 30
replace the code in Main() with this :-
Console.WriteLine("Enter how much numbers");
int howMuch = int.Parse(Console.ReadLine());
int[] num = new int[howMuch];
Console.WriteLine("Enter numbers");
int sum = 0;
for (int i = 0; i < num.Length; i++ )
{
num[i] = int.Parse(Console.ReadLine());
sum += num[i] * num[i]; // this is what i did but it does not work?
}
Console.WriteLine(sum);
Console.ReadLine();

How to measure similarity of 2 strings aside from computing distance

I am creating a program that checks if the word is simplified word(txt, msg, etc.) and if it is simplified it finds the correct spelling like txt=text, msg=message. Iam using the NHunspell Suggest Method in c# which suggest all possible results.
The problem is if I inputted "txt" the result is text,tat, tot, etc. I dont know how to select the correct word. I used Levenshtein Distance (C# - Compare String Similarity) but the results still results to 1.
Input: txt
Result: text = 1, ext = 1 tit = 1
Can you help me how to get the meaning or the correct spelling of the simplified words?
Example: msg
I have tested your input with your sample data and only text has a distance of 25 whereas the other have a distance of 33. Here's my code:
string input = "TXT";
string[] words = new[]{"text","tat","tot"};
var levenshtein = new Levenshtein();
const int maxDistance = 30;
var distanceGroups = words
.Select(w => new
{
Word = w,
Distance = levenshtein.iLD(w.ToUpperInvariant(), input)
})
.Where(x => x.Distance <= maxDistance)
.GroupBy(x => x.Distance)
.OrderBy(g => g.Key)
.ToList();
foreach (var topCandidate in distanceGroups.First())
Console.WriteLine("Word:{0} Distance:{1}", topCandidate.Word, topCandidate.Distance);
and here is the levenshtein class:
public class Levenshtein
{
///*****************************
/// Compute Levenshtein distance
/// Memory efficient version
///*****************************
public int iLD(String sRow, String sCol)
{
int RowLen = sRow.Length; // length of sRow
int ColLen = sCol.Length; // length of sCol
int RowIdx; // iterates through sRow
int ColIdx; // iterates through sCol
char Row_i; // ith character of sRow
char Col_j; // jth character of sCol
int cost; // cost
/// Test string length
if (Math.Max(sRow.Length, sCol.Length) > Math.Pow(2, 31))
throw (new Exception("\nMaximum string length in Levenshtein.iLD is " + Math.Pow(2, 31) + ".\nYours is " + Math.Max(sRow.Length, sCol.Length) + "."));
// Step 1
if (RowLen == 0)
{
return ColLen;
}
if (ColLen == 0)
{
return RowLen;
}
/// Create the two vectors
int[] v0 = new int[RowLen + 1];
int[] v1 = new int[RowLen + 1];
int[] vTmp;
/// Step 2
/// Initialize the first vector
for (RowIdx = 1; RowIdx <= RowLen; RowIdx++)
{
v0[RowIdx] = RowIdx;
}
// Step 3
/// Fore each column
for (ColIdx = 1; ColIdx <= ColLen; ColIdx++)
{
/// Set the 0'th element to the column number
v1[0] = ColIdx;
Col_j = sCol[ColIdx - 1];
// Step 4
/// Fore each row
for (RowIdx = 1; RowIdx <= RowLen; RowIdx++)
{
Row_i = sRow[RowIdx - 1];
// Step 5
if (Row_i == Col_j)
{
cost = 0;
}
else
{
cost = 1;
}
// Step 6
/// Find minimum
int m_min = v0[RowIdx] + 1;
int b = v1[RowIdx - 1] + 1;
int c = v0[RowIdx - 1] + cost;
if (b < m_min)
{
m_min = b;
}
if (c < m_min)
{
m_min = c;
}
v1[RowIdx] = m_min;
}
/// Swap the vectors
vTmp = v0;
v0 = v1;
v1 = vTmp;
}
// Step 7
/// Value between 0 - 100
/// 0==perfect match 100==totaly different
///
/// The vectors where swaped one last time at the end of the last loop,
/// that is why the result is now in v0 rather than in v1
//System.Console.WriteLine("iDist=" + v0[RowLen]);
int max = System.Math.Max(RowLen, ColLen);
return ((100 * v0[RowLen]) / max);
}
///*****************************
/// Compute the min
///*****************************
private int Minimum(int a, int b, int c)
{
int mi = a;
if (b < mi)
{
mi = b;
}
if (c < mi)
{
mi = c;
}
return mi;
}
///*****************************
/// Compute Levenshtein distance
///*****************************
public int LD(String sNew, String sOld)
{
int[,] matrix; // matrix
int sNewLen = sNew.Length; // length of sNew
int sOldLen = sOld.Length; // length of sOld
int sNewIdx; // iterates through sNew
int sOldIdx; // iterates through sOld
char sNew_i; // ith character of sNew
char sOld_j; // jth character of sOld
int cost; // cost
/// Test string length
if (Math.Max(sNew.Length, sOld.Length) > Math.Pow(2, 31))
throw (new Exception("\nMaximum string length in Levenshtein.LD is " + Math.Pow(2, 31) + ".\nYours is " + Math.Max(sNew.Length, sOld.Length) + "."));
// Step 1
if (sNewLen == 0)
{
return sOldLen;
}
if (sOldLen == 0)
{
return sNewLen;
}
matrix = new int[sNewLen + 1, sOldLen + 1];
// Step 2
for (sNewIdx = 0; sNewIdx <= sNewLen; sNewIdx++)
{
matrix[sNewIdx, 0] = sNewIdx;
}
for (sOldIdx = 0; sOldIdx <= sOldLen; sOldIdx++)
{
matrix[0, sOldIdx] = sOldIdx;
}
// Step 3
for (sNewIdx = 1; sNewIdx <= sNewLen; sNewIdx++)
{
sNew_i = sNew[sNewIdx - 1];
// Step 4
for (sOldIdx = 1; sOldIdx <= sOldLen; sOldIdx++)
{
sOld_j = sOld[sOldIdx - 1];
// Step 5
if (sNew_i == sOld_j)
{
cost = 0;
}
else
{
cost = 1;
}
// Step 6
matrix[sNewIdx, sOldIdx] = Minimum(matrix[sNewIdx - 1, sOldIdx] + 1, matrix[sNewIdx, sOldIdx - 1] + 1, matrix[sNewIdx - 1, sOldIdx - 1] + cost);
}
}
// Step 7
/// Value between 0 - 100
/// 0==perfect match 100==totaly different
//System.Console.WriteLine("Dist=" + matrix[sNewLen, sOldLen]);
int max = System.Math.Max(sNewLen, sOldLen);
return (100 * matrix[sNewLen, sOldLen]) / max;
}
}
Not a complete solution, just a hopefully helpful suggestion...
It seems to me that people are unlikely to use simplifications that are as long as the correct word, so you could at least filter out all results whose length <= the input's length.
You really need to implement the SOUNDEX routine that exists in SQL. I've done that in the following code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Soundex
{
class Program
{
static char[] ignoreChars = new char[] { 'a', 'e', 'h', 'i', 'o', 'u', 'w', 'y' };
static Dictionary<char, int> charVals = new Dictionary<char, int>()
{
{'b',1},
{'f',1},
{'p',1},
{'v',1},
{'c',2},
{'g',2},
{'j',2},
{'k',2},
{'q',2},
{'s',2},
{'x',2},
{'z',2},
{'d',3},
{'t',3},
{'l',4},
{'m',5},
{'n',5},
{'r',6}
};
static void Main(string[] args)
{
Console.WriteLine(Soundex("txt"));
Console.WriteLine(Soundex("text"));
Console.WriteLine(Soundex("ext"));
Console.WriteLine(Soundex("tit"));
Console.WriteLine(Soundex("Cammmppppbbbeeelll"));
}
static string Soundex(string s)
{
s = s.ToLower();
StringBuilder sb = new StringBuilder();
sb.Append(s.First());
foreach (var c in s.Substring(1))
{
if (ignoreChars.Contains(c)) { continue; }
// if the previous character yields the same integer then skip it
if ((int)char.GetNumericValue(sb[sb.Length - 1]) == charVals[c]) { continue; }
sb.Append(charVals[c]);
}
return string.Join("", sb.ToString().Take(4)).PadRight(4, '0');
}
}
}
See, with this code, the only match out of the examples you gave would be text. Run the console application and you'll see the output (i.e. txt would match text).
One method I think programs like word uses to correct spellings, is to use NLP (Natural Language Processing) techniques to get the order of Nouns/Adjectives used in the context of the spelling mistakes.. then comparing that to known sentence structures they can estimate 70% chance the spelling mistake was a noun and use that information to filter the corrected spellings.
SharpNLP looks like a good library but I haven't had a chance to fiddle with it yet. To build a library of known sentence structures BTW, in uni we applied our algorithms to public domain books.
check out sams simMetrics library I found on SO (download here, docs here) for loads more options for algorithms to use besides Levenshtein distance.
Expanding on my comment, you could use regex to search for a result that is an 'expansion' of the input. Something like this:
private int stringSimilarity(string input, string result)
{
string regexPattern = ""
foreach (char c in input)
regexPattern += input + ".*"
Match match = Regex.Match(result, regexPattern,
RegexOptions.IgnoreCase);
if (match.Success)
return 1;
else
return 0;
}
Ignore the 1 and the 0 - I don't know how similarity valuing works.

How to calculate the reverberation time of a wave signal in C#

I am trying to develop a console application in C# which uses a WAV-file for input. The application should do a couple of things all in order, as shown below. First of all, the complete code:
class Program
{
static List<double> points = new List<double>();
static double maxValue = 0;
static double minValue = 1;
static int num = 0;
static int num2 = 0;
static List<double> values = new List<double>();
private static object akima;
static void Main(string[] args)
{
string[] fileLines = File.ReadAllLines(args[0]);
int count = 0;
foreach (string fileLine in fileLines)
{
if (!fileLine.Contains(";"))
{
string processLine = fileLine.Trim();
processLine = Regex.Replace(processLine, #"\s+", " ");
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
{
processLine = processLine.Replace(".", ",");
}
string[] dataParts = processLine.Split(Char.Parse(" "));
points.Add(double.Parse(dataParts[0]));
double value = Math.Pow(double.Parse(dataParts[1]), 2);
if (value > maxValue)
{
maxValue = value;
num = count;
}
values.Add(value);
}
count++;
}
for (int i = num; i < values.Count; i++)
{
if (values[i] < minValue)
{
minValue = values[i];
num2 = i;
}
}
Console.WriteLine(num + " " + num2);
int between = num2 - num;
points = points.GetRange(num, between);
values = values.GetRange(num, between);
List<double> defVal = new List<double>();
List<double> defValPoints = new List<double>();
alglib.spline1dinterpolant c;
alglib.spline1dbuildakima(points.ToArray(), values.ToArray(), out c);
double baseInt = alglib.spline1dintegrate(c, points[points.Count - 1]);
List<double> defETC = new List<double>();
for (int i = 0; i < points.Count; i += 10)
{
double toVal = points[i];
defETC.Add(10 * Math.Log10(values[i]));
defVal.Add(10 * Math.Log10((baseInt - alglib.spline1dintegrate(c, toVal)) / baseInt));
defValPoints.Add(points[i]);
}
WriteDoubleArrayToFile(defValPoints.ToArray(), defVal.ToArray(), "test.dat");
WriteDoubleArrayToFile(defValPoints.ToArray(), defETC.ToArray(), "etc.dat");
int end = 0;
for (int i = 0; i < points.Count; i++)
{
if (defVal[i] < -10)
{
end = i;
break;
}
}
//Console.WriteLine(num + " " + end);
int beginEDT = num;
int endEDT = num + end;
double timeBetween = (defValPoints[endEDT] - defValPoints[beginEDT]) * 6;
Console.WriteLine(timeBetween);
for (int i = 0; i < points.Count; i++)
{
}
Console.ReadLine();
}
static void WriteDoubleArrayToFile(double[] points, double[] values, string filename)
{
string[] defStr = new string[values.Length];
for (int i = 0; i < values.Length; i++)
{
defStr[i] = String.Format("{0,10}{1,25}", points[i], values[i]);
}
File.WriteAllLines(filename, defStr);
}
}
Extract the decimal/float/double value from the WAV-file
Create an array from extracted data
Create an Energy Time Curve that displays the decay of the noise/sound in a decibel-like way
Create an Decay Curve from the ETC created in step 3
Calculate things as Early Decay Time (EDT), T15/T20 and RT60 from this Decay Curve.
Display these Reverb Times in stdout.
At the moment I am sort of like half way through the process. I´ll explain what I did:
I used Sox to convert the audio file into a .dat file with numbers
I create an array using C# by simply splitting each line in the file above and putting the times in a TimesArray and the values at those points in a ValuesArray.
I am displaying a graph via GNUPlot, using the data processed with this function: 10 * Math.Log10(values[i]); (where i is an iterative integer in a for-loop iterating over all the items in the ValuesArray)
This is where I'm starting to get stuck. I mean, in this step I am using an Akima Spline function from Alglib to be able to integrate a line. I am doing that with a Schroeder integration (reversed), via this mathematical calculation: 10 * Math.Log10((baseInt - alglib.spline1dintegrate(c, toVal)) / baseInt); (where baseInt is a value calculated as a base integral for the complete curve, so I have a calculated bottom part of the reversed Schroeder integration. The c is a spline1dinterpolant made available when using the function alglib.spline1dbuildakima, which takes the timeArray as x values, valueArray as the y values, and c as an outwards spline1dinterpolant. toval is an x-value from the points array. The specific value is selected using a for-loop.) From these newly saved values I want to create an interpolated line and calculate the RT60 from that line, but I do not know how to do that.
Tried, did not really work out.
Same as above, I have no real values to show.
I'm quite stuck now, as I'm not sure if this is the right way to do it. If anyone can tell me how I can calculate the reverberation times in a fast and responsive way in C#, I'd be pleased to hear. The way of doing it might be completely different from what I have now, that's OK, just let me know!
Maybe you need to approach this differently:
start with the underlying maths. find out the mathematical formulas for these functions.
Use a simple mathematical function and calculate by hand (in excel or matlab) what the values should be (of all these things ETC, DC, EDC, T15, T20, RT60)
(A function such as a sine wave of just the minimum number of points you need )
then write a separate procedure to evaluate each of these in C# and verify your results for consistency with excel/matlab.
in C#, maybe store your data in a class that you pass around to each of the methods for its calculation.
your main function should end up something like this:
main(){
data = new Data();
//1, 2:
extract_data(data, filename);
//3:
energy_time_curve(data)
//...4, 5
//6:
display_results(data);
}

Categories