I would like to play an .ogg file with more than 2 channels from one AudioSource and be able to manipulate each channel’s volume independently.
This could mean routing them to different channels in an AudioMixer or to access each channel through C# script.
I thought one could fill an array with all samples from the multichannel clip, and then in OnAudioFilterRead() access the channels needed and output. It seems like the following code should work, but it doesn’t? I’m posting it anyway in case someone can spot the mistake I’ve made? I don’t have time to investigate now, but will return to it later…
float[] multiSamples;
int sampleOffset = 0;
int numberOfChannels = 0;
void Start()
{
audioSource = GetComponent<AudioSource>();
numberOfChannels = audioSource.clip.channels;
multiSamples = new float[audioSource.clip.samples * numberOfChannels];
audioSource.clip.GetData(multiSamples, 0);
}
void OnAudioFilterRead(float[] data, int channels)
{
if (multiSamples != null)
{
for (int i = 0; i < data.Length; i+=2)
{
//write channels 1 and 2 to AudioSource output
data[i] = multiSamples[sampleOffset + (i* numberOfChannels)];
data[i + 1] = multiSamples[sampleOffset + (i * numberOfChannels)+1];
//uncomment to write other channesl to outpu, mixing may be needed if you wish to output multiple channels..
//data[i] = multiSamples[sampleOffset + (i * numberOfChannels) + 2];
//data[i + 1] = multiSamples[sampleOffset + (i * numberOfChannels) + 3];
//data[i] = multiSamples[sampleOffset + (i * numberOfChannels) + 4];
//data[i + 1] = multiSamples[sampleOffset + (i * numberOfChannels) + 5];
//data[i] = multiSamples[sampleOffset + (i * numberOfChannels) + 6];
//data[i + 1] = multiSamples[sampleOffset + (i * numberOfChannels) + 7];
}
sampleOffset += data.Length;
}
}
If you figure out the pitch problem let me know. Looks like I’m skipping through the multiSamples[ ] array too quickly, but I can’t see what’s causing it. Note that you should reset offsetSamples once the multichannel file has been read, otherwise you’ll get an array index out of bounds error.
Ha, got it. I wasn’t taking into account that data[ ] represents a stereo buffer as the speaker config is set to stereo by default.
[edit] I’ve updated the code so that you can control the amplitude of the tracks with public floats. I’m using Mathf.clamp01() so that we can modify the amplitude of each channel in the Unity Editor without worrying about distortion.
private AudioSource audioSource;
float[] multiSamples;
int sampleOffset = 0;
int numberOfChannels = 0;
public float chan1Gain, chan2Gain, chan3Gain, chan4Gain, chan5Gain, chan6Gain, chan7Gain, chan8Gain;
float leftChannelOutput, rightChannelOutput;
void Start()
{
audioSource = GetComponent<AudioSource>();
numberOfChannels = audioSource.clip.channels;
multiSamples = new float[audioSource.clip.samples * numberOfChannels];
audioSource.clip.GetData(multiSamples, 0);
}
void OnAudioFilterRead(float[] data, int channels)
{
if (multiSamples != null)
{
for (int i = 0, y = 0; i < data.Length; y++, i += channels)
{
//check we don't go outside array index
if (sampleOffset + y * numberOfChannels + numberOfChannels > multiSamples.Length)
sampleOffset = 0;
//mix signals for left channel
leftChannelOutput = (multiSamples[sampleOffset + (y * numberOfChannels)] * Mathf.Clamp01(chan1Gain) +
multiSamples[sampleOffset + (y * numberOfChannels) + 2] * Mathf.Clamp01(chan3Gain) +
multiSamples[sampleOffset + (y * numberOfChannels) + 4] * Mathf.Clamp01(chan5Gain) +
multiSamples[sampleOffset + (y * numberOfChannels) + 6] * Mathf.Clamp01(chan7Gain));
//mix signals for right channel
rightChannelOutput = (multiSamples[sampleOffset + (y * numberOfChannels) + 1] * Mathf.Clamp01(chan2Gain) +
multiSamples[sampleOffset + (y * numberOfChannels) + 3] * Mathf.Clamp01(chan4Gain) +
multiSamples[sampleOffset + (y * numberOfChannels) + 5] * Mathf.Clamp01(chan6Gain) +
multiSamples[sampleOffset + (y * numberOfChannels) + 7] * Mathf.Clamp01(chan8Gain));
//write left and right channels
data[i] = leftChannelOutput;
data[i + 1] = rightChannelOutput;
}
//increment sampleOffset
sampleOffset += (data.Length / channels) * numberOfChannels;
}
}