I am trying to create an audio visualizer in the Unity game engine using C#.
I am also using the FMOD plugin which has functions for retrieving the audio spectrum of a playing sound (aka the volume of a sound across a range of frequencies).
FMOD returns this data as a struct containing a jagged array, with the first array being the audio channel (0 = left, 1 = right), and the second array being the volumes for each frequency range, being a float value between 0 and 1.
I need to copy the values in these arrays into my own arrays, for the purposes of altering and displaying the data. I am currently using a for-loop to iterate through the array and assign the values from the left and right channels to new arrays, as well as another array that finds the average volume:
private int SAMPLE_SIZE = 512;
private float[] spectrumLeft;
private float[] spectrumRight;
public float[] spectrumTotal;
void Start () {
spectrumLeft = new float[SAMPLE_SIZE];
spectrumRight = new float[SAMPLE_SIZE];
spectrumTotal = new float[SAMPLE_SIZE];
}
void Update () {
System.IntPtr data;
uint length;
result = spectrumFilter.getParameterData ((int)FMOD.DSP_FFT.SPECTRUMDATA, out data, out length);
FMOD.DSP_PARAMETER_FFT spectrumBuffer = new FMOD.DSP_PARAMETER_FFT ();
spectrumBuffer = (FMOD.DSP_PARAMETER_FFT) Marshal.PtrToStructure(data,typeof (FMOD.DSP_PARAMETER_FFT));
for (int i = 0; i < SAMPLE_SIZE; i++) {
spectrumLeft[i] = spectrumBuffer.spectrum[0][i];
spectrumRight[i] = spectrumBuffer.spectrum[1][i];
spectrumTotal[i] = (spectrumLeft[i] + spectrumRight[i]) / 2f;
}
}
THE PROBLEM: The code running in the for-loop above is incredibly slow (It maxes out my CPU core and causes the program to run slowly, when I comment out the contents of the loop the CPU usage is below 1%).
Since FMOD returns the data as a struct in this format I cannot change how I retrieve the data.
Is there a better/faster way of retrieving values from a struct?
Note that I am relatively new to C# and I may be missing something obvious
Ok, very simple fix I overlooked:
private int SAMPLE_SIZE = 512;
private float[][] spectrum;
public float[] spectrumTotal;
void Start () {
spectrum = new float[2][];
spectrum[0] = new float[SAMPLE_SIZE];
spectrum[1] = new float[SAMPLE_SIZE];
spectrumTotal = new float[SAMPLE_SIZE];
}
void Update () {
System.IntPtr data;
uint length;
result = spectrumFilter.getParameterData ((int)FMOD.DSP_FFT.SPECTRUMDATA, out data, out length);
FMOD.DSP_PARAMETER_FFT spectrumBuffer = new FMOD.DSP_PARAMETER_FFT ();
spectrumBuffer = (FMOD.DSP_PARAMETER_FFT) Marshal.PtrToStructure(data,typeof (FMOD.DSP_PARAMETER_FFT));
spectrum = spectrumBuffer.spectrum;
for (int i = 0; i < SAMPLE_SIZE; i++) {
spectrumTotal[i] = (spectrum[0][i] + spectrum[1][i]) / 2f;
}
}
Rather than iterating through the entire array and assigning each value one by one, I simply declared my own jagged array with the same size as the one in the struct and copied it all over in one go. Then I can loop through them and find the average volume.
I am however still interested in why it takes so much longer to get a value from an array in a struct, compared to just another local array. Will do some more research.
Silly mistake now that I see it but thanks, was amazed at how quickly people posted helpful responses.
Related
Say I have this 2D array set up that shows the rotation state of the current Tetris block. How would I go about pasting these values into another bigger 2D array at any specified position? In this case the piece array is four blocks wide and tall, with a map that's ten blocks wide and 20 blocks tall. The value 0 in the example below stands for empty space, and 1 stands for a block piece.
// Array of block
blockMap = new int[4,4]{
{ 0,1,0,0},
{ 0,1,0,0},
{ 0,1,0,0},
{ 0,1,0,0},
};
You would write a double loop, copying each value to the larger array, using an offset to position the values. I.e. something like
blockMap = new int[4,4]{
{ 0,1,0,0},
{ 0,1,0,0},
{ 0,1,0,0},
{ 0,1,0,0},
};
var myLargeArray = new int[64, 64];
var offsetx = 3;
var offsety = 4;
// Note: GetLength is slow, so store values before looping
var sizex = blockMap.GetLength(0);
var sizey = blockMap.GetLength(1);
for(var x = 0; x < sizex; x++){
for(var y = 0; y < sizey; y++){
// Note: you might want to check if the target indices are valid
myLargeArray[x + offsetx, y + offsety] = blockMap[x, y];
}
}
I usually recommend writing your own 2D-array class that uses a 1D-array as storage, rather than using the build in multidimensional array. The reasons being that it is often useful to have data in a 1D array when interacting with other systems, and that the multidimensional array stores values column-major, while row-major is more common, at least in image processing. But these points are more relevant when dealing with larger amounts of data.
I am stuck with this two dimension array with Unity framework coding with c#. I am trying to create a Checker game with an Intelligent agent. So I trying to get all the pieces and passing to the Alpha Beta algorithm and get the maximum amount of Opponent side. But in order to do that I have to check each piece on my board. I am using 8by8 board on my game. So I used
Piece[,] pieces = new Pieces[8,8];
code in order to store the pieces. Now I want to change one piece and retrieve the pieces.
I tried in several ways and experimented, But I could not found any to take this array and change one generic object and retrieve the list with the changes.
Please help me to solve this. Thanks in advance.
I written down here about what I experimented. Any suggestions about this matter or did I do something wrong?....
internal static void TryMoveInImaginaryWay(Piece[,] piece)
{
int x = 0;
int y =2;
int x1 = 1;
int y1 = 3;
Moves move = new Moves();
move.X = x;
move.Y = y;
move.X1 = x1;
move.Y1 = y1;
// Copping the pieces state
Piece p = piece[x, y];
//I am creating a list with those pieces
List<Piece> temppiecelist = new List<Piece>();
foreach (Piece pi in piece)
{
if (pi == p)
{
piece[x1, y1] = p;
temppiecelist.Add(null);
}
else
{
temppiecelist.Add(pi);
}
}
Piece[,] piek = temppiecelist; // can't convert it says Cannot Implicitly convert type "System.Collection.Generic.List<Piece>' to Piece[*,*]'"
// I know i can't convert like this, but any ideas.
//Piece[,] piek = new Piece[8, 8];
I am asking about , Is there any way to change above Piece[,] array list values. And I want the same format as output.
It looks like what you are trying to do is make a copy of the board and then make a move on that board.
Why on you would get a List involved I cannot figure out; what led you to believe that a List was the right solution? I am interested to know; by learning why people come to bad conclusions about program writing, I can help them write better programs.
To make a mutated copy of the board, just copy the board and mutate the copy:
internal static void TryMoveInImaginaryWay(Piece[,] original)
{
int x0 = 0;
int y0 = 2;
int x1 = 1;
int y1 = 3;
Piece[,] copy = (Piece[,])original.Clone();
copy[x1,y1] = copy[x0,y0];
copy[x0,y0] = null;
And you're done.
How can we free GPU memory of an array with AleaGpu ? (on the GPU card)
Inside the function/sub, if we want to free GPU memory of array dOutputs,dInputs, how should we do it ?
1/ Will "dOutputs.Dispose();
dInputs.Dispose();" free the GPU memory ?
2/ Does a "GC.Collect()" for GPU exist ? is it necessary ?
3/ With AleaGpu, do we have a command to free to totaly the GPUmemory ?
private void button3_Click(object sender, EventArgs e)
{
textBox3.Text = "";
var worker = Worker.Default;
const int rows = 10;
const int cols = 5;
var rng = new Random();
var inputs = new double[rows, cols];
for (var row = 0; row < rows; ++row)
{
for (var col = 0; col < cols; ++col)
{
inputs[row, col] = rng.Next(1, 100);
}
}
var dInputs = worker.Malloc(inputs);
var dOutputs = worker.Malloc<double>(rows, cols);
var lp = new LaunchParam(1, 1);
worker.Launch(Kernel, lp, dOutputs.Ptr, dInputs.Ptr, rows, cols);
var outputs = new double[rows, cols];
dOutputs.Gather(outputs);
Assert.AreEqual(inputs, outputs);
dOutputs.Dispose();
dInputs.Dispose();"
}
3/ As GPU card have a limited Memory, we need to use Single/Int16/Int32 instead of double.
I tried :
var inputs = new Single[rows, cols];
var dOutputs = worker.Malloc<Single>(rows, cols);
var inputs2 = new Int16[rows, cols];
but
worker.Launch(Kernel, lp, dOutputs.Ptr, dInputs.Ptr, rows, cols);
don't to take it. I get error "there is some invalid argument" .
How can we make worker.Launch(Kernel, lp, ...) take Int16,Int32 and single ?
The type returned by Worker.Malloc() is DeviceMemory, this represents one memory allocation on Gpu. It is disposable, so you can dispose it yourself or let GC to clean it. But note, if you rely on GC to collect, there is delay (it is done in a GC thread), and since Gpu memory is all pinned memory (cannot be swapped to disk), so it is recommended that you dispose it explicitly. To make code easier, you can use C# "using" keyword.
Of course, Alea Gpu works with those types, the problem you met is because you need specify the exact type. Note, a 1.0 is of type double, and 1.0f is of type single. The reason is, the kernel function is provided to worker launch method by delegate, so you need specify the correct data type to help it to find the kernel method. Implicit type conversion doesn't work here. For numeric literal you can reference here.
I did a small example code, and it works:
static void Kernel(deviceptr<float> data, float value)
{
data[0] = value;
}
static void Kernel(deviceptr<short> data, short value)
{
data[0] = value;
}
static void Main(string[] args)
{
var worker = Worker.Default;
var lp = new LaunchParam(1, 1);
using (var dmemSingle = worker.Malloc<float>(1))
using (var dmemShort = worker.Malloc<short>(1))
{
worker.Launch(Kernel, lp, dmemSingle.Ptr, 4.1f);
worker.Launch(Kernel, lp, dmemShort.Ptr, (short)1);
}
}
You can have overload definitions on the GPU ! this is excellent.
Like a regular code on CPU, you can have more than one definition for the same function on the GPU.
By coding a loop with "using", it is releasing the memory on the GPU as soon as you get out of the loop. Excellent !!!!!
Thank you
Issue:
Im trying to use my graphics card to do some computation using cudafy.net. Ive ran 2 versions of my kernel now and i keep getting an errors at specific intervals ie every 2nd location in the array is 0.0 but should be something much larger. Below is a table of what the GPU returns vs what the correct value is. Note: I've read that comparing floats isnt ideal but getting 0.0 when i should be getting something as large as 6.34419e17 seems wrong.
I GPU Correct Value
16,777,217 0.0 6.34419E17
16,777,219 0.0 6.34419E17
... ... .....
From quickly scanning through them, they seem to be occurring at every 2nd i value.
Checked thus far:
Ive also ran the below code at a different start value as i believed it may be an issue with the data but i still get the same i value for each error.
Ive also changed the order in which the memory is allocated onto the GPU but that doesnt seem to affect the results. Note: since im debugging in VS, im not explicitly clearing the memory on the GPU after i stop. Is this being cleared once i stop debugging? The error is still present once i restart my pc.
Graphics Card:
My graphics card is as follows: EVGA GTX 660 SC.
Code:
My kernel: (Note: i have several variables which arent used below but i havent removed since i wanted to remove 1 thing at a time in order to nail down whats causing this error)
[Cudafy]
public static void WorkerKernelOnGPU(GThread thread, float[] value1, float[] value2, float[] value3, float[] dateTime, float[,] output)
{
float threadIndex = thread.threadIdx.x;
float blockIndex = thread.blockIdx.x;
float threadsPerBlock = thread.blockDim.x;
int tickPosition = (int)(threadIndex + (blockIndex * threadsPerBlock));
//Check to ensure threads dont go out of range.
if (tickPosition < dateTime.Length)
{
output[tickPosition, 0] = dateTime[tickPosition];
output[tickPosition, 1] = -1;
}
}
Below is the segment of code which im using to call the Kernel and then check the results.
CudafyModule km = CudafyTranslator.Cudafy();
_gpu = CudafyHost.GetDevice(eGPUType.Cuda);
_gpu.LoadModule(km);
float[,] Output = new float[SDS.dateTime.Length,2];
float[] pm = new float[]{0.004f};
//Otherwise need to allocate then specify the pointer in the CopyToDevice so it know which pointer to add data to
float[] dev_tpc = _gpu.CopyToDevice(pm);
float[] dev_p = _gpu.CopyToDevice(SDS.p);
float[] dev_s = _gpu.CopyToDevice(SDS.s);
float[,] dev_o = _gpu.CopyToDevice(Output);
float[] dev_dt = _gpu.CopyToDevice(SDS.dateTime);
dim3 grid = new dim3(20000, 1, 1);
dim3 block = new dim3(1024, 1, 1);
Stopwatch sw = new Stopwatch();
sw.Start();
_gpu.Launch(grid, block).WorkerKernelOnGPU(dev_tpc,dev_p, dev_s, dev_dt, dev_o);
_gpu.CopyFromDevice(dev_o, Output);
sw.Stop(); //0.29 seconds
string resultGPU = sw.Elapsed.ToString();
sw.Reset();
//Variables used to record errors.
bool failed = false;
float[,] wrongValues = new float[Output.Length, 3];
int counterError = 0;
//Check the GPU values are as expected. If not record GPU value, Expected value, position.
for (int i = 0; i < 20480000; i++)
{
float gpuValue = Output[i, 0];
if (SDS.dateTime[i] == gpuValue) { }
else
{
failed = true;
wrongValues[counterError, 0] = gpuValue;
wrongValues[counterError, 1] = SDS.dateTime[i];
wrongValues[counterError, 2] = (float)i;
counterError++;
}
}
I only have a single graphics card at my disposal atm so i cant quickly check to see if its an error with the card or not. The card is less then 8 months old and was new when bought.
Any ideas on what could be causing the above error??
Thanks for your time.
Edit:
Just tried to reduce my gtx 660 to the stock speeds of a 660. Still experiencing the error though.
Edit2 Ive used _gpu.FreeMemory; to determine if i was exceeding the cards memory. I still have 1,013,202,944 bytes left though.
Edit3 Ive just changed the datatype of the output array to long instead of float. I now seem to have just over 500MB of free space on the card yet i still get the wrong results from the same value ie i = 16,777,217. I guess this seems to suggest it possible something to do with the index thats the issue??
float threadIndex = thread.threadIdx.x;
float blockIndex = thread.blockIdx.x;
float threadsPerBlock = thread.blockDim.x;
int tickPosition = (int)(threadIndex + (blockIndex * threadsPerBlock));
The issue was that fact i was using float for ThreadIndex etc. Once this was changed to int, the issue was resolved.
Time for this fool to get some time away from the pc.
I'm sifting through some of my old bugs and while reviewing some nasty code I realized that my averaging or smoothing algorithm was pretty bad. I did a little research which led me to the "running mean" - makes sense, pretty straightforward. I was thinking through a possible implementation and realized that I don't know which collection would provide the type of "sliding" functionality that I need. In other words, I need to push/add an item to the end of the collection and then also pop/remove the first item from the collection. I think if I knew what this was called I could find the correct collection but I don't know what to search for.
Ideally a collection where you set the max size and anything added to it that exceeds that size would pop off the first item.
To illustrate, here is what I came up with while messing around:
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
LinkedList<int> samples = new LinkedList<int>();
// Simulate packing the front of the samples, this would most like be a pre-averaged
// value from the raw samples
for (int i = 0; i < 10; i++)
{
samples.AddLast(0);
}
for (int i = 0; i < 100; i++)
{
// My attempt at a "sliding collection" - not really sure what to call it but as
// an item is added the first item is removed
samples.RemoveFirst();
samples.AddLast(i);
foreach (int v in samples)
{
Console.Write("{0:000} ", v);
}
Console.WriteLine(String.Empty);
}
Console.ReadLine();
}
}
}
As you can see I am manually handling the removal of the first item. I'm just asking if there is a standard collection that is optimized for this type of use?
It appears that you're looking for a Circular Buffer. Here's a .NET implementation on CodePlex. You may also want to look at this question: How would you code an efficient Circular Buffer in Java or C#?
From the sample you've provided, it isn't clear how exactly this relates to an online-mean algorithm. If the only operation allowed on the buffer is to append; it should be trivial to cache and update the "total" inside the buffer (add the new value, subtract the removed one); making the maintaining of the mean an O(1) operation for every append. In this case, the buffer is effectively holding the Simple Moving Average (SMA) of a series.
Have you had a look at Queue Class
Does a List satisfy your needs?
List<String> myList = new List<String>();
myList.Add("Something to the end");
myList.RemoveAt(0);
#Ani - I'm creating a new Answer instead of comment because I have some code to paste. I took a swing at a dead simple object to assist with my running mean and came up with the following:
class RollingMean
{
int _pos;
int _count;
double[] _buffer;
public RollingMean(int size)
{
_buffer = new double[size];
_pos = 0;
_count = 0;
}
public RollingMean(int size, double initialValue)
: this(size)
{
// Believe it or not there doesn't seem to be a better(performance) way...
for (int i = 0; i < size; i++)
{
_buffer[i] = initialValue;
}
_count = size;
}
public double Push(double value)
{
_buffer[_pos] = value;
_pos = (++_pos > _buffer.Length - 1) ? 0 : _pos;
_count = Math.Min(++_count, _buffer.Length);
return Mean;
}
public double Mean
{
get
{
return _buffer.Sum() / _count;
}
}
}
I'm reading 16 channels of data from a data acquisition system so I will just instantiate one of these for each channel and I think it will be cleaner than managing a multi-dimensional array or separate set of buffer/postition for each channel.
Here is sample usage for anyone interested:
static void Main(string[] args)
{
RollingMean mean = new RollingMean(10, 7);
mean.Push(3);
mean.Push(4);
mean.Push(5);
mean.Push(6);
mean.Push(7.125);
Console.WriteLine( mean.Mean );
Console.ReadLine();
}
I was going to make the RollingMean object a generic rather than lock into double but I couldn't find a generic constraint to limit the tpye numerical types. I moved on, gotta get back to work. Thanks for you help.