Frequency and pitch relation for AudioClip - Unity3D - c#

I am trying to recreate the full range of a guitar only using 6 audio clips.
I was thinking there would be a way to set frequency of an audio clip but audio.frequency only returns the frequency of the audio based on compression format and not the actual tone.
I know I can read GetSpectrumData, but that solution is fairly complex and would require some Fourier Transform analysis or something of the kind.
Affecting the pitch, it is easy to alter the tone so I can go up and down but is there a way to figure out what are the steps to use.
void Update ()
{
CheckAudio(KeyCode.Q, 1.0f);
CheckAudio(KeyCode.W, 1.1f);
CheckAudio(KeyCode.E, 1.2f);
CheckAudio(KeyCode.R, 1.3f);
CheckAudio(KeyCode.T, 1.4f);
}
void CheckAudio(KeyCode key, float pitch)
{
if (Input.GetKeyDown (key))
{
audio.pitch = pitch;
audio.Play ();
}
}
I can hear it does not sound right.
Knowing the initial tone E4 329.63Hz with pitch at 1 is there any equation that affecting the pitch, I would get the next key F4 349.23Hz (or close enough)?
It has to be considered also that Unity AudioSource limits the pitch within -3/3 range (which I think is more than needed).
EDIT: Adding some personal research. It seems pitch 1 is initial note and setting to 2 give the same key one octave higher.
Since a chromatic scale (all black and white notes on the piano) is 12 keys, I assume that using 1/12 for each step should do it.
It sounds close but I fell it is not quite right. Here is the new code:
[SerializeField] private AudioSource audio;
float step = 1f/12f;
KeyCode[]keys = new KeyCode[]{
KeyCode.Q, KeyCode.W,KeyCode.E,KeyCode.R,KeyCode.T,
KeyCode.Y, KeyCode.U, KeyCode.I, KeyCode.O, KeyCode.P,
KeyCode.A, KeyCode.S, KeyCode.D
};
void Update ()
{
float f = 0.0f;
foreach (KeyCode key in keys)
{
CheckAudio(key, f);
f += 1f;
}
}
void CheckAudio(KeyCode key, float pitch)
{
if (Input.GetKeyDown (key))
{
audio.pitch = 1f + pitch * step;
audio.Play ();
}
}

