OnAudioFilterRead Question

Hi all,

So I’m trying to use Unity’s OnAudioFilterRead() method to generate a tone that is the combination of several sine waves (i.e. 440Hz, 800Hz, and 1000Hz playing simultaneously). I started with this code that I found online, which generates a pure tone at 440Hz. How can I use this same framework to generate a series of tones rather than just one? Thanks in advance.

using UnityEngine;
using System; // Needed for Math
public class Sinus : MonoBehaviour
{
// un-optimized version
public double frequency = 440;
public double gain = 0.05;
private double increment;
private double phase;
private double sampling_frequency = 48000;
void OnAudioFilterRead(float[ ] data, int channels)
{
// update increment in case frequency has changed
increment = frequency * 2 * Math.PI / sampling_frequency;
for (var i = 0; i < data.Length; i = i + channels)
{
phase = phase + increment;
// this is where we copy audio data to make them “available” to Unity
data = (float)(gain*Math.Sin(phase));
// if we have stereo, we copy the mono data to each channel
if (channels == 2) data[i + 1] = data*;*
if (phase > 2 * Math.PI) phase = 0;
}
}
}

bumppp

Hi! Here is a quick modification that might help you:

using UnityEngine;
using System; // Needed for Math

public class Sinus : MonoBehaviour
{
// un-optimized version
	public double frequency = 440;
	public double gain = 0.05;
	private double increment;
	private double phase;
	private double sampling_frequency = 48000;
	
	private double[] tones = { 440.0f, 1046.5f, 3729.3f, 1046.5f };
	private int tone_index;
	private float last_time;
	
	void Start()
	{
Debug.Log("" + tone_index + ": " + tones[tone_index]);		
	}
	
	void Update()
	{
		if (Time.realtimeSinceStartup - last_time > 1)
		{
			last_time = Time.realtimeSinceStartup;
			tone_index++;
			if (tone_index >= tones.Length)
				tone_index = 0;
Debug.Log("" + tone_index + ": " + tones[tone_index]);
		}
	}
	
	void OnAudioFilterRead(float[] data, int channels)
	{
// update increment in case frequency has changed
		increment = tones[tone_index] * 2 * Math.PI / sampling_frequency;

		for (var i = 0; i < data.Length; i += channels)
		{
			phase += increment;
// this is where we copy audio data to make them “available” to Unity
			data[i] = (float) (gain * Math.Sin(phase));
// if we have stereo, we copy the mono data to each channel
			if (channels == 2)
				data[i + 1] = data[i];
			if (phase > 2 * Math.PI)
				phase = 0;
		}
	}
}

Thank you for your help thus far, I really appreciate it.

The code you posted functions a little differently than what I need. Rather than cycling through an array of tones, I need to play a set of tones simultaneously i.e. 440 Hz, 1000 Hz, and 250 Hz all at the same instant in the audio buffer. Is this possible? Thanks again for your reply!

This information might help you:

http://www.school-for-champions.com/science/sound_beat_frequencies.htm

What he’s asking is how do you write a function to play multiple tones that you can play simultaneously to form chords. I tried to achieve this by firing the same function 3 times at the same time, but every sinewave with a different frequency. This isn’t accurate like I want it to be, the delay between the sinewaves is very noticeable.

Exactly. Also, I read the link you provided Unimechanic, but it seems that the simple addition of frequency values doesn’t produce the desired results. The script just plays back the tone with the highest frequency and seems to ignore the rest.

If anyone could help me out with this, I’d greatly appreciate it - Thank you so much!

To combine three (or any number of) tones or clips you just need to sum their audio data, sample-by-sample. So you could try something like this:

public double[ ] frequencies = {440.0f, 554.365f, 659.225f}; // A major
public double gain = 0.05;
private double increment;
private double phase;
…
and then later…

void OnAudioFilterRead(float[ ] data, int channels)
{
foreach (double currentFrequency in frequencies)
{
// update increment in case frequency has changed
increment = currentFrequency * 2 * Math.PI / sampling_frequency;
for (var i = 0; i < data.Length; i = i + channels)
{
phase = phase + increment;
// this is where we copy audio data to make them “available” to Unity
data += (float)(gain*Math.Sin(phase));
// if we have stereo, we copy the mono data to each channel
if (channels == 2) data[i + 1] = data*;*
if (phase > 2 * Math.PI) phase = 0;
}
}
}
Note that this assumes all data samples are 0 coming in (i.e., there’s not a playing audio source passing audio in).
Also, watch your gain! Because there are three tones, this will multiply your maximum gain by three. (So 0.05 becomes 0.15) It will always multiply your maximum gain by the number of elements in frequencies (because it’s summing them!) So if you had 30 tones, your maximum gain would be 0.05 * 30 which is 1.5, well over the full system volume. Your maximum gain should never exceed 1.0, and you should probably keep it lower so that your other sounds have some room to play nicely too.

hiya, i am an audio programmer.

to get any tone, find a Pitch to Frequency Converter in code, i.e. delphi, musicdsp website, etc. then you can have 128 piano key tones.

Ok so get 4 sines/waves at different Freqs/Pitches and ADD them together and devide amplitude by 4.

make 4 sine waves or other waves, (math ping pong of (sine(f)*wavemod,1)*2-1 changes the timbre of the waveform for example.

That is 4 sines of equal amplitude different tones playing at same time. you can then make music by changing all the pitch inputs of the 4 waves.

has there been any update on this?