So while I was working in Berlin doing my internship my colleague and I had an idea for a VR conference room application to look at CAD-Models. The project never went as far as I wanted it to go, but I ripped some parts out and made a standalone version of them.

Meet Heliograph! A shitty and as-barebones-as-it-gets VOIP implementation for Unity using vis2k’s Mirror Networking Library.

Is it bad? Yes!

Is it really bad? This was literally done in two days time! Of course it's bad!

Is it insecure? Hell yes!

Is it fun? Absolutely!

How does it work? Well, I’m reading the default microphones input (don’t you dare connect multiple microphones!😠) in HelioRecorder.

    [AddComponentMenu("Audio/Heliograph/HelioRecorder")]
    public class HelioRecorder : NetworkBehaviour
    {
        public MicrophoneSettings settings;
        private AudioClip clipToTransmit;
        private int lastSampleOffset;
        private HelioPlayer helioPlayer;

        private void Start()
        {
            if (!isLocalPlayer) return;
            helioPlayer = GetComponent<HelioPlayer>();
            clipToTransmit = Microphone.Start(null, true, 10, MicrophoneSettings.Frequency);
        }

        private void OnDisable()
        {
            if (!isLocalPlayer) return;
            Microphone.End(null);
        }

        private void FixedUpdate()
        {
            if (!isLocalPlayer) return;
            int currentMicSamplePosition = Microphone.GetPosition(null);
            int samplesToTransmit = GetSampleTransmissionCount(currentMicSamplePosition);
            if (samplesToTransmit > 0)
            {
                TransmitSamples(samplesToTransmit);
                lastSampleOffset = currentMicSamplePosition;
            }
        }

        private int GetSampleTransmissionCount(int currentMicrophoneSample)
        {
            int sampleTransmissionCount = currentMicrophoneSample - lastSampleOffset;
            if (sampleTransmissionCount < 0)
            {
                sampleTransmissionCount = (clipToTransmit.samples - lastSampleOffset) + currentMicrophoneSample;
            }
            return sampleTransmissionCount;
        }

        private void TransmitSamples(int sampleCountToTransmit)
        {
            float[] samplesToTransmit = new float[sampleCountToTransmit * clipToTransmit.channels];
            clipToTransmit.GetData(samplesToTransmit, lastSampleOffset);
            CmdSendAudio(new AudioPacket(samplesToTransmit));
        }

        [Command]
        public void CmdSendAudio(AudioPacket audio)
        {
            foreach (var connection in NetworkServer.connections)
            {
                if (connection.Value != connectionToClient)
                {
                    TargetPlayAudio(connection.Value, audio);
                }
            }
        }

        [TargetRpc]
        public void TargetPlayAudio(NetworkConnection target, AudioPacket audio)
        {
            helioPlayer.UpdateSoundSamples(audio);
        }
    }

This is the AudioPacket. It’s just a float array with the samples from the microphone. I’m sending this to the other clients.

[Serializable]
    public class AudioPacket
    {
        public float[] samples;

        public AudioPacket(float[] samples)
        {
            this.samples = samples;
        }
        public AudioPacket()
        {

        }
    }

This is the actual playback component. HelioPlayer receives the AudioPackets from other players and directly feeds them into a audioclip, which I then play via a normal AudioSource.

[AddComponentMenu("Audio/Heliograph/HelioPlayer")]
    public class HelioPlayer : MonoBehaviour
    {
        [SerializeField] private AudioSource audioSource;
        private int lastSamplePlayed;

        void OnEnable()
        {
            audioSource = gameObject.GetComponent<AudioSource>();
        }

        public void UpdateSoundSamples(AudioPacket sound)
        {
            if (!audioSource.isPlaying)
            {
                InitializeAudioSource();
            }

            audioSource.clip.SetData(sound.samples, lastSamplePlayed);

            if (!audioSource.isPlaying)
            {
                audioSource.PlayDelayed(0.1f);
            }

            lastSamplePlayed = (lastSamplePlayed + sound.samples.Length) % MicrophoneSettings.MaxAudioClipSamples;
        }

        private void InitializeAudioSource()
        {
            lastSamplePlayed = 0;
            audioSource.clip = AudioClip.Create("TransmittedAudio", MicrophoneSettings.MaxAudioClipSamples,
                MicrophoneSettings.AudioTransmissionChannels, MicrophoneSettings.Frequency, false);
        }
    }

If you’re interested you can clone and modify Heliograph from my Github. I would not recommend it, tho. 😁

Thanks for reading! 🎤