Specifying Duration of sound(tone) created with WaveTone() in NAudio - c#

I want to play a sound(tone) at specified volumes and frequencies but want the duration of playback fixed say 2 seconds.
My code is similar to one given here.
double freq, volume;
WaveTone tone = new WaveTone(freq, volume);
stream = new BlockAlignReductionStream(tone);
output = new DirectSoundOut();
output.Init(stream);
output.Play();
I tried to use latency in DirectSoundOut() above but it did not work as desired. I have to change freq and volume dynamically for each playback.
I need to know the exact duration of playback of tone.

The WaveTone class (assuming you're using one of the ones I just googled) probably provides an endless stream of data. If you want to limit the output to a specific duration you'll need to either load a specific amount of data into another buffer/stream or modify the WaveTone class to stop producing data past the duration.
Something like this:
class WaveTone : WaveStream
{
readonly WaveFormat Format;
public readonly double Frequency;
public readonly double Amplitude;
public readonly double Duration;
readonly long streamLength;
long pos;
const double timeIncr = 1 / 44100.0;
readonly double sinMult;
public WaveTone(double freq, double amp)
: this(freq, amp, 0)
{ }
public WaveTone(double freq, double amp, double dur)
{
Format = new WaveFormat(44100, 16, 1);
Frequency = freq;
Amplitude = Math.Min(1, Math.Max(0, amp));
Duration = dur;
streamLength = Duration == 0 ? long.MaxValue : (long)(44100 * 2 * Duration);
pos = 0;
sinMult = Math.PI * 2 * Frequency;
}
public override WaveFormat WaveFormat
{
get { return Format; }
}
public override long Length
{
get { return streamLength; }
}
public override long Position
{
get { return pos; }
set { pos = value; }
}
public override int Read(byte[] buffer, int offset, int count)
{
if (pos >= streamLength)
return 0;
int nSamples = count / 2;
if ((pos + nSamples * 2) > streamLength)
nSamples = (int)(streamLength - pos) / 2;
double time = pos / (44100 * 2.0);
int rc = 0;
for (int i = 0; i < nSamples; i++, time += timeIncr, ++rc, pos += 2)
{
double val = Amplitude * Math.Sin(sinMult * time);
short sval = (short)(Math.Round(val * (short.MaxValue - 1)));
buffer[offset + i * 2] = (byte)(sval & 0xFF);
buffer[offset + i * 2 + 1] = (byte)((sval >> 8) & 0xFF);
}
return rc * 2;
}
}

Related

Unable to get Yolov4-Tiny working with Windows.AI.MachineLearning APIs

I am implementing Yolov4-Tiny (onnx model found here) in Unity with the Windows ML APIs. I can load the model, and begin a session with no issue. I am using a VideoFrame (sized to 416x416) as the input and can access the two output Tensors. The problems arise when I begin to parse the output Tensors. With a confidence threshold of .5, I get between 700 and 1000 detections each frame, way way more than I expect. Also, the bboxes appear to be very small. The NMS and IOU functions below are nearly verbatim from here so I am not using anchors for the bounding boxes. I believe my issues is in the NMS and IOU functions, but I cannot locate the problem. My gut tells me I am manipulating the output Tensors incorrectly. Any ideas?
private List<DetectionResult> ParseResult(float[] boxes, float[] classes)
{
int c_values = 80;
int c_boxes = boxes.Length / 4;
int c_classNames = classes.Length / c_values;
float confidence_threshold = 0.5f;
List<DetectionResult> detections = new List<DetectionResult>();
for (int i_box = 0; i_box < c_classNames; i_box++)
{
float max_prob = 0.0f;
int label_index = -1;
for (int j_confidence = 0; j_confidence < c_values; j_confidence++)
{
int index = i_box * c_values + j_confidence;
if (Sigmoid(classes[index]) > max_prob)
{
max_prob = Sigmoid(classes[index]) ;
label_index = j_confidence;
}
}
if (max_prob > confidence_threshold)
{
//Debug.Log(_labels[label_index]);
List<float> bbox = new List<float>();
bbox.Add(boxes[(i_box * 4) + 0] * 416);
bbox.Add(boxes[(i_box * 4) + 1] * 416);
bbox.Add(boxes[(i_box * 4) + 2] * 416);
bbox.Add(boxes[(i_box * 4) + 3] * 416);
detections.Add(new DetectionResult()
{
label = _labels[label_index],
bbox = bbox,
prob = max_prob
});
}
}
private List<DetectionResult> NMS(IReadOnlyList<DetectionResult> detections,
float IOU_threshold = 0.45f,
float score_threshold = 0.3f)
{
List<DetectionResult> final_detections = new List<DetectionResult>();
for (int i = 0; i < detections.Count; i++)
{
int j = 0;
for (j = 0; j < final_detections.Count; j++)
{
if (ComputeIOU(final_detections[j], detections[i]) > IOU_threshold)
{
break;
}
}
if (j == final_detections.Count)
{
final_detections.Add(detections[i]);
}
}
return final_detections;
}
private float ComputeIOU(DetectionResult DRa, DetectionResult DRb)
{
float ay1 = DRa.bbox[0];
float ax1 = DRa.bbox[1];
float ay2 = DRa.bbox[2];
float ax2 = DRa.bbox[3];
float by1 = DRb.bbox[0];
float bx1 = DRb.bbox[1];
float by2 = DRb.bbox[2];
float bx2 = DRb.bbox[3];
// determine the coordinates of the intersection rectangle
float x_left = Math.Max(ax1, bx1);
float y_top = Math.Max(ay1, by1);
float x_right = Math.Min(ax2, bx2);
float y_bottom = Math.Min(ay2, by2);
if (x_right < x_left || y_bottom < y_top)
return 0;
float intersection_area = (x_right - x_left) * (y_bottom - y_top);
float bb1_area = (ax2 - ax1) * (ay2 - ay1);
float bb2_area = (bx2 - bx1) * (by2 - by1);
float iou = intersection_area / (bb1_area + bb2_area - intersection_area);
return iou;
}

Unity Converting Microphone input into Hertz

I'm working on a Unity app that has some Microphone controls. At one point, I have to convert the Microphone input into Hertz (Hz) values and show them to the user. Now, I did some research and I made the following script for this purpose:
int amountSamples = 1024;
void Start ()
{
_fSample = AudioSettings.outputSampleRate;
}
void Update() {
if (focused && Initialized) {
if (Microphone.IsRecording(selectedDevice) && recording) {
spectrumData = GetSpectrumAnalysis();
if (spectrumCurve.keys.Length <= spectrumData.Length) {
float keyTimeValue = 0;
float currentHighestKeyTime = 0;
//create a curvefield if none exists
spectrumCurve = new AnimationCurve();
for (int t = 0; t < spectrumData.Length; t++) {
spectrumCurve.AddKey(1 / spectrumData.Length + t, spectrumData[t]);
spectrumCurve.MoveKey(1 / spectrumData.Length + t, new Keyframe(1 / spectrumData.Length + t, keyTimeValue = spectrumData[t])); //update keyframe value
if (keyTimeValue > currentHighestKeyTime) {
currentHighestKeyTime = keyTimeValue;
}
}
HighestKeyTimeValue = currentHighestKeyTime;
float freqN = HighestKeyTimeValue;
float f = freqN * (_fSample / 2) / amountSamples;
Debug.Log(f); //hz
}
}
}
audioSource.volume = 1;
}
And the GetSpectrumAnalysis()
public float[] GetSpectrumAnalysis ()
{
float[] dataSpectrum = new float[amountSamples];
audioSource.GetSpectrumData (dataSpectrum, 0, FFTWindow.BlackmanHarris);
for (int i = 0; i <= dataSpectrum.Length - 1; i++)
{
dataSpectrum[i] = Mathf.Abs (dataSpectrum[i] * sensitivity);
}
return dataSpectrum;
}
Now, with this code, the Hz value should be calculated in float f, it does work but the Hz values aren't too accurate, for example, I'm getting 400-500 Hz where I should get around 880 Hz. Similarly I'm getting 130 Hz instead of 220 Hz, etc.. So, I have 2 issues: I'm getting less Hz then I should and the Hz value is jumping too much and too fast so it's not consistent even if the sound playing is constant. Any idea how to improve this code? Where did I made a mistake?
EDIT
Check my answer for the solution.
Ok, nevermind, I found the solution, maybe this will help someone stumbling across this thread, change GetSpectrumAnalysis method to this:
public float test() {
float Threshold = 0.02f;
float[] dataSpectrum = new float[amountSamples];
audioSource.GetSpectrumData(dataSpectrum, 0, FFTWindow.BlackmanHarris); //Rectangular
float maxV = 0;
var maxN = 0;
for (int i = 0; i < amountSamples; i++) {
if (!(dataSpectrum[i] > maxV) || !(dataSpectrum[i] > Threshold)) {
continue;
}
maxV = dataSpectrum[i];
maxN = i; // maxN is the index of max
}
float freqN = maxN; // pass the index to a float variable
if (maxN > 0 && maxN < amountSamples - 1) { // interpolate index using neighbours
var dL = dataSpectrum[maxN - 1] / dataSpectrum[maxN];
var dR = dataSpectrum[maxN + 1] / dataSpectrum[maxN];
freqN += 0.5f * (dR * dR - dL * dL);
}
return freqN * (_fSample / 2) / amountSamples; // convert index to frequency
}
Then just call this in the update method like this:
Text.text = test().ToString("00");
For more info check out this thread: Unity answers

calculating reduced level of details of a mesh

I am attempting to create randomised terrain meshes as I have done so in the screenshot below:
However, the issue I am facing is when attempting to reduces the number of triangles and vertices (Level of Detail).
I understand that to do this I can just skip over vertices.
for example:
The above mesh is full detail in that the vertices are generated like so:
0->1->2->3->4->5->6->7->8->9->...
and to generate a lower level of detail i can skip vertices as long as the skipping of vertices does not exceed the length of vertices so i could do the following generation to lower detail:
0->2->4->6->8->10->12->14->16->...
or:
0->4->8->12->16->20->24->28->32->...
Using a 2D array and a nested loop makes this trivial as each iteration on 2D coordinates x/y can be incremented by the increment: 1, 2, 4, 8, however, i am dealing with 2D arrays in 1D format.
I have the following code which executes and almost correctly generates the above mesh in the screenshot above.
Unfortunately it does seem to be missing one line of of vertices on the top left (3d z axis) as seen below:
One caveat to the Execute(int, int) method below is that any access to the NativeArray which is not labeled [ReadOnly] will throw an exception if the array is accessing indexes outside of it's batch size.
public struct int6
{
public int a, b, c, d, e, f;
public int6(int a, int b, int c, int d, int e, int f) { this.a = a; this.b = b; this.c = c; this.d = d; this.e = e; this.f = f; }
}
public class MeshGeneratorJob2
{
[ReadOnly] public static int width = 241;
[ReadOnly] public static int height = 241;
[ReadOnly] public static float topLeftX = (width - 1) / -2f;
[ReadOnly] public static float topLeftZ = (height - 1) / 2f;
[ReadOnly] public static NativeArray<float> heightMap = new NativeArray<float>(width * height, Allocator.TempJob);
public static NativeArray<float> heightCurveSamples;
public static NativeArray<float3> vertices = new NativeArray<float3>(width * height, Allocator.TempJob);
public static NativeArray<int6> triangles = new NativeArray<int6>((width - 1) * (height - 1), Allocator.TempJob);
public static NativeArray<float2> uvs = new NativeArray<float2>(width * height, Allocator.TempJob);
public void Execute()
{
for (int i = 0; i < vertices.Length; i += 5)
{
Execute(i, 5);
}
}
private void Execute(int startIndex, int count)
{
for (int vertexIndex = startIndex; vertexIndex < startIndex + count; vertexIndex++)
{
int x = vertexIndex % width;
int y = vertexIndex / width;
vertices[vertexIndex] = new float3(topLeftX + x, heightMap[vertexIndex] * 16.67f, topLeftZ - y);
uvs[vertexIndex] = new float2(x / (float)width, y / (float)height);
if (vertexIndex < triangles.Length && x < width - 1 && y < height - 1)
{
triangles[vertexIndex] = new int6(vertexIndex, vertexIndex + width + 1, vertexIndex + width,
vertexIndex + width + 1, vertexIndex, vertexIndex + 1);
}
}
}
}
I have come up with the following solution to this problem:
The first issue i solved was using a nested for loop y, x with y always starting at startIndex.
this, however, caused an issue as the vertexIndex could be higher than the length of the triangles length, so i calculated the current vertexIndex at the supplied startIndex as follows:
Here i introduced an incrementer value which increments both the x and y loops rather than y++, x++ however in this example incrementer is 1 which is essentially the same thing.
int vertexIndex = (int)(math.ceil((float)width / incrementer) * math.ceil((float)startIndex / incrementer));
however calculating the vertexIndex caused another issue which again caused out of bounds exceptions on setting the vertices.
This was due to the startIndex being incremented by count, where count was not the same as the incrementer.
To solve this I at the start of the method added the following code to round the startIndex up to the next incremental count if needed.
startIndex += startIndex % incrementer;
and altogether i then get the following code:
public struct int6
{
public int a, b, c, d, e, f;
public int6(int a, int b, int c, int d, int e, int f) { this.a = a; this.b = b; this.c = c; this.d = d; this.e = e; this.f = f; }
}
public class MeshGeneratorJob2
{
public static int width = 241;
public static int height = 241;
public static float topLeftX = (width - 1) / -2f;
public static float topLeftZ = (height - 1) / 2f;
public static int increment = 1;
public static NativeArray<float> heightMap = new NativeArray<float>(width * height, Allocator.TempJob);
public static NativeArray<float> heightCurveSamples;
public static NativeArray<float3> vertices = new NativeArray<float3>(width * height, Allocator.TempJob);
public static NativeArray<int6> triangles = new NativeArray<int6>((width - 1) * (height - 1), Allocator.TempJob);
public static NativeArray<float2> uvs = new NativeArray<float2>(width * height, Allocator.TempJob);
public void Execute()
{
for (int i = 0; i < vertices.Length; i += 5)
{
Execute(i, 5);
}
}
private void Execute(int startIndex, int count)
{
startIndex += startIndex % increment;
int vertexIndex = (int)(math.ceil((float)width / increment) * math.ceil((float)startIndex / increment));
for (int y = startIndex; y < startIndex + count && y < height; y++)
{
for (int x = 0; x < width; x += increment)
{
vertices[vertexIndex] = new float3(topLeftX + x, heightMap[vertexIndex] * 16.67f, topLeftZ - y);
uvs[vertexIndex] = new float2(x / (float)width, y / (float)height);
if (vertexIndex < triangles.Length && x < width - 1 && y < height - 1)
{
triangles[vertexIndex] = new int6(vertexIndex, vertexIndex + width + 1, vertexIndex + width,
vertexIndex + width + 1, vertexIndex, vertexIndex + 1);
}
vertexIndex++;
}
}
}
}

Xamarin Urho IOS how to set up an application?

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);
}
}
}

Bug with repeat sine sound

What is my code:
void SpeakThreadFunction()
{
while (SpeakThreadState)
{
Speaker.Play();
Thread.Sleep(100);
Speaker.Stop()
Thread.Sleep(Interval);
}
}
//Speaker is WaveOut
And Speaker.Init is SineWaveProvider32.
public class SineWaveProvider32 : WaveProvider32
{
int sample;
public SineWaveProvider32()
{
Frequency = 1000;
Amplitude = 0.25f;
}
public float Frequency { get; set; }
public float Amplitude { get; set; }
public override int Read(float[] buffer, int offset, int sampleCount)
{
int sampleRate = WaveFormat.SampleRate;
for (int n = 0; n < sampleCount; n++)
{
buffer[n + offset] = (float)(Amplitude * Math.Sin((2 * Math.PI * sample * Frequency) / sampleRate));
sample++;
if (sample >= sampleRate) sample = 0;
}
return sampleCount;
}
}
After 10-15 iterations on my cycle, sound is stop :(. What i need to do, to my sound repeat all time?
You won't have much success trying to continually start and stop the soundcard like that. The default WaveOut buffer sizes in NAudio are 100ms long. It would be much better to open the soundcard once, and then send it portions of sine wave, followed by zeroes, to create the sound you want.

Categories