I want to generate a sine sweep in C# where I am able to define the start frequency, end frequency, and the duration of the sweep. I've looked at sound libraries such as DirectSound and ASIO that play a buffer. But I haven't been able to figure out how to control the duration of the sweep when the duration of the sweep is long enough to fill more than one buffer due to the buffer size limitation. Any samples or guides would be extremely helpful.
If you are satisfied with an running program without writing it yourself take a look at The Audio Test File Generator.
This small windows EXE is able to generate a linear sine sweep with a given start and end frequency.
If you want to write it by your own, you have to fill the buffer using:
sin(2*pi * f * n/sample_rate)
where
f is the current sine frequency (you want to sweep) in Hz
n is the sample index of the buffer
sample_rate is the sample rate in Hz
An example with f=10Hz.
ulrichb has already stated all necessary information but recently I had to build a sine sweep generator in .Net with C#.
It looked cool to me, I'll leave the code here, maybe it will be useful for others.
numberofSamples: Buffer size.
sweepDuration: Time takes to go from low frequency to high frequency.
lowFreq: Start frequency
highFreq: End Frequency
deltaTime: 1 / sampling rate (time taken to take 1 sample)
float sweepCurrentTime = 0.0f;
float sweepFrequencyFactor = 0.0f;
float sweepCurrentCyclePosition = 0.0f;
float sweepFrequency = 0.0f;
public void generateSineSweep(float[] buffer, int numberOfSamples, int sampleRate, int sweepDuration, float lowFreq, float highFreq)
{
float deltaTime = 1.0f / sampleRate;
for (int i = 0; i < numberOfSamples; i++)
{
sweepFrequency = lowFreq + ((highFreq - lowFreq) * sweepFrequencyFactor);
sweepCurrentCyclePosition += sweepFrequency / sampleRate;
buffer[i] = Convert.ToSingle(0.25f * Math.Sin(sweepCurrentCyclePosition * 2 * Math.PI));
if (sweepCurrentTime > sweepDuration)
{
sweepCurrentTime -= sweepDuration;
sweepCurrentTime += deltaTime;
sweepFrequencyFactor = 0.0f;
}
else
{
sweepCurrentTime += deltaTime;
sweepFrequencyFactor = sweepCurrentTime / sweepDuration;
}
}
}
The function progresses from low frequency to high frequency increasing the frequency by a certain amount after each sample.
Related
I'm trying to get the pitch from the microphone input. First I have decomposed the signal from time domain to frequency domain through FFT. I have applied Hamming window to the signal before performing FFT. Then I get the complex results of FFT. Then I passed the results to Harmonic product spectrum, where the results get downsampled and then multiplied the downsampled peaks and gave a value as a complex number. Then what should I do to get the fundamental frequency?
public float[] HarmonicProductSpectrum(Complex[] data)
{
Complex[] hps2 = Downsample(data, 2);
Complex[] hps3 = Downsample(data, 3);
Complex[] hps4 = Downsample(data, 4);
Complex[] hps5 = Downsample(data, 5);
float[] array = new float[hps5.Length];
for (int i = 0; i < array.Length; i++)
{
checked
{
array[i] = data[i].X * hps2[i].X * hps3[i].X * hps4[i].X * hps5[i].X;
}
}
return array;
}
public Complex[] Downsample(Complex[] data, int n)
{
Complex[] array = new Complex[Convert.ToInt32(Math.Ceiling(data.Length * 1.0 / n))];
for (int i = 0; i < array.Length; i++)
{
array[i].X = data[i * n].X;
}
return array;
}
I have tried to get the magnitude using,
magnitude[i] = (float)Math.Sqrt(array[i] * array[i] + (data[i].Y * data[i].Y));
inside the for loop in HarmonicProductSpectrum method. Then tried to get the maximum bin using,
float max_mag = float.MinValue;
float max_index = -1;
for (int i = 0; i < array.Length / 2; i++)
if (magnitude[i] > max_mag)
{
max_mag = magnitude[i];
max_index = i;
}
and then I tried to get the frequency using,
var frequency = max_index * 44100 / 1024;
But I was getting garbage values like 1248.926, 1205,859, 2454.785 for the A4 note (440 Hz) and those values don't look like harmonics of A4.
A help would be greatly appreciated.
I implemented harmonic product spectrum in Python to make sure your data and algorithm were working nicely.
Here’s what I see when applying harmonic product spectrum to the full dataset, Hamming-windowed, with 5 downsample–multiply stages:
This is just the bottom kilohertz, but the spectrum is pretty much dead above 1 KHz.
If I chunk up the long audio clip into 8192-sample chunks (with 4096-sample 50% overlap) and Hamming-window each chunk and run HPS on it, this is the matrix of HPS. This is kind of a movie of the HPS spectrum over the entire dataset. The fundamental frequency seems to be quite stable.
The full source code is here—there’s a lot of code that helps chunk the data and visualize the output of HPS running on the chunks, but the core HPS function, starting at def hps(…, is short. But it has a couple of tricks in it.
Given the strange frequencies that you’re finding the peak at, it could be that you’re operating on the full spectrum, from 0 to 44.1 KHz? You want to only keep the “positive” frequencies, i.e., from 0 to 22.05 KHz, and apply the HPS algorithm (downsample–multiply) on that.
But assuming you start out with a positive-frequency-only spectrum, take its magnitude properly, it looks like you should get reasonable results. Try to save out the output of your HarmonicProductSpectrum to see if it’s anything like the above.
Again, the full source code is at https://gist.github.com/fasiha/957035272009eb1c9eb370936a6af2eb. (There I try out another couple of spectral estimator, Welch’s method from Scipy and my port of the Blackman-Tukey spectral estimator. I’m not sure if you are set on implementing HPS or if you would consider other pitch estimators, so I’m leaving the Welch/Blackman-Tukey results there.)
Original I wrote this as a comment but had to keep revising it because it was confusing so here’s it as a mini-answer.
Based on my brief reading of this intro to HPS, I don’t think you’re taking the magnitudes correctly after you find the four decimated responses.
You want:
array[i] = sqrt(data[i] * Complex.conjugate(data[i]) *
hps2[i] * Complex.conjugate(hps2[i]) *
hps3[i] * Complex.conjugate(hps3[i]) *
hps4[i] * Complex.conjugate(hps4[i]) *
hps5[i] * Complex.conjugate(hps5[i])).X;
This uses the sqrt(x * Complex.conjugate(x)) trick to find x’s magnitude, and then multiplies all 5 magnitudes.
(Actually, it moves the sqrt outside the product, so you only do one sqrt, saves some time, but gives the same result. So maybe that’s another trick.)
Final trick: it takes that result’s real part because sometimes due to float accuracy issues, a tiny imaginary component, like 1e-15, survives.
After you do this, array should contain just real floats, and you can apply the max-bin-finding.
If there’s no Conjugate method, then the old-fashioned way should work:
public float mag2(Complex c) { return c.X * c.X + c.Y * c.Y; }
// in HarmonicProductSpectrum
array[i] = sqrt(mag2(data[i]) * mag2(hps2[i]) * mag2(hps3[i]) * mag2(hps4[i]) * mag2(hps5[i]));
There’s algebraic flaws with the two approaches you suggested in the comments below, but the above should be correct. I’m not sure what C# does when you assign a Complex to a float—maybe it uses the real component? I’d have thought that’d be a compiler error, but with the above code, you’re doing the right thing with the complex data, and only assigning a float to array[i].
To get a pitch estimate, you have to divide your sumed bin frequency estimate by the downsampling ratio used for that sum.
Added: You should also sum the magnitudes (abs()), not take the magnitude of the complex sum.
But the harmonic product spectrum algorithm (HPS), especially when using only integer ratios of downsampling, doesn't usually provide better pitch estimation resolution. Instead, it provides a more robust rough pitch estimate (less likely to be fooled by a harmonic) than using a single bare FFT magnitude peak for sequential overtone rich timbres that have weak or missing fundamental spectral content.
If you know how to downsample a spectrum by fractional ratios (using interpolation, etc.), you can try finer grained downsampling to get a better pitch estimate out of HPS. Or you can use an HPS result to inform you of a narrower frequency range in which to search using another pitch or frequency estimation method.
float wave = 0.5f * sin( _Time.w * Frequency + 12.0f) + 1.0f;
Even if i change "Frequency" in super small steps (0.001f) the animation tends to jump.
Thanks.
You need to keep track of the phase, because when you change frequency and then multiply by the current time, you have a sudden jump in phase.
Based on the way you currently calculate it:
in your class definition:
float mCurrentPhase = 12.0f; // start phase at 12 for some reason
in your function
mCurrentPhase += (Time.deltaTime * Frequency);
float wave = 0.5f * sin( mCurrentPhase ) + 1.0f;
What is the +12.0f for? Did you want to start the waveform at a certain point? Because +12.0f is actually going to just start you off in the next period of the sine wave - same as the last one.
Also, sine functions get flakey with really big numbers, and since sine is periodic, you can also do this:
mCurrentPhase += (Time.deltaTime * Frequency);
// Keep phase between 0 .. 2*PI ... Note not tested in Unity
if (mCurrentPhase >= 2*PI) mCurrentPhase -= 2*PI;
float wave = 0.5f * sin( mCurrentPhase ) + 1.0f;
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
so i have torpedos in my game and they start out at 0 meters per second and accelerate realistically. after so many seconds they stop accelerating and travel at a constant rate forward.
I have a distance to the target and I basically am trying to calculate lead time for autoaiming.
So given
Distance to target;
Acceleration (per second);
burn time (number of seconds before acceleration stops);
I need to basically determine I believe the average meters per second the projectile is travelling.
The only way I can see to do it is something like this.
curdistance; //stores distance traveled per second
currentspeed; //stores speed at a given second
acceleration;
for(int timer = 1; curdistance < distanceToTarget;timer++)
{
currentspeed = currentspeed + acceleration;
curdistance = curdistance + ( currentspeed);
if(timer >= burnTime)
{
acceleration = 0;
}
}
Now this works but it has 2 problems.
The burn time has to be an int or else the smaller the fraction the greater the number of runs to keep accuracy.
If i want a 4.2 burn time for example in order to keep the accuracy i have to run it 42 times and calculate for every 10th of a second.
Also the average could be off by quite a bit depending on how much it overshoots a target depending again on how precise the timer is.
if my projectile is going at 30 meters per second and it needs to go 121 meters it'll add another full second of travel before it goes ok you've gone to/past the target which will mean it will actualy be aiming as it were at a point 29 meters further than it really should.
The only way to combat this with this algorithm is to check more often every 10th or 100th of a second.
I feel like though there might be a math equation I don't know that lets me solve this precisely.
Any Help?
During accelerated motion you can use d = a*t^2/2, or equivalently t = sqrt(2*d/a), at which time velocity v = a*t.
Then you can extrapolate to the target using that v.
As you describe it your movement happens in 2 parts. The first part is accelerated movement (with constant acceleration) and the second part is movement under constant velocity.
You can calculate the traveling distance (or time) for each one individually and then combine them for the desired result.
Keep in mind that you need to check for special cases where the target is closer than the burn distance. The code below does that with the check if (distanceToTarget < burnDistance)
// these will be the results
float timeToTarget;
float averageSpeed;
// assign values to these
float distanceToTarget;
float acceleration;
float burnTime;
float burnDistance = acceleration * burnTime * burnTime * 0.5;
if (distanceToTarget < burnDistance)
{
timeToTarget = Math.Sqrt(2 * distanceToTarget / acceleration);
}
else
{
float velocity = acceleration * burnTime;
timeToTarget = burnTime + (distanceToTarget - burnDistance) / velocity;
}
averageSpeed = distanceToTarget / timeToTarget;
If
d = initial distance to the target
b = burn time
a = acceleration
When the projectile stops accelerating, it will have
speed = a*b
distance (traveled) = dt = a*b^2/2
From that moment, it will need
time for impact = ti = (d-dt)/(a*b)
The total time will be
total time for impact = ti + b
This is one way:
Function VelocityGivenTime(burnTime, givenTime)
(
T = givenTime
If T > burnTime Then T = burnTime
return acceleration * T
)
Function DistanceGivenTime(burnTime, givenTime)
(
If burntime >= givenTime Then
T = givenTime
return 0.5 * acceleration * T^2
Else
T = burnTime
D = 0.5 * acceleration * T^2
D = D + VelocityGivenTime(T) * (givenTime - burnTime)
return D
End IF
)
However, if what you really wanted was the time to a target give its distance, you could do it like this:
Function TimeGivenDistance(burnTime, distance)
(
burnDistance = DistanceGivenTime(burnTime)
If distance > burnDistance Then
return burnTime + (distance - burnDistance) / VelocityGivenTime(burnTime)
Else
return SQRT(2 * distance / acceleration)
End If
)
I'm writing a synthesizer in C# using NAudio. I'm trying to make it slide smoothly between frequencies. But I have a feeling I'm not understanding something about the math involved. It slides wildly at a high pitch before switching to the correct next pitch.
What's the mathematically correct way to slide from one pitch to another?
Here's the code:
public override int Read(float[] buffer, int offset, int sampleCount)
{
int sampleRate = WaveFormat.SampleRate;
for (int n = 0; n < sampleCount; n++)
{
if (nextFrequencyQueue.Count > 0)
{
nextFrequency = nextFrequencyQueue.Dequeue();
}
if (nextFrequency > 0 && Frequency != nextFrequency)
{
if (Frequency == 0) //special case for first note
{
Frequency = nextFrequency;
}
else //slide up or down to next frequency
{
if (Frequency < nextFrequency)
{
Frequency = Clamp(Frequency + frequencyStep, nextFrequency, Frequency);
}
if (Frequency > nextFrequency)
{
Frequency = Clamp(Frequency - frequencyStep, Frequency, nextFrequency);
}
}
}
buffer[n + offset] = (float)(Amplitude * Math.Sin(2 * Math.PI * time * Frequency));
try
{
time += (double)1 / (double)sampleRate;
}
catch
{
time = 0;
}
}
return sampleCount;
}
You are using absolute time to determine the wave function, so when you change the frequency very slightly, the next sample is what it would have been had you started the run at that new frequency.
I don't know the established best approach, but a simple approach that's probably good enough is to compute the phase (φ = t mod 1/fold) and adjust t to preserve the phase under the new frequency (t = φ/fnew).
A smoother approach would be to preserve the first derivative. This is more difficult because, unlike for the wave itself, the amplitude of the first derivative varies with frequency, which means that preserving the phase isn't sufficient. In any event, this added complexity is almost certainly overkill, given that you are varying the frequency smoothly.
One approach is to use wavetables. You construct a full cycle of a sine wave in an array, then in your Read function you can simply lookup into it. Each sample you read, you advance by an amount calculated from the desired output frequency. Then when you want to glide to a new frequency, you calculate the new delta for lookups into the table, and then instead of going straight there you adjust the delta incrementally to move to the new value over a set period of time (the 'glide' or portamento time).
Frequency = Clamp(Frequency + frequencyStep, nextFrequency, Frequency);
The human ear doesn't work like that, it is highly non-linear. Nature is logarithmic. The frequency of middle C is 261.626 Hz. The next note, C#, is related to the previous one by a factor of Math.Pow(2, 1/12.0) or about 1.0594631. So C# is 277.183 Hz, an increment of 15.557 Hz.
The next C up the scale has double the frequency, 523.252 Hz. And C# after that is 554.366 Hz, an increment of 31.084 Hz. Note how the increment doubled. So the frequencyStep in your code snippet should not be an addition, it should be a multiplication.
buffer[n + offset] = (float)(Amplitude * Math.Sin(2 * Math.PI * time * Frequency));
That's a problem as well. Your calculated samples do not smoothly transition from one frequency to the next. There's a step when "Frequency" changes. You have to apply an offset to "time" so it produces the exact same sample value at sample time "time - 1", the same value you previously calculated with the previous value of Frequency. These steps produce high frequency artifacts with many harmonics that are gratingly obvious to the human ear.
Background info is available in this Wikipedia article. It will help to visualize the wave form you generate, you would have easily diagnosed the step problem. I'll copy the Wiki image:
I'm making a galaxian-like shooter, and my enemy objects have a destination Vector which they travel towards, using this bit of code:
position.X -= (Motion.X / Magnitude) * Speed;
position.Y -= (Motion.Y / Magnitude) * Speed;
Motion is worked out by:
this.Motion = InitialPosition - Destination;
This makes them travel in a straight line towards the destination.
However, I want to make them a bit more interesting, and travel on a sin or cos wave, a bit like Galaxian did.
How can I do this?
You might be better off defining a bezier curve for the movement function than simple functions like a sine wave. Galaxian certainly had more complex movements than that.
Here is a link to a primer on the maths of Bezier curves. It's quite a long document, but does a good job of covering the maths involved, with plenty of examples.
Hope that helps inspire you.
One way to do this would be to create an acceleration factor for the horizontal motion and add that factor to the horizontal speed every tick. So if your horizontal speed for a given enemy was 2 to begin, and your acceleration was -.01, then after 200 ticks the enemy would be going straight down, and after another 200 ticks it would be moving at a horizontal speed of -2. This will give a nice curve.
By determining the speed and acceleration randomly for each enemy (within certain limits determined by experimentation) you can create a nice looking variety of attack profiles without too much effort. This would give a very Galaxian-like motion.
You can do the same thing with the vertical as well, though, of course, the acceleration limits would be very different...for the horizontal acceleration you would probably want to determine a range that was equal in magnitude on either side of 0 (say -.02 to +.02), while for the vertical acceleration, you probably always want the ship to end up going down off the bottom of the screen, so you probably want that acceleration to always end up positive (or negative depending on how you're doing screen coordinates.)
You would do this by utilizing waypoint navigation, in line with your current motion code. You would calculate the waypoints by graphing the sine wave. You would do this by using something to the effect of Destination.Y = Math.Sin(Destination.X) - it's a little difficult to say for sure without seeing your code at large.
Creating an oscillator and moving the enemy (even without momentum) perpendicularly to its direction by an offset equals to the sine or cosine of the oscillator would be enough.
The following example, while working, is clearly just a guideline. I hope it can help you.
var dest = new PointF(200, 100);
var pos = new PointF(30, 140);
var oscAngle = 0d;
var dirAngle = Math.Atan2(dest.Y - pos.Y, dest.X - pos.X);
//Constants for your simulation
const int movSpeed = 2;
const int amp = 2;
const double frequency = Math.PI / 5;
//Inappropriate loop condition, change it to proper
while (true)
{
oscAngle += frequency;
//Scalar offset, you can use Cos as well
var oscDelta = Math.Sin(oscAngle);
//Linear movement
var stepVector = new SizeF((float)(Math.Cos(dirAngle) * movSpeed), (float)(Math.Sin(dirAngle) * movSpeed));
//Oscillating movement, making it transversal by adding 90° to the direction angle
var oscNormalAngle = dirAngle + Math.PI / 2;
//Vector for the oscillation
var oscVector = new SizeF((float)(Math.Cos(oscNormalAngle) * oscDelta) * amp, (float)(Math.Sin(oscNormalAngle) * oscDelta) * amp);
pos += stepVector + oscVector;
//Operate below
}