Advertisement
Guest User

Modified Voice Chat (FMOD + Mirror + Facepunch Steamworks)

a guest
Mar 23rd, 2022
55
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. using System;
  2. using Mirror;
  3. using Steamworks;
  4. using System.IO;
  5. using UnityEngine;
  6. using TMPro;
  7. using FMOD.Studio;
  8. using FMODUnity;
  9. using System.Runtime.InteropServices;
  10.  
  11. public class VoiceChat : NetworkBehaviour
  12. {
  13.     [SerializeField]
  14.     private MemoryStream output;
  15.     private MemoryStream stream;
  16.     private MemoryStream input;
  17.     private int optimalRate;
  18.     private int clipBufferSize;
  19.     private byte[] clipBuffer;
  20.  
  21.     [Header("Debug")]
  22.     [SerializeField] private Canvas chatGUI;
  23.     [SerializeField] private TMP_Text talkingText;
  24.     private string talkingTextNoSound = "No sound detected";
  25.     private string talkingTextHeard = "<b>Broadcasting Voice!</b>";
  26.  
  27.     [Header("Required Components")]
  28.     public EventReference voiceChatEvent;
  29.     private EventInstance voiceChatInstance;
  30.     FMOD.SOUND_PCMREAD_CALLBACK pcmReadCallback;
  31.     FMOD.Studio.EVENT_CALLBACK voiceEventCallback;
  32.     private FMOD.CREATESOUNDEXINFO exinfo;
  33.     private FMOD.Sound recordedSound;
  34.  
  35.     private void Start()
  36.     {
  37.         optimalRate = (int)SteamUser.OptimalSampleRate;
  38.         Debug.Log(optimalRate);
  39.         clipBufferSize = optimalRate * 5;
  40.         clipBuffer = new byte[clipBufferSize];
  41.  
  42.         stream = new MemoryStream();
  43.         output = new MemoryStream();
  44.         input = new MemoryStream();
  45.  
  46.         //Create sound for programmer instrument
  47.         exinfo.cbsize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(FMOD.CREATESOUNDEXINFO));
  48.         exinfo.numchannels = 1;
  49.         exinfo.format = FMOD.SOUND_FORMAT.PCM16;
  50.         exinfo.defaultfrequency = 24000;
  51.         exinfo.length = (uint)clipBufferSize;
  52.         pcmReadCallback = new FMOD.SOUND_PCMREAD_CALLBACK(PcmReadCallback);
  53.         exinfo.pcmreadcallback = pcmReadCallback;
  54.         FMODUnity.RuntimeManager.CoreSystem.createSound(exinfo.userdata, FMOD.MODE.LOOP_NORMAL | FMOD.MODE.OPENUSER, ref exinfo, out recordedSound);
  55.  
  56.         //Initialize Event Instance
  57.         voiceEventCallback = new FMOD.Studio.EVENT_CALLBACK(VoiceEventCallback);
  58.         voiceChatInstance = FMODUnity.RuntimeManager.CreateInstance(voiceChatEvent);
  59.         voiceChatInstance.setCallback(voiceEventCallback);
  60.         voiceChatInstance.start();
  61.  
  62.         if(isLocalPlayer) {
  63.             chatGUI.gameObject.SetActive(true);
  64.         }
  65.     }
  66.  
  67.     //Clear instances and callback reference
  68.     void OnDestroy() {
  69.         voiceChatInstance.setCallback(null);
  70.         voiceChatInstance.stop(FMOD.Studio.STOP_MODE.IMMEDIATE);
  71.         voiceChatInstance.release();
  72.         recordedSound.release();
  73.     }
  74.  
  75.     // User Input: Automatically send data to server in CmdVoice()
  76.     private void Update()
  77.     {
  78.         if(!isLocalPlayer) return;
  79.  
  80.         //Push to talk
  81.         SteamUser.VoiceRecord = Input.GetKey(KeyCode.V);
  82.  
  83.         //Update Text GUI when pushing
  84.         if(SteamUser.VoiceRecord) {
  85.             talkingText.enabled = true;
  86.         } else {
  87.             talkingText.enabled = false;
  88.         }
  89.  
  90.         //Send any recorded voice data
  91.         if (SteamUser.HasVoiceData)
  92.         {
  93.             int compressedWritten = SteamUser.ReadVoiceData(stream);
  94.             stream.Position = 0;
  95.  
  96.             CmdVoice(new ArraySegment<byte>(stream.GetBuffer(), 0, compressedWritten));
  97.            
  98.             //Update GUI to show voice detected
  99.             talkingText.text = talkingTextHeard;
  100.         } else {
  101.             talkingText.text = talkingTextNoSound;
  102.         }
  103.     }
  104.  
  105.     // Executed on Server: Broadcast voice data to all players
  106.     [Command(channel = 1)]
  107.     public void CmdVoice(ArraySegment<byte> compressed)
  108.     {
  109.         if(!isServer) return;
  110.         RpcVoiceData(compressed);
  111.     }
  112.  
  113.  
  114.     /* ### CALLED ON CLIENT WHEN DATA RECIEVED ### */
  115.     [ClientRpc(channel = 1)]
  116.     public void RpcVoiceData(ArraySegment<byte> compressed)
  117.     {
  118.         input.Write(compressed.ToArray(), 0, compressed.Count);
  119.         input.Position = 0;
  120.  
  121.         //Decompress voice to output buffer
  122.         int uncompressedWritten = SteamUser.DecompressVoice(input, compressed.Count, output);
  123.         input.Position = 0;
  124.         byte[] outputBuffer = output.GetBuffer();
  125.  
  126.         //Write data to Programmer Instrument
  127.         WriteToClip(outputBuffer, uncompressedWritten);
  128.         output.Position = 0;
  129.  
  130.         //Checking length of data
  131.         //Debug.Log(compressed.Count + " " + uncompressedWritten + " " + outputBuffer.Length);
  132.     }
  133.    
  134.     /* ### WRITES NEW AUDIO DATA INTO CLIP BUFFER ### */
  135.     [Client]
  136.     private void WriteToClip(byte[] uncompressed, int iSize)
  137.     {  
  138.         exinfo.cbsize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(FMOD.CREATESOUNDEXINFO));
  139.         exinfo.numchannels = 1;
  140.         exinfo.format = FMOD.SOUND_FORMAT.PCM16;
  141.         exinfo.defaultfrequency = 24000;
  142.         exinfo.length = (uint)iSize;
  143.        
  144.         FMODUnity.RuntimeManager.CoreSystem.createSound(uncompressed, FMOD.MODE.OPENMEMORY | FMOD.MODE.OPENRAW | FMOD.MODE.CREATESAMPLE, ref exinfo, out recordedSound);
  145.     }
  146.  
  147.     //PCM Read Callback, Not sure if i need this?
  148.     FMOD.RESULT PcmReadCallback(IntPtr sound, IntPtr data, uint datalen) {
  149.        
  150.         Debug.Log("PCM Read Callback Fired"); //Test
  151.         return FMOD.RESULT.OK;
  152.     }
  153.  
  154.     //Programmer Instrument Callback
  155.     [AOT.MonoPInvokeCallback(typeof(FMOD.Studio.EVENT_CALLBACK))]
  156.     FMOD.RESULT VoiceEventCallback(FMOD.Studio.EVENT_CALLBACK_TYPE type, IntPtr instancePtr, IntPtr parameterPtr)
  157.     {
  158.         switch (type)
  159.         {
  160.             case EVENT_CALLBACK_TYPE.CREATE_PROGRAMMER_SOUND:
  161.             {
  162.                 //get properties
  163.                 PROGRAMMER_SOUND_PROPERTIES properties = (PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure(parameterPtr, typeof(PROGRAMMER_SOUND_PROPERTIES));
  164.  
  165.                 //Assign Sound
  166.                 properties.sound = recordedSound.handle;
  167.                
  168.                 Marshal.StructureToPtr(properties, parameterPtr, false);
  169.                 break;
  170.             }
  171.             case FMOD.Studio.EVENT_CALLBACK_TYPE.DESTROY_PROGRAMMER_SOUND:
  172.             {
  173.                 var parameter = (FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure(parameterPtr, typeof(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES));
  174.                 var sound = new FMOD.Sound(parameter.sound);
  175.                 sound.release();
  176.                 break;
  177.             }
  178.         }
  179.         return FMOD.RESULT.OK;
  180.     }
  181. }
Advertisement
RAW Paste Data Copied
Advertisement