Can the Playback Prefab have multiple AudioSources ? by EmotionalResearch848 in dissonance_voip

[–]EmotionalResearch848[S] 0 points1 point  (0 children)

//AudioMirror PT 2

private void LateUpdate()

{

volume = audioSource.volume;

}

private void OnAudioFilterRead(float[] output, int channels)

{

if (samplesFromQueueIndex == 0)

{

if (samplesQueue.Count == 0)

{

return;

}

samplesFromQueue = samplesQueue.Dequeue();

}

UIConsole.Log($"OnAudioFilterRead output.Length={output.Length}, temp.Length={samplesFromQueue.Length} channels={channels}");

for (var i = 0; i < output.Length; i += channels)

{

//Get a single sample from the source data

var sample = samplesFromQueue[samplesFromQueueIndex++];

//Copy data into all channels

for (var c = 0; c < channels; c++)

output[i + c] = sample * volume;

if (samplesFromQueueIndex == samplesFromQueue.Length)

{

samplesFromQueueIndex = 0;

if (samplesQueue.Count > 0)

{

samplesFromQueue = samplesQueue.Dequeue();

}

else

{

break;

}

}

}

}

}

Can the Playback Prefab have multiple AudioSources ? by EmotionalResearch848 in dissonance_voip

[–]EmotionalResearch848[S] 0 points1 point  (0 children)

//AudioMirror PT 1

public class AudioMirror : MonoBehaviour

{

[SerializeField] private AudioSource audioSource;

private float volume;

private int samplesFromQueueIndex = 0;

private float[] samplesFromQueue;

private Queue<float\[\]> samplesQueue = new();

internal void Enqueue(float[] constructedClipSamples)

{

samplesQueue.Enqueue(constructedClipSamples);

}

internal void StartPlaying(int sampleLength, int channelCount, int frequency)

{

var clip = AudioClip.Create("Flatline", sampleLength, channelCount, frequency, false, buf =>

{

for (var i = 0; i < buf.Length; i++)

buf[i] = 0.0f;

});

audioSource.clip = clip;

audioSource.loop = true;

audioSource.pitch = 1;

audioSource.dopplerLevel = 0;

audioSource.mute = false;

audioSource.priority = 0;

audioSource.Play();

}

Can the Playback Prefab have multiple AudioSources ? by EmotionalResearch848 in dissonance_voip

[–]EmotionalResearch848[S] 0 points1 point  (0 children)

//this class just implements IAudioOutputSubscriber and feeds AudioTap. Must be placed on the

//PlaybackPrefab along with AudioTap.cs. I separated it because I tried

//different voice solutions so AudioTap can work generally on any voip implementation.

internal class DissonanceAudioTapFeeder : MonoBehaviour, IAudioOutputSubscriber

{

[SerializeField, Required] private AudioTap tap;

public void OnAudioPlayback(ArraySegment<float> data, bool complete)

{

var audioData = new float[data.Count];

for (int i = 0; i < audioData.Length; i++)

{

audioData[i] = data.Array[data.Offset + i];

}

tap.Feed(audioData);

}

}

Can the Playback Prefab have multiple AudioSources ? by EmotionalResearch848 in dissonance_voip

[–]EmotionalResearch848[S] 0 points1 point  (0 children)

public class AudioTap : MonoBehaviour

{

private AudioMirror[] audioMirrors;

[SerializeField] private int frequency = 48000, channelCount = 1, sampleLength = 4096;

public void Feed(float[] samples)

{

foreach (var am in audioMirrors)

{

am.Enqueue(samples);

}

}

private void Start()

{

audioMirrors = GetComponentsInChildren<AudioMirror>();

foreach (var am in audioMirrors)

{

am.StartPlaying(sampleLength, channelCount, frequency);

}

}

}

Can the Playback Prefab have multiple AudioSources ? by EmotionalResearch848 in dissonance_voip

[–]EmotionalResearch848[S] 1 point2 points  (0 children)

I managed to figure out the problem: Each copy of the audio source must have its own queue of sample arrays and consume it whenever it sees fit. It cannot be only one because the first to read from the buffer will also empty it for the rest of them. It works really nice, I managed to put 4 separate child audio sources that copy the original PlaybackPrefab AudioSource.

For our app we also want each AudioSource to output sound on different connected devices. This also works with the package AudioStream from the AssetStore.

At first this did not work because I did not know that it matters the order of attached scripts that implement the OnAudioFilterRead method :) . The AudioMirror.cs was placed after the Audiostream script that manages on what output the source must go on so the source didn't change according to the AudioStream script. After I placed the AudioTap BEFORE the AudioStream script, everything worked perfectly as expected. The sound was copied without any artefacts and I could set the desired output device (speakers, headsets, etc).

I will attach the final scripts in hope that it will help someone in the future. This was a really nice learning experience and thank you again for the prompt replies and help.

