Create triangle audio wave - c#

i use NAudio to create diferent audio wave with two channels. For sine wave I have next code:
int sampleRate = WaveFormat.SampleRate;
for (int n = 0; n < sampleCount; n += 2)
{
buffer[n + offset] = (float)(AmplitudeLeft * Math.Sin((2 * Math.PI * sample * FrequencyLeft) / sampleRate));
buffer[n + offset + 1] = (float)(AmplitudeRight * Math.Sin((2 * Math.PI * sample * FrequencyRight) / sampleRate));
sample++;
if (sample >= sampleRate)
sample = 0;
}
return sampleCount;
I need a simple formula to create a triangle audio wave.

Divide the work to make it easier:
void makesamples_mono(float[] buffer, int offset, int sampleCount, int rate, double amp, double freq)
{
double one_over_half_per = freq * 2.0 / rate;
for (int i = 0; i < sampleCount; i++)
{
double div = i * one_over_half_per;
buffer[offset + 2 * i] = (float)(((((int)div) % 2 == 0) ? -amp : amp) * (1.0 - 2.0 * (div - (int)div)));
}
}
void writesamples_stereo(float[] buffer, int offset, int sampleCount, int rate, double l_amp, double r_amp, double l_freq, double r_freq)
{
makesamples_mono(buffer, offset, sampleCount, rate, l_amp, l_freq); // left channel
makesamples_mono(buffer, offset+1, sampleCount, rate, r_amp, r_freq); // right channel
}

Related

Matrix Multiplication returning wrong value

I am calculating values by using weights and bias from MATLAB trained ANN. trying to code a sigmoid simulation equation, but for some reason C# calculations vary too much than that of MATLAB. i.e. error is too high. I tried to check each step of the equation and found out the specific part that is creating the problem (Emphasized part), but I don't know how to solve this issue, if someone could help, would be a huge favour.
1+(purelin(net.LW{2}×(tansig(net.IW{1}×(1-(abs(2×([inputs]-1)))))+net.b{1}))+net.b{2}))/2
//Normalization of Data
public double Normalization(double x, double xMAx, double xMin)
{
double xNorm = 0.0;
xNorm = (x - xMin) / (xMAx - xMin);
if (xNorm < 0)
xNorm = 0;
if (xNorm > 1)
xNorm = 1;
xNorm = Math.Round(xNorm, 4);
return xNorm;
}
// Equation to calculate ANN based Output Values
public double MetrixCalc(double[] Pn, double[,] W1, double[] W2, double[] b1, double b2, double maxValue, double minValue)
{
double FinalValue = 0;
double[] PnCalc1 = new double[Pn.Length];
double[] PnCalc2 = new double[W1.Length / Pn.Length];
for (int i = 0; i < Pn.Length; i++)
{
PnCalc1[i] = 1 - Math.Abs(2 * (Pn[i] - 1));
}
for (int i = 0; i < (W1.Length / Pn.Length); i++)
{
double PnCalc = 0.0;
for (int j = 0; j < Pn.Length; j++)
{
PnCalc = PnCalc + (W1[i, j] * PnCalc1[j]);
}
PnCalc2[i] = PnCalc;
}
for (int i = 0; i < PnCalc2.Length; i++)
{
//PnCalc2[i] = Math.Tanh(PnCalc2[i] + b1[i]);
PnCalc2[i] = PnCalc2[i] + b1[i];
PnCalc2[i] = 2.0 / (1 + Math.Exp(-2 * (PnCalc2[i]))) - 1;
PnCalc2[i] = Math.Round(PnCalc2[i], 4);
}
double FinalCalc = 0.0;
for (int i = 0; i < PnCalc2.Length; i++)
{
*FinalCalc = FinalCalc + (W2[i] * (PnCalc2[i]));*
//FinalValue = FinalCalc;
}
FinalValue = FinalCalc + b2;
FinalValue = 1 + FinalValue;
FinalValue = (1 + FinalValue) / 2.0;
FinalValue = (FinalValue * (maxValue - minValue)) + minValue;
FinalValue = Math.Round(FinalValue, 4);
FinalValue = Math.Abs(FinalValue);
return FinalValue;
}
Problem is solved.
Problem was with the weights matrix copied from MATLAB. debugging mode saved my life. :)

