I am trying to make a game in which my character shoots when a loud enough sound is heard true the mic (in Unity). I have however no idea how to start.
Thank you for your help!
You can get the decibels from a Microphone by retrieving the block of the currently playing Microphone's output data with the AudioSource.GetOutputData function. To get the dB from this data, you need sum the data samples then calculate the RMS. That's RMS value you can use to calculate the dB with 20 * Mathf.Log10 (RMS / refVal).
There is a complete example on about this on Unity's post. You can read that for more information and the code below is based on that:
public float rmsVal;
public float dbVal;
public float pitchVal;
private const int QSamples = 1024;
private const float RefValue = 0.1f;
private const float Threshold = 0.02f;
float[] _samples;
private float[] _spectrum;
private float _fSample;
void Start()
{
_samples = new float[QSamples];
_spectrum = new float[QSamples];
_fSample = AudioSettings.outputSampleRate;
}
void Update()
{
AnalyzeSound();
Debug.Log("RMS: " + rmsVal.ToString("F2"));
Debug.Log(dbVal.ToString("F1") + " dB");
Debug.Log(pitchVal.ToString("F0") + " Hz");
}
void AnalyzeSound()
{
GetComponent<AudioSource>().GetOutputData(_samples, 0); // fill array with samples
int i;
float sum = 0;
for (i = 0; i < QSamples; i++)
{
sum += _samples[i] * _samples[i]; // sum squared samples
}
rmsVal = Mathf.Sqrt(sum / QSamples); // rms = square root of average
dbVal = 20 * Mathf.Log10(rmsVal / RefValue); // calculate dB
if (dbVal < -160) dbVal = -160; // clamp it to -160dB min
// get sound spectrum
GetComponent<AudioSource>().GetSpectrumData(_spectrum, 0, FFTWindow.BlackmanHarris);
float maxV = 0;
var maxN = 0;
for (i = 0; i < QSamples; i++)
{ // find max
if (!(_spectrum[i] > maxV) || !(_spectrum[i] > Threshold))
continue;
maxV = _spectrum[i];
maxN = i; // maxN is the index of max
}
float freqN = maxN; // pass the index to a float variable
if (maxN > 0 && maxN < QSamples - 1)
{ // interpolate index using neighbours
var dL = _spectrum[maxN - 1] / _spectrum[maxN];
var dR = _spectrum[maxN + 1] / _spectrum[maxN];
freqN += 0.5f * (dR * dR - dL * dL);
}
pitchVal = freqN * (_fSample / 2) / QSamples; // convert index to frequency
}
Related
Well, my question is pretty simple to understand, I'm trying to create a grid of chunks but on each step, I want to double the distance of a chunk.
Then, this is the code I have:
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class DrawLoDTest : MonoBehaviour
{
private List<Vector3> initialChunks;
public int chunkSize = 16;
public int lodLevels = 2;
public int width = 16;
public int height = 16;
private Color[] colorList;
// Start is called before the first frame update
private void Start()
{
}
// Update is called once per frame
private void Update()
{
}
private void Init()
{
if (initialChunks != null && initialChunks.Count > 0)
return;
colorList = new[] { Color.blue, Color.green, Color.yellow, Color.cyan, Color.magenta, Color.grey };
Debug.Log("Init test!");
initialChunks = new List<Vector3>(16 * 16);
for (var i = 0; i < width * height; i++)
{
var x = i % width;
var y = i / width;
initialChunks.Add(new Vector3(x * chunkSize - width * chunkSize / 2, 0, y * chunkSize - height * chunkSize / 2));
}
//Debug.Log(string.Join(Environment.NewLine, initialChunks.Select(v => v.ToString())));
}
private void OnDrawGizmos()
{
Init();
Gizmos.color = Color.red;
foreach (var chunk in initialChunks)
{
Gizmos.DrawWireCube(chunk, Vector3.one * chunkSize);
}
var ww = 0;
var ww2 = 0;
var sum = 0;
var halfChunkSize = chunkSize / 2;
var halfWidth = width * halfChunkSize;
var halfHeight = height * halfChunkSize;
for (var i = 1; i <= lodLevels; i++)
{
if (ww > 0)
++ww2;
var pow = (int)Mathf.Pow(2, i);
var w = width / pow;
var h = height / pow;
Gizmos.color = colorList[i - 1];
var oddSum = ww * chunkSize * pow;
for (var x = -1 - ww2; x <= w + ww2; x++)
{
for (var n = 0; n <= 1; n++)
{
var sign = n == 0 ? -1 : 1;
var chunk = new Vector3(
halfWidth - x * chunkSize * pow - halfChunkSize,
0,
halfHeight * sign - halfChunkSize - chunkSize * 2 * sign + oddSum * sign + sum * sign);
chunk.x -= pow * halfChunkSize;
for (var j = i + 1; j >= i; j--)
{
chunk.z += chunkSize * (int)Mathf.Pow(2, j) / 2 * sign;
}
Gizmos.DrawWireCube(chunk, Vector3.one * chunkSize * pow);
}
}
// TODO
if ((w + ww2 * 2 + 2) / 2 % 2 != 0)
{
Debug.Log($"[{i}, {ww}, {ww2}] W: {w} + {ww2} * 2 = {w + ww2 * 2} + 2 = {w + ww2 * 2 + 2} / 2 = {(w + ww2 * 2 + 2) / 2}");
if (ww == 0)
{
--i;
++ww;
}
else
{
ww = 0;
sum += oddSum;
}
}
else
{
if (ww2 > 0)
ww2 = 0;
}
}
}
}
A little bit of explanation:
First, I load a 16x16 chunk grid (initialChunks).
Then, I start a loop foreach lod level (I'm testing it for 6 levels).
Then, foreach axis I iterate its position.
Then, in a nested loop foreach axis I give a sign (a loop for 0 to 1, that converts into -1 and 1 for the sign).
Then, and this is the more complex thing, if the following row/column has an odd number of elements then I try to match the current row/column by doubling it, in that way non of the following corner will be half of the previous one, like the image below:
I have two main problems:
For some reason, sum and oddSum variables has the wrong input.
At certain levels, (for i >= 5) the logic is broken.
Now I have an octahedron sphere and I woud like to add noise to it, but I can' t do it because I dont know to much of this,
I think I need to modify the vertex pos but i don't know how.
Here is the script that generate the vertices, If you need More, only Ask (;
Vector3[] vertices = new Vector3[(resolution + 1) * (resolution + 1) * 4 - (resolution * 2 - 1) * 3];
int v = 0, vBottom = 0, t = 0;
for (int i = 0; i < 4; i++)
{
vertices[v++] = Vector3.down;
}
for (int i = 1; i <= resolution; i++)
{
float progress = (float)i / resolution;
Vector3 from, to;
vertices[v++] = to = Vector3.Lerp(Vector3.down, Vector3.up, progress);
for (int d = 0; d < 4; d++)
{
from = to;
to = Vector3.Lerp(Vector3.down, directions[d], progress);
t = CreateLowerStrip(i, v, vBottom, t, triangles);
v = CreateVertexLine(from, to, i, v, vertices);
vBottom += i > 1 ? (i - 1) : 1;
}
vBottom = v - 1 - i * 4;
}
Depending on how exactly your noise should look like the simplest I can imagine would be to simply iterate through the vertices and slightly shift them around by random direction vectors with a random magnitude up to a certain maximum like e.g.
void Randomize(Vector3[] vertices, float maxDistance)
{
for(var i = 0; i< vertices.Length; i++)
{
// Generates a random vactor pointing in any possible direction
var randomDirection = new Vector3(Random.Range(-1f, 1f), Random.Range(-1f, 1f), Random.Range(-1f, 1f)).normalized;
// Generate a random magnitude between 0 and given maxDistance
var randomMagnitude = Random.Range(0, maxDistance);
// Move the current vert the randomMagnitude along the randomDirection
vertices[i] += randomDirection * randomMagnitude;
}
}
Or alternatively you could only move on Vectors between the current vert position and the center (assuming Vector3.zero for now)
void RandomizeFromCenter(Vector3[] vertices, float maxDistance)
{
var center = Vector3.zero;
for(var i = 0; i< vertices.Length; i++)
{
var direction = (vertices[i] - center).normalized;
// Generate a random magnitude between -maxDistance and maxDistance
var randomMagnitude = Random.Range(-maxDistance, maxDistance);
// Move the current vert the randomMagnitude along the randomDirection
vertices[i] += direction * randomMagnitude;
}
}
Project: intelligence scissors.
The first part of the project is to load an image (in the form of RGBPixel 2d array) then to construct a graph of weights to use it later to determine the shortest path between 2 points on the image (the 2 points will be determined by an anchor and a free point..In short i will have the source and the destination points).
I have a function that open the image and return a RGBPixel 2d array already made.Now image is loaded i want to construct the graph to do the i should use a function called CalculatePixelEnergies here is the code
public static Vector2D CalculatePixelEnergies(int x, int y, RGBPixel[,] ImageMatrix)
{
if (ImageMatrix == null) throw new Exception("image is not set!");
Vector2D gradient = CalculateGradientAtPixel(x, y, ImageMatrix);
double gradientMagnitude = Math.Sqrt(gradient.X * gradient.X + gradient.Y * gradient.Y);
double edgeAngle = Math.Atan2(gradient.Y, gradient.X);
double rotatedEdgeAngle = edgeAngle + Math.PI / 2.0;
Vector2D energy = new Vector2D();
energy.X = Math.Abs(gradientMagnitude * Math.Cos(rotatedEdgeAngle));
energy.Y = Math.Abs(gradientMagnitude * Math.Sin(rotatedEdgeAngle));
return energy;
}
This function use CalculateGradientAtPixel, Here is the code in case you want it.
private static Vector2D CalculateGradientAtPixel(int x, int y, RGBPixel[,] ImageMatrix)
{
Vector2D gradient = new Vector2D();
RGBPixel mainPixel = ImageMatrix[y, x];
double pixelGrayVal = 0.21 * mainPixel.red + 0.72 * mainPixel.green + 0.07 * mainPixel.blue;
if (y == GetHeight(ImageMatrix) - 1)
{
//boundary pixel.
for (int i = 0; i < 3; i++)
{
gradient.Y = 0;
}
}
else
{
RGBPixel downPixel = ImageMatrix[y + 1, x];
double downPixelGrayVal = 0.21 * downPixel.red + 0.72 * downPixel.green + 0.07 * downPixel.blue;
gradient.Y = pixelGrayVal - downPixelGrayVal;
}
if (x == GetWidth(ImageMatrix) - 1)
{
//boundary pixel.
gradient.X = 0;
}
else
{
RGBPixel rightPixel = ImageMatrix[y, x + 1];
double rightPixelGrayVal = 0.21 * rightPixel.red + 0.72 * rightPixel.green + 0.07 * rightPixel.blue;
gradient.X = pixelGrayVal - rightPixelGrayVal;
}
return gradient;
}
In my code of graph construction i decided to make a 2d double array to hold the weights, here what i do but it seems to be a wrong construction
public static double [,] calculateWeights(RGBPixel[,] ImageMatrix)
{
double[,] weights = new double[1000, 1000];
int height = ImageOperations.GetHeight(ImageMatrix);
int width = ImageOperations.GetWidth(ImageMatrix);
for (int y = 0; y < height - 1; y++)
{
for (int x = 0; x < width - 1; x++)
{
Vector2D e;
e = ImageOperations.CalculatePixelEnergies(x, y, ImageMatrix);
weights[y + 1, x] = 1 / e.X;
weights[y, x + 1] = 1 / e.Y;
}
}
return weights;
}
an example for an image
an other example for an image
I'm trying to create a terrain editor, that works on runtime. At this point I can raise a square but have problems to raise a circle-shape. I saw this post and thoughts thats the solution for my problem, so I transfered it to my code. It really raise a circle somewhere (where I dont click) but its not efficient, because its updating the whole terrain.
The whole function:
public void RaiseTerrain(Terrain terrain, Vector3 location, float effectIncrement)
{
int offset = areaOfEffectSize / 2;
Vector3 tempCoord = (location - terrain.GetPosition());
Vector3 coord;
coord = new Vector3(
(tempCoord.x / GetTerrainSize().x),
(tempCoord.y / GetTerrainSize().y),
(tempCoord.z / GetTerrainSize().z)
);
Vector3 locationInTerrain = new Vector3(coord.x * terrainHeightMapWidth, 0, coord.z * terrainHeightMapHeight);
int terX = (int)locationInTerrain.x - offset;
int terZ = (int)locationInTerrain.z - offset;
int terXInv = (int)locationInTerrain.x + offset;
int terZInv = (int)locationInTerrain.z + offset;
//This raises a square
//float[,] heights = targetTerrainData.GetHeights(terX, terZ, areaOfEffectSize, areaOfEffectSize);
//for (int xx = 0; xx < areaOfEffectSize; xx++)
//{
// for (int yy = 0; yy < areaOfEffectSize; yy++)
// {
// heights[xx, yy] += (effectIncrement * Time.smoothDeltaTime);
// }
//}
//targetTerrainData.SetHeights(terX, terZ, heights);
//This raises a circle
float[,] heights = targetTerrainData.GetHeights(0, 0, terrainHeightMapWidth, terrainHeightMapHeight);
for (int xx = terX; xx < terXInv; xx++)
{
for (int yy = terZ; yy < terZInv; yy++)
{
float currentRadiusSqr = (new Vector2(locationInTerrain.x, locationInTerrain.z) - new Vector2(xx, yy)).sqrMagnitude;
if(currentRadiusSqr < offset*offset)
{
heights[xx, yy] += (effectIncrement * Time.smoothDeltaTime);
}
}
}
targetTerrainData.SetHeights(0, 0, heights);
}
All following lines are just excerpts from above.
I guess my problem is this line:
float[,] heights = targetTerrainData.GetHeights(0, 0, terrainHeightMapWidth, terrainHeightMapHeight);
Because raising a square works fine. I just update a specific area, so its updating the terrain without dropping framerate:
float[,] heights = targetTerrainData.GetHeights(terX, terZ, areaOfEffectSize, areaOfEffectSize);
[...]
targetTerrainData.SetHeights(terX, terZ, heights);
So my question is, how can I raise a circle and update the changes like I did with raising a square? Update only the changed heights?
I tried to use this lines from the square:
float[,] heights = targetTerrainData.GetHeights(terX, terZ, areaOfEffectSize, areaOfEffectSize);
[...]
targetTerrainData.SetHeights(terX, terZ, heights);
in comination with:
for (int xx = terX; xx < terXInv; xx++)
{
for (int yy = terZ; yy < terZInv; yy++)
{
float currentRadiusSqr = (new Vector2(locationInTerrain.x, locationInTerrain.z) - new Vector2(xx, yy)).sqrMagnitude;
if(currentRadiusSqr < offset*offset)
{
heights[xx, yy] += (effectIncrement * Time.smoothDeltaTime);
}
}
}
but it didnt work. Im getting an error:
IndexOutOfRangeException: Index was outside the bounds of the array.
instead of heights[xx, yy] += (effectIncrement * Time.smoothDeltaTime);
try heights[yy, xx] += (effectIncrement * Time.smoothDeltaTime); this seems to work for me
int offset = radious / 2;
int mouseX = (int)((point.x / terrainData.size.x) * heightmapWidth);
int mouseZ = (int)((point.z / terrainData.size.z) * heightmapHeight);
int terX = mouseX - offset;
int terZ = mouseZ - offset;
int terXInv = mouseX + offset;
int terZInv = mouseZ + offset;
float[,] heights = terrainData.GetHeights(0, 0, heightmapWidth, heightmapHeight);
for (int xx = terX; xx < terXInv; xx++)
{
for (int yy = terZ; yy < terZInv; yy++)
{
float currentRadiusSqr = (new Vector2(mouseX, mouseZ) - new Vector2(xx, yy)).sqrMagnitude;
if (currentRadiusSqr < offset * offset)
{
heights[yy, xx] += (strength * Time.smoothDeltaTime);
}
}
}
terrainData.SetHeights(0, 0, heights);
Take a look at this link.
public struct COMPLEX
{
public double real, imag;
public Complex(double x, double y)
{
real = x;
imag = y;
}
public float Magnitude()
{
return ((float)Math.Sqrt(real * real + imag * imag));
}
public float Phase()
{
return ((float)Math.Atan(imag / real));
}
}
public static COMPLEX[,] ApplyFilterHMMFreqDomain(COMPLEX[,] FFTData,
float rH, float rL, float Sigma, float Slope)
{
COMPLEX[,] Output = new COMPLEX[FFTData.GetLength(0), FFTData.GetLength(1)];
int i, j, W, H;
W = FFTData.GetLength(0);
H = FFTData.GetLength(1);
double Weight;
//Taking FFT of Gaussian HPF
double[,] GaussianHPF =
GenerateGaussianKernelHPF(FFTData.GetLength(0), Sigma, Slope, out Weight);
//Variables for FFT of Gaussian Filter
COMPLEX[,] GaussianHPFFFT;
for (i = 0; i <= GaussianHPF.GetLength(0) - 1; i++)
for (j = 0; j <= GaussianHPF.GetLength(1) - 1; j++)
{
GaussianHPF[i, j] = GaussianHPF[i, j];// / Weight;
}
FFT GaussianFFTObject = new FFT(GaussianHPF);
GaussianFFTObject.ForwardFFT(GaussianHPF);
//Shifting FFT for Filtering
GaussianFFTObject.FFTShift();
GaussianHPFFFT = GaussianFFTObject.FFTShifted;
for (i = 0; i <= GaussianHPF.GetLength(0) - 1; i++)
for (j = 0; j <= GaussianHPF.GetLength(1) - 1; j++)
{
GaussianHPFFFT[i, j].real = (rH - rL) * GaussianHPFFFT[i, j].real + rL;
GaussianHPFFFT[i, j].imag = (rH - rL) * GaussianHPFFFT[i, j].imag + rL;
}
// Applying Filter on the FFT of the Log Image by Multiplying in Frequency Domain
Output = MultiplyFFTMatrices(GaussianHPFFFT, FFTData);
return Output;
}
After detail experiment, I found no or insignificant effect of the following snippet of source code:
for (i = 0; i <= GaussianHPF.GetLength(0) - 1; i++)
for (j = 0; j <= GaussianHPF.GetLength(1) - 1; j++)
{
GaussianHPFFFT[i, j].real = (rH - rL) * GaussianHPFFFT[i, j].real + rL;
GaussianHPFFFT[i, j].imag = (rH - rL) * GaussianHPFFFT[i, j].imag + rL;
}
Can anyone explain why this part of the code is/isn't important?
That snippet of code applies the same linear transformation to each value of the frequency domain. This is equivalent to applying the same transformation to each pixel in the spatial domain (the Fourier transform is linear)
The linear transformation and the convolution commute, as is easy to see since the convolution is a multiplication in the frequency domain, you can clearly swap these two operations there.
Thus, you are applying this constant, linear transformation to the output of the filter. If you display the output by linearly scaling the values to the output range, you will undo any effect of this transformation.
My guess is that it’s there to scale the output spatial-domain image to a meaningful range.