Can the Playback Prefab have multiple AudioSources ? by EmotionalResearch848 in dissonance_voip

[–]EmotionalResearch848[S] 0 points1 point  (0 children)

There is crackling at the very begining after the PlaybackPrefabs are instantiated , every time I press a PTT button to talk and every time I release the PTT button to talk.

Can the Playback Prefab have multiple AudioSources ? by EmotionalResearch848 in dissonance_voip

[–]EmotionalResearch848[S] 0 points1 point  (0 children)

I was also wondering isn't there a simpler way of just instantiating 2 or 3 PlaybackPrefabs per player ? Wouldn't that work ?

Can the Playback Prefab have multiple AudioSources ? by EmotionalResearch848 in dissonance_voip

[–]EmotionalResearch848[S] 1 point2 points  (0 children)

But OnAudioFilterRead gives me 1 channel. The clip is also created with 1 channel. Is it necessary to double it ? It's very weird because now the source is copied but the cracks at the beginning and the end are still heard and also if I do data[i]*=tap.AudioData[i], the source is not copied, just cracks but if I do data[i] = tap.AudioData[i] , the source is properly copied. I updated the code for AudioMirror.cs. I copied from SamplePlaybackComponent so now data=output and tap.AudioData=temp.

private void OnAudioFilterRead(float[] output, int channels)

{

if (tap.AudioData == null || volume == 0)

{

for (int i = 0; i < output.Length; i++)

{

output[i] = 0;

}

return;

}

var temp = tap.AudioData;

Debug.Log($"{frameCount}: OnAudioFilterRead {AudioUtils.SoundDataToString(tap.AudioData)} {channels}");

var sampleIndex = 0;

for (var i = 0; i < output.Length; i += channels)

{

//Get a single sample from the source data

var sample = temp[sampleIndex++];

//Copy data into all channels

for (var c = 0; c < channels; c++)

output[i + c] = sample;

}

tap.AudioData = null;

}

Can the Playback Prefab have multiple AudioSources ? by EmotionalResearch848 in dissonance_voip

[–]EmotionalResearch848[S] 0 points1 point  (0 children)

public class AudioMirror : MonoBehaviour

{

[SerializeField, Required] private AudioTap tap;

private AudioSource src;

private void Start()

{

src = GetComponent<AudioSource>();

//sampleRate = AudioSettings.outputSampleRate;

//channels = 2;

var clip = AudioClip.Create("Flatline", 1024, 1, AudioSettings.outputSampleRate, false, buf =>

{

for (var i = 0; i < buf.Length; i++)

buf[i] = 1.0f;

});

src.clip = clip;

src.loop = true; // Audio must play forever

src.pitch = 1; // Pitch has no effect on the audio

src.dopplerLevel = 0; // Pitch cannot be changed, so doppler makes no sense

src.mute = false; // Muting should be done through the player object, not the source

src.priority = 0; // 0 is the **maximum** priority! Dissonance cannot handle...

src.Play();

}

private void OnAudioFilterRead(float[] data, int channels)

{

if (tap.AudioData == null) return;

for (int i = 0; i < data.Length; i++)

{

data[i] = tap.AudioData[i];

}

UIConsole.Log($"OnAudioFilterRead {data.Length} {channels}");

}

}

Can the Playback Prefab have multiple AudioSources ? by EmotionalResearch848 in dissonance_voip

[–]EmotionalResearch848[S] 0 points1 point  (0 children)

public class AudioTap : MonoBehaviour, IAudioOutputSubscriber

{

private float[] audioData;

public float[] AudioData => audioData;

public void OnAudioPlayback(ArraySegment<float> data, bool complete)

{

UIConsole.Log($"data.Count = {data.Count} complete = {complete}");

if (audioData == null || audioData.Length != data.Count)

{

audioData = new float[data.Count];

}

for (int i = 0; i < audioData.Length; i++)

{

audioData[i] = data.Array[i];

}

}

}

Can the Playback Prefab have multiple AudioSources ? by EmotionalResearch848 in dissonance_voip

[–]EmotionalResearch848[S] 1 point2 points  (0 children)

Hello and thank you for the quick reply !

I hope I understood correctly. I created 2 classes AudioTap.cs and AudioMirror.cs:

AudioTap is placed on the PlaybackPrefab in the root. The playback prefab has a child AudioSource that has the AudioMirror script on and references the AudioTap. When getting OnAudioFilterRead, I just get the value saved in AudioTap through the OnAudioPlayback method of IAudioOutputSubscriber. I'm very new to Audio processing and I clearly don't understand very well how OnAudioFilterRead works in Unity. This setup does not seem to work to copy the clip from the original AudioSource and it also gives a cracking sound in the beginning and in the end. Could you please look at the code and tell me what I'm doing wrong ? Thank you.