C# .wav file data analysis & draw Freq.Domain graph from it

my first question in stackoverflow...so pls inform me, if i violate the forum rules
i try to analyze the audio .wav data (basically a sinusoid with/without Noise) in FFT & draw the result as a Freq. Domain graph using C#. I need to analyze the sine wave & find whether any noise is present or not for 10 to 20seconds audio data.
Sine wave was generated using Audacity with the following properties.
1KHz Sine Tone .wav data for 15 to 20s.
16Bits per sample
44100Hz
Mono Channel
Here is the code part to separate the Audio data from the Header data.
// --- Open & Read .wav File from the Path
System.IO.FileStream WaveFile = System.IO.File.OpenRead(waveFilePath);
System.IO.BinaryReader br = new System.IO.BinaryReader(WaveFile);
// Convert the length of the file into Byte value.
waveLength = WaveFile.Length;
wave = new byte[waveLength];
// --- Subtract the .wav File Header Info (First 44 Bytes) to get the real Audio Data. Source -> http://www.topherlee.com/software/pcm-tut-wavformat.html
wavenew = new byte[(waveLength - 44)];
for (long i = 0; i < waveLength; i++)
{
wave[i] = br.ReadByte();
//Get only the Audio Data byte values
if (i >= 44)
{
wavenew[i - 44] = wave[i];
}
}
// Bits per Sample(whether 8 Bit or 16 Bit.)- Header Information35, 36 is for Bits per Sample.
bitsPerSample = BitConverter.ToInt16(wave, 34); //16Bit
//Number of Channels - Header Information 23, 24 is for No of Channels. (Mono or Stereo)
noOfChannels = BitConverter.ToInt16(wave, 22); //Mono
// Calculate the over all Audio Data length, here for FFT only values of exponential 2 can be given. So take 2 power 17 = 131072 for this calculation. Remaining Bytes will be neglected.
// Divide by 4 since every audio data contains 4 bytes. (Mono,16 Bit or Stereo,8 bit - 4 Bytes. Mono, 8 bit - 2 Bytes)
dataLength = (131116-44)/4; // /4;
data = new double[dataLength];
//SampleRate - Header Information 25 to 28 (4 Bytes) are for SampleRates
SampleRate = BitConverter.ToInt32(wave, 24); // 44100;
// The every 4 Bytes contain the data of Sinus wave.
for (int i = 0; i < dataLength; i++)
{
data[i] = BitConverter.ToInt32(wavenew, (1 + i) * 4);
}
Here are example raw Audio data after conversion.
890381306
1403864333
1804165263
2058843210
2147319456
2063433407
1812231337
1414947118
903955929
320087260
-289603352
-875963166
-1391936590
-1795514361
-2054321130
-2147384959
For FFT, I use http://www.lomont.org/Software/Misc/FFT/LomontFFT.html as a reference, which returns a Double -Real & Imag Value.
public void RealFFT(double[] data, bool forward)
{
var n = data.Length; // # of real inputs, 1/2 the complex length
// checks n is a power of 2 in 2's complement format
if ((n & (n - 1)) != 0)
throw new ArgumentException(
"data length " + n + " in FFT is not a power of 2"
);
var sign = -1.0; // assume inverse FFT, this controls how algebra below works
if (forward)
{ // do packed FFT. This can be changed to FFT to save memory
TableFFT(data, true);
sign = 1.0;
// scaling - divide by scaling for N/2, then mult by scaling for N
if (A != 1)
{
var scale = Math.Pow(2.0, (A - 1) / 2.0);
for (var i = 0; i < data.Length; ++i)
data[i] *= scale;
}
}
var theta = B * sign * 2 * Math.PI / n;
var wpr = Math.Cos(theta);
var wpi = Math.Sin(theta);
var wjr = wpr;
var wji = wpi;
for (var j = 1; j <= n/4; ++j)
{
var k = n / 2 - j;
var tkr = data[2 * k]; // real and imaginary parts of t_k = t_(n/2 - j)
var tki = data[2 * k + 1];
var tjr = data[2 * j]; // real and imaginary parts of t_j
var tji = data[2 * j + 1];
var a = (tjr - tkr) * wji;
var b = (tji + tki) * wjr;
var c = (tjr - tkr) * wjr;
var d = (tji + tki) * wji;
var e = (tjr + tkr);
var f = (tji - tki);
// compute entry y[j]
data[2 * j] = 0.5 * (e + sign * (a + b));
data[2 * j + 1] = 0.5 * (f + sign * (d - c));
// compute entry y[k]
data[2 * k] = 0.5 * (e - sign * (b + a));
data[2 * k + 1] = 0.5 * (sign * (d - c) - f);
var temp = wjr;
// todo - allow more accurate version here? make option?
wjr = wjr * wpr - wji * wpi;
wji = temp * wpi + wji * wpr;
}
if (forward)
{
// compute final y0 and y_{N/2}, store in data[0], data[1]
var temp = data[0];
data[0] += data[1];
data[1] = temp - data[1];
}
else
{
var temp = data[0]; // unpack the y0 and y_{N/2}, then invert FFT
data[0] = 0.5 * (temp + data[1]);
data[1] = 0.5 * (temp - data[1]);
// do packed inverse (table based) FFT. This can be changed to regular inverse FFT to save memory
TableFFT(data, false);
// scaling - divide by scaling for N, then mult by scaling for N/2
//if (A != -1) // todo - off by factor of 2? this works, but something seems weird
{
var scale = Math.Pow(2.0, -(A + 1) / 2.0)*2;
for (var i = 0; i < data.Length; ++i)
data[i] *= scale;
}
}
} void Scale(double[] data, int n, bool forward)
{
// forward scaling if needed
if ((forward) && (A != 1))
{
var scale = Math.Pow(n, (A - 1) / 2.0);
for (var i = 0; i < data.Length; ++i)
data[i] *= scale;
}
// inverse scaling if needed
if ((!forward) && (A != -1))
{
var scale = Math.Pow(n, -(A + 1) / 2.0);
for (var i = 0; i < data.Length; ++i)
data[i] *= scale;
}
}
public void TableFFT(double[] data, bool forward)
{
var n = data.Length;
// checks n is a power of 2 in 2's complement format
if ((n & (n - 1)) != 0)
throw new ArgumentException(
"data length " + n + " in FFT is not a power of 2"
);
n /= 2; // n is the number of samples
Reverse(data, n); // bit index data reversal
// make table if needed
if ((cosTable == null) || (cosTable.Length != n))
Initialize(n);
// do transform: so single point transforms, then doubles, etc.
double sign = forward ? B : -B;
var mmax = 1;
var tptr = 0;
while (n > mmax)
{
var istep = 2 * mmax;
for (var m = 0; m < istep; m += 2)
{
var wr = cosTable[tptr];
var wi = sign * sinTable[tptr++];
for (var k = m; k < 2 * n; k += 2 * istep)
{
var j = k + istep;
var tempr = wr * data[j] - wi * data[j + 1];
var tempi = wi * data[j] + wr * data[j + 1];
data[j] = data[k] - tempr;
data[j + 1] = data[k + 1] - tempi;
data[k] = data[k] + tempr;
data[k + 1] = data[k + 1] + tempi;
}
}
mmax = istep;
}
// perform data scaling as needed
Scale(data, n, forward);
}
Amplitude is derived from the received Real & Imag. value as below
//Converting RealFFT return value in to an amplitude for Graph
(SqurRoot of Real*Real + Imag * Imag)
realPart = new double[(dataLength / 2)];
imagPart = new double[(dataLength / 2)];
dataNew = new double[(dataLength / 2)];
for (int i = 0; i < dataNew.Length; i++)
{
if (i == 0) //Ignore the first two values, since it contain the special values. Then calculate the magnitude for real & imaginary part.
{
}
else
{
realPart[i] = data[(i * 2)] * data[(i * 2)];
imagPart[i] = data[((i * 2) + 1)] * data[((i * 2) + 1)];
dataNew[i] = Math.Sqrt(realPart[i] + imagPart[i]);
}
}
// Get these values for Y Axis as Amplitude
Values.Add(dataNew);
int N = dataNew.Length;
double[] frequencies = new double[N];
for (int i = 0; i < N; i++ )
{
if (i < (N / 2))
{
frequencies[i] = i * SampleRate / N;
}
else if (i >= (N / 2))
{
frequencies[i] = (N - i) * SampleRate / N;
}
//frequencies[i] = (i / N); //*10;
}
// Get the Frequency values for X Axis
Values.Add(frequencies);
Processed Amplitude values are looking like..
0
9030724,08220743
78204971,4566076
11562334,8871099
10855402,9273669
9213306,99124749
39816810,42806
9154491,10446211
10747893,2800474
9744695,14696198
140738122,900694
Derived Frequency from the sample rate are as follows
0
2
5
8
10
13
16
18
21
24
26
29
Finally the graph does not show the correct Freq range & peaks are observed through out the chart, even though the .wav file is a pure sine wave & does not contain any noise. (I´m not able to attach the graph image due to missing reputation, but i´m ready to post it.)
As per Sinus Tone, there should be only one peak around 1KHz, but I´m seeing again sinus like signal. I´ve checked with the different frequencies, but the result does not match with the expectation.
I could not clearly figure out where I´m doing the mistake, whether
Processing Audio Data (Splitting 4 Bytes after Header Info to Double)
Processing data within FFT
or Amplitude/Freq Graph calculation.
I´m ready to provide additional information, if required & any help would be appreciated.