What you are trying to do will not work well by simply changing the pitch of the audio. By changing the pitch, you will run into other problems such as sound finishing too fast or taking more time to finish and the sound will not be good either.
The first solution is to make a plugin(Synthesizer) in C++ that reads the audio file from Unity and change the frequency. It should also perform other actions to fix speed issues. This is very complicated unless you are an audio engineer with some great math skills. And trying this on a mobile device is whole different story. OnAudioFilterRead is a function you should use if you decide to go with this method.
The second and the recommended solution is to make an audio file for each guitar key then put them into array of audioClip. This solves every other problems.The down side is that you will have more files.
EDIT:
If you don't care about it being perfect, you can use something below from this nice guy on the internet.
void playSound(){
float transpose = -4;
float note = -1;
if (Input.GetKeyDown("a")) note = 0; // C
if (Input.GetKeyDown("s")) note = 2; // D
if (Input.GetKeyDown("d")) note = 4; // E
if (Input.GetKeyDown("f")) note = 5; // F
if (Input.GetKeyDown("g")) note = 7; // G
if (Input.GetKeyDown("h")) note = 9; // A
if (Input.GetKeyDown("j")) note = 11; // B
if (Input.GetKeyDown("k")) note = 12; // C
if (Input.GetKeyDown("l")) note = 14; // D
if (note>=0){ // if some key pressed...
audio.pitch = Mathf.Pow(2, (note+transpose)/12.0);
audio.Play();
}
EDIT: For those of you interested in why the Mathf.Pow equation is used and working, read the following: https://en.wikipedia.org/wiki/Twelfth_root_of_two

Related

Saving a float value in a certain moment in unity

Hello I did the Brackeys Tutorial and I am trying to make my own game off of it and the first thing i wanted to do was a restart screen and for that i need the score but if the cube falls off it still goes forward so i need the z value of the cube when it falls of the edge
What do you mean?
If you just want to save the float value of a transform, use something like
float z = cube.transform.position.z;
When setting the transform, you need to use a whole Vector3; you can't just change one value.
cube.transform.position = Vector3(cube.transform.position.x, cube.transform.position.y, z);
Ok. According to your comment,
bool checkForY = true;
GameObject cube;
float cubeZ;
void Update()
{
if (checkForY)
{
if (cube.transform.position.y < 0)
{
cubeZ = cube.transform.position.z;
checkForY = false;
}
}
}
This should work. You might want to rename "cubeZ" to whatever you're using it for, though.

Creating Simple Endless Moving Platform with cubes

Trying to create simple endless moving platform with 3 cubes of scale 70 on z(Player will not move forward, will just move left/right). The RepositionPlatform script is attached to each platform/cube which is responsible for movement and checks the z position of each platform and if it is <= -100.0f, then position is changed to (0,0,200.0f).
Problem is sometimes there is a little gap between the platforms(cubes) or there is a little overlap which I don't want.
Platforms should be placed one after each other without any gap or overlap!!!
Can anyone help find the issue looking at the script or suggest any other better way ?
The script below is attached to 3 platform game objects!!!
public class RepositionPlatform : MonoBehaviour
{
private GameObject platformGO;
[SerializeField]
private float speed;
// Start is called before the first frame update
void Start()
{
platformGO = this.gameObject;
Debug.Log("In RepositionPlatform Start method - "+ platformGO.name);
}
// Update is called once per frame
void Update()
{
Debug.Log("In RepositionPlatform Update Method- " + platformGO.name);
platformGO.transform.Translate(Vector3.back * Time.deltaTime * speed);
Transform platformTransform = platformGO.transform;
if(platformTransform.position.z <= -100.0f)
{
platformTransform.position = new Vector3(0,0,200.0f);
}
}
}
Probably because speed is a floating point value. You should read up on them if you haven't already.
Long story short, you aren't accounting for how far below -100 the value might have gone, you're just resetting it.
If you translate it instead, you will preserve any extra distance beyond -100 that the transform might have gone.
Try this instead:
If (transform.position.z < -100){
transform.Translate(new Vector3(0,0,200));
}
Edit
Should be Z value, not X

how to change shake vibrato/strength while tween is playing

I have a shake tweener initilialized like this:
mainCamera.DOShakePosition (100f, 0.05f, shakeVibrato, 90, false, false);
The duration 100 is just put there so I can have some big value. The idea is to change the vibrato/strength of the shake while it is active.
Imagine some vibration source is coming closer to the player. As it approaches, the vibrato increases, as it passes by the vibrato decreases. How can I manipulate these values while tweener is active? I saw some ChangeValues() methods but I'm not sure what they do and the documentation is not very clear about them.
As far as I know, there's no proper way to do this for DOTween's DOShake methods (though I would love to be corrected). One really hacky way to do this is by setting the duration to a low-ish value, and re-initializing the tween with different values in it's OnComplete callback. It's pretty far from ideal, since you're not changing the values during the tween, but rather at an interval - resulting in a stepped or sudden change. Resource-wise, I don't imagine this being very efficient either.
Tweener shakeTween;
TweenCallback shakeTweenComplete;
void Start()
{
shakeTweenComplete = () =>
{
shakeTween = transform.DOShakeRotation(0.1f, strength: shakeStrength, vibrato: shakeVibrato, fadeOut: false).SetRelative();
shakeTween.OnComplete(shakeTweenComplete);
};
// Invoke the callback instead of having duplicated code
shakeTweenComplete.Invoke()
}
I have shakeVibrato modified elsewhere. I also tried to do this with SetLoop and OnStepComplete, with no luck.
EDIT - For future reference, this is my take on how OP ended up solving this issue. I substituted changing the vibrato with Tweener.timeScale, since it ends up looking the same anyway.
public float shakeMultiplier = 1.0f;
public float shakeTimeScale = 1.0f;
// These values won't be changed
public float baseShakeDuration = 1.0f;
public float baseShakeStrength = 0.1f;
public int baseShakeVibrato = 10;
Vector3 shakePosition;
Tweener shakeTween;
void Start()
{
shakeTween = DOTween.Shake(() => shakePosition, x => shakePosition = x, baseShakeDuration, baseShakeStrength, baseShakeVibrato, fadeOut: true)
.SetLoops(-1, LoopType.Restart);
}
void Update()
{
transform.localPosition = shakePosition * shakeMultiplier;
shakeTween.timeScale = shakeTimeScale;
}

How to get an accurate method of time passed using audioSettings.dspTime?

Thanks for looking and for any help / advice you might have.
I'm trying to sync audio with gameplay.
Is this an accurate method of finding out how much time has passed using audioSettings.dspTime?
I take a float representing time and multiply by song.clip.frequency.
float myChosenAmountOfTimeInSamples = myChosenAmountOfTime * song.clip.frequency;
float audioSecondsLater = Audio + myChosenAmountOfTimeInSamples;
And this onto audioSettings.dspTime * song.clip.frequency
float Audio = (float)AudioSettings.dspTime * song.clip.frequency;
float audioSecondsLater = Audio + myChosenAmountOfTimeInSamples;
I'm using this code to see if it works. As far as I can tell it happens 1 second later
In Update
if (Input.GetKeyDown (KeyCode.A)){
StartCoroutine(test ());
}
And the coroutine
IEnumerator test ()
{
float Audio = (float)AudioSettings.dspTime * song.clip.frequency;
float myChosenAmountOfTime = 1f;
float myChosenAmountOfTimeInSamples = myChosenAmountOfTime * song.clip.frequency;
float audioSecondsLater = Audio + myChosenAmountOfTimeInSamples;
print ("Audio seconds later: " + audioSecondsLater);
while (true)
{
Audio = (float)AudioSettings.dspTime * song.clip.frequency;
if (Audio >= audioSecondsLater)
{
print ("Audio Now: " + Audio); // Theoretically this is 1 second later
break;
}
yield return new WaitForSeconds(1 / 1000f);
}
}
I'm not entirely sure, but I think you're on the right track. I've done sync before against a single AudioClip, but I keep all my timing in samples. To check the current sample I examine AudioSource.timeSamples on the clip that is playing. I use REAPER to examine the audio file and figure out at which sample in the file the events happen, and compare that to timeSamples to see if my sync event has happened yet.
AudioSource also has a time property which is the playback position in seconds, though see note in the documentation about inaccuracy when used with compressed audio.

Unity3D: Adding charged force in relation to position

I am trying to make a game which "kind of" simulates the shooting of the "worms" game. The player can choose the position (circular) of an object and then, the force that is applied to the object should move in the direction its pointing towards. I tried using the AddForce(transform.right) code, but it would just go to the right. (2D BoxCollider and RigidBody2D)
Then comes the hard part, making the player choose the force by charging the power. When the player holds down the "f" key, I want the power to go up to a certain point. Once it reaches that point, I want it to go down again and then up again, so the player can choose the power he wants. I have no idea how to go about this.
It's been awhile since I've did Unity coding, so there may be some minor errors with my syntax but it should give you an idea of how to accomplish this. Your best bet for the loop is to use a coroutine to not block the main thread.
in Update() check for 'on key down' for F and start this coroutine:
IEnumerator Cycle()
{
float max = 10.0f;
float min = 1.0f;
float interval = 0.5f;
do
{
for(int i=min;i<max;i++)
{
PowerValue = i;
yield return new waitforseconds(interval);
if(!input.getkey(f))
break;
}
for(int i=max;i>min;i--)
{
PowerValue = i;
yield return new waitforseconds(interval);
if(!input.getkey(f))
break;
}
} while(input.getkey(f));
}
And back in update() use that powerValue with getKeyUp(f)
And here is PowerValue setup as a parameter that prevents code from setting the max and min outside of a 1 to 10 range (configurable)
private float powerValue = 1.0f;
public float PowerValue
{
get { return powerValue; }
set {
if(value>10f)
powerValue=10f;
else if (value<1f)
powerValue=1f;
else
powerValue=value;
}
}

Categories

Resources