Homography array transformation

I am working on a homography method to copy and expand the relevant points from a rectangle area of depthImg array to a new array, but the bottomright points do not change the homographyImg array. Any ideas, suggestions appreciated. Thanks and happy holidays.
public short[] depthImg;
public int topleftx = 10;
public int toplefty = 20;
public int toprightx = 300;
public int toprighty = 20;
public int bottomleftx = 30;
public int bottomlefty = 200;
public int bottomrightx = 310;
public int bottomrighty = 220;
short[] homographyImg = new short[320 * 240];
for(int ii = 0; ii < 320 * 240; ii++)
{
int xx = ii % 320;
int yy = ii / 320;
int lx =(topleftx + (bottomleftx - topleftx)*(yy/240));
int rx =(toprightx + (bottomrightx - toprightx)*(yy/240));
if (xx < rx & xx > lx)
{
int px = 320*(xx-lx)/(rx-lx);
int ty =(toplefty + (toprighty - toplefty)*(px/320));
int by =(bottomlefty + (bottomrighty - bottomlefty)*(px/320));
if (yy > ty & yy < by)
{
int pxy = 240*(yy-ty)/(by-ty)*320 + px;
homographyImg[pxy] = depthImg[ii];
}
}
}
// I couldn't paste code in the conversation, so I uploaded here. This is a similar array transformation where they modify the corner coordinates to get a correct picture.
for(int yy = 0; yy < newHeight; yy++)
{
for(int xx = 0; xx < newWidth; xx++)
{
int TLidx = (xx * 2) + yy * 2 * width;
int TRidx = (xx * 2 + 1) + yy * width * 2;
int BLidx = (xx * 2) + (yy * 2 + 1) * width;
int BRidx = (xx * 2 + 1) + (yy * 2 + 1) * width;
dst[newWidth- xx - 1 + yy * newWidth] = Color32.Lerp(Color32.Lerp(src[BLidx],src[BRidx],.5F),
Color32.Lerp(src[TLidx],src[TRidx],.5F),.5F);
}
}

Calculating an NxN matrix determinant in C#

How do you calculate the determinant of an NxN matrix C# ?
The OP posted another question asking specifically about 4x4 matrices, which has been closed as an exact duplicate of this question. Well, if you're not looking for a general solution but instead are constrained to 4x4 matrices alone, then you can use this ugly looking but tried-and-true code:
public double GetDeterminant() {
var m = _values;
return
m[12] * m[9] * m[6] * m[3] - m[8] * m[13] * m[6] * m[3] -
m[12] * m[5] * m[10] * m[3] + m[4] * m[13] * m[10] * m[3] +
m[8] * m[5] * m[14] * m[3] - m[4] * m[9] * m[14] * m[3] -
m[12] * m[9] * m[2] * m[7] + m[8] * m[13] * m[2] * m[7] +
m[12] * m[1] * m[10] * m[7] - m[0] * m[13] * m[10] * m[7] -
m[8] * m[1] * m[14] * m[7] + m[0] * m[9] * m[14] * m[7] +
m[12] * m[5] * m[2] * m[11] - m[4] * m[13] * m[2] * m[11] -
m[12] * m[1] * m[6] * m[11] + m[0] * m[13] * m[6] * m[11] +
m[4] * m[1] * m[14] * m[11] - m[0] * m[5] * m[14] * m[11] -
m[8] * m[5] * m[2] * m[15] + m[4] * m[9] * m[2] * m[15] +
m[8] * m[1] * m[6] * m[15] - m[0] * m[9] * m[6] * m[15] -
m[4] * m[1] * m[10] * m[15] + m[0] * m[5] * m[10] * m[15];
}
It assumes you store your vector data in a 16-element array called _values (of double in this case, but float would work too), in the following order:
0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
12, 13, 14, 15
Reduce to upper triangular form, then make a nested loop where you multiply all the values at position i == j together. There you have it.
The standard method is LU decomposition. You may want to use a library instead of coding it yourself. I don't know about C#, but the 40-year standard is LAPACK.
This solution is achieved using row operations. I took an identity matrix of the same dimensions as of my target matrix and then converted the target matrix to the identity matrix such that, every operation which is performed on target matrix, must also be performed to the identity matrix. In last, the target matrix will become identity matrix and the identity matrix will hold the inverse of the target matrix.
private static double determinant(double[,] matrix, int size)
{
double[] diviser = new double[size];// this will be used to make 0 all the elements of a row except (i,i)th value.
double[] temp = new double[size]; // this will hold the modified ith row after divided by (i,i)th value.
Boolean flag = false; // this will limit the operation to be performed only when loop n != loop i
double determinant = 1;
if (varifyRowsAndColumns(matrix, size)) // verifies that no rows or columns are similar or multiple of another row or column
for (int i = 0; i < size; i++)
{
int count = 0;
//this will hold the values to be multiplied by temp matrix
double[] multiplier = new double[size - 1];
diviser[i] = matrix[i, i];
//if(i,i)th value is 0, determinant shall be 0
if (diviser[i] == 0)
{
determinant = 0;
break;
}
/*
* whole ith row will be divided by (i,i)th value and result will be stored in temp matrix.
* this will generate 1 at (i,i)th position in temp matrix i.e. ith row of matrix
*/
for (int j = 0; j < size; j++)
{
temp[j] = matrix[i, j] / diviser[i];
}
//setting up multiplier to be used for multiplying the ith row of temp matrix
for (int o = 0; o < size; o++)
if (o != i)
multiplier[count++] = matrix[o, i];
count = 0;
//for creating 0s at every other position than (i,i)th
for (int n = 0; n < size; n++)
{
for (int k = 0; k < size; k++)
{
if (n != i)
{
flag = true;
matrix[n, k] -= (temp[k] * multiplier[count]);
}
}
if (flag)
count++;
flag = false;
}
}
else determinant = 0;
//if determinant is not 0, (i,i)th element will be multiplied and the result will be determinant
if (determinant != 0)
for (int i = 0; i < size; i++)
{
determinant *= matrix[i, i];
}
return determinant;
}
private static Boolean varifyRowsAndColumns(double[,] matrix, int size)
{
List<double[]> rows = new List<double[]>();
List<double[]> columns = new List<double[]>();
for (int j = 0; j < size; j++)
{
double[] temp = new double[size];
for (int k = 0; k < size; k++)
{
temp[j] = matrix[j, k];
}
rows.Add(temp);
}
for (int j = 0; j < size; j++)
{
double[] temp = new double[size];
for (int k = 0; k < size; k++)
{
temp[j] = matrix[k, j];
}
columns.Add(temp);
}
if (!RowsAndColumnsComparison(rows, size))
return false;
if (!RowsAndColumnsComparison(columns, size))
return false;
return true;
}
private static Boolean RowsAndColumnsComparison(List<double[]> rows, int size)
{
int countEquals = 0;
int countMod = 0;
int countMod2 = 0;
for (int i = 0; i < rows.Count; i++)
{
for (int j = 0; j < rows.Count; j++)
{
if (i != j)
{
double min = returnMin(rows.ElementAt(i), rows.ElementAt(j));
double max = returnMax(rows.ElementAt(i), rows.ElementAt(j));
for (int l = 0; l < size; l++)
{
if (rows.ElementAt(i)[l] == rows.ElementAt(j)[l])
countEquals++;
for (int m = (int)min; m <= max; m++)
{
if (rows.ElementAt(i)[l] % m == 0 && rows.ElementAt(j)[l] % m == 0)
countMod++;
if (rows.ElementAt(j)[l] % m == 0 && rows.ElementAt(i)[l] % m == 0)
countMod2++;
}
}
if (countEquals == size)
{
return false;
// one row is equal to another row. determinant is zero
}
if (countMod == size)
{
return false;
}
if (countMod2 == size)
{
return false;
}
}
}
}
return true;
}
private static double returnMin(double[] row1, double[] row2)
{
double min1 = row1[0];
double min2 = row2[0];
for (int i = 1; i < row1.Length; i++)
if (min1 > row1[i])
min1 = row1[i];
for (int i = 1; i < row2.Length; i++)
if (min2 > row2[i])
min2 = row2[i];
if (min1 < min2)
return min1;
else return min2;
}
private static double returnMax(double[] col1, double[] col2)
{
double max1 = col1[0];
double max2 = col2[0];
for (int i = 1; i < col1.Length; i++)
if (max1 < col1[i])
max1 = col1[i];
for (int i = 1; i < col2.Length; i++)
if (max2 < col2[i])
max2 = col2[i];
if (max1 > max2)
return max1;
else return max2;
}

Generate color gradient in C#

My question here is similar to the question here, except that I am working with C#.
I have two colors, and I have a predefine steps. How to retrieve a list of Colors that are the gradients between the two?
This is an approach that I tried, which didn't work:
int argbMax = Color.Chocolate.ToArgb();
int argbMin = Color.Blue.ToArgb();
var colorList = new List<Color>();
for(int i=0; i<size; i++)
{
var colorAverage= argbMin + (int)((argbMax - argbMin) *i/size);
colorList.Add(Color.FromArgb(colorAverage));
}
If you try the above code, you will find that a gradual increase in argb doesn't correspond to a visual gradual increase in the color.
Any idea on this?
You will have to extract the R, G, B components and perform the same linear interpolation on each of them individually, then recombine.
int rMax = Color.Chocolate.R;
int rMin = Color.Blue.R;
// ... and for B, G
var colorList = new List<Color>();
for(int i=0; i<size; i++)
{
var rAverage = rMin + (int)((rMax - rMin) * i / size);
var gAverage = gMin + (int)((gMax - gMin) * i / size);
var bAverage = bMin + (int)((bMax - bMin) * i / size);
colorList.Add(Color.FromArgb(rAverage, gAverage, bAverage));
}
Oliver's answer was very close... but in my case some of my stepper numbers needed to be negative. When converting the stepper values into a Color struct my values were going from negative to the higher values e.g. -1 becomes something like 254. I setup my step values individually to fix this.
public static IEnumerable<Color> GetGradients(Color start, Color end, int steps)
{
int stepA = ((end.A - start.A) / (steps - 1));
int stepR = ((end.R - start.R) / (steps - 1));
int stepG = ((end.G - start.G) / (steps - 1));
int stepB = ((end.B - start.B) / (steps - 1));
for (int i = 0; i < steps; i++)
{
yield return Color.FromArgb(start.A + (stepA * i),
start.R + (stepR * i),
start.G + (stepG * i),
start.B + (stepB * i));
}
}
Maybe this function can help:
public IEnumerable<Color> GetGradients(Color start, Color end, int steps)
{
Color stepper = Color.FromArgb((byte)((end.A - start.A) / (steps - 1)),
(byte)((end.R - start.R) / (steps - 1)),
(byte)((end.G - start.G) / (steps - 1)),
(byte)((end.B - start.B) / (steps - 1)));
for (int i = 0; i < steps; i++)
{
yield return Color.FromArgb(start.A + (stepper.A * i),
start.R + (stepper.R * i),
start.G + (stepper.G * i),
start.B + (stepper.B * i));
}
}
public static List<Color> GetGradientColors(Color start, Color end, int steps)
{
return GetGradientColors(start, end, steps, 0, steps - 1);
}
public static List<Color> GetGradientColors(Color start, Color end, int steps, int firstStep, int lastStep)
{
var colorList = new List<Color>();
if (steps <= 0 || firstStep < 0 || lastStep > steps - 1)
return colorList;
double aStep = (end.A - start.A) / steps;
double rStep = (end.R - start.R) / steps;
double gStep = (end.G - start.G) / steps;
double bStep = (end.B - start.B) / steps;
for (int i = firstStep; i < lastStep; i++)
{
var a = start.A + (int)(aStep * i);
var r = start.R + (int)(rStep * i);
var g = start.G + (int)(gStep * i);
var b = start.B + (int)(bStep * i);
colorList.Add(Color.FromArgb(a, r, g, b));
}
return colorList;
}
Use double instead of int:
double stepA = ((end.A - start.A) / (double)(steps - 1));
double stepR = ((end.R - start.R) / (double)(steps - 1));
double stepG = ((end.G - start.G) / (double)(steps - 1));
double stepB = ((end.B - start.B) / (double)(steps - 1));
and:
yield return Color.FromArgb((int)start.A + (int)(stepA * step),
(int)start.R + (int)(stepR * step),
(int)start.G + (int)(stepG * step),
(int)start.B + (int)(stepB * step));
Combining this answer with the idea from several other answers to use floating-point steps, here's a complete method snippet for stepping with floating point. (With integer stepping, I had been getting asymmetrical gradient colors in a 16-color gradient from blue to red.)
Important difference in this version: you pass the total number of colors you want in the returned gradient sequence, not the number of steps to take within the method implementation.
public static IEnumerable<Color> GetColorGradient(Color from, Color to, int totalNumberOfColors)
{
if (totalNumberOfColors < 2)
{
throw new ArgumentException("Gradient cannot have less than two colors.", nameof(totalNumberOfColors));
}
double diffA = to.A - from.A;
double diffR = to.R - from.R;
double diffG = to.G - from.G;
double diffB = to.B - from.B;
var steps = totalNumberOfColors - 1;
var stepA = diffA / steps;
var stepR = diffR / steps;
var stepG = diffG / steps;
var stepB = diffB / steps;
yield return from;
for (var i = 1; i < steps; ++i)
{
yield return Color.FromArgb(
c(from.A, stepA),
c(from.R, stepR),
c(from.G, stepG),
c(from.B, stepB));
int c(int fromC, double stepC)
{
return (int)Math.Round(fromC + stepC * i);
}
}
yield return to;
}

Categories