Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using Mirror;
- using Steamworks;
- using System.IO;
- using UnityEngine;
- using TMPro;
- using FMOD.Studio;
- using FMODUnity;
- using System.Runtime.InteropServices;
- public class VoiceChat : NetworkBehaviour
- {
- [SerializeField]
- private MemoryStream output;
- private MemoryStream stream;
- private MemoryStream input;
- private int optimalRate;
- private int clipBufferSize;
- private byte[] clipBuffer;
- [Header("Debug")]
- [SerializeField] private Canvas chatGUI;
- [SerializeField] private TMP_Text talkingText;
- private string talkingTextNoSound = "No sound detected";
- private string talkingTextHeard = "<b>Broadcasting Voice!</b>";
- [Header("Required Components")]
- public EventReference voiceChatEvent;
- private EventInstance voiceChatInstance;
- FMOD.SOUND_PCMREAD_CALLBACK pcmReadCallback;
- FMOD.Studio.EVENT_CALLBACK voiceEventCallback;
- private FMOD.CREATESOUNDEXINFO exinfo;
- private FMOD.Sound recordedSound;
- private void Start()
- {
- optimalRate = (int)SteamUser.OptimalSampleRate;
- Debug.Log(optimalRate);
- clipBufferSize = optimalRate * 5;
- clipBuffer = new byte[clipBufferSize];
- stream = new MemoryStream();
- output = new MemoryStream();
- input = new MemoryStream();
- //Create sound for programmer instrument
- exinfo.cbsize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(FMOD.CREATESOUNDEXINFO));
- exinfo.numchannels = 1;
- exinfo.format = FMOD.SOUND_FORMAT.PCM16;
- exinfo.defaultfrequency = 24000;
- exinfo.length = (uint)clipBufferSize;
- pcmReadCallback = new FMOD.SOUND_PCMREAD_CALLBACK(PcmReadCallback);
- exinfo.pcmreadcallback = pcmReadCallback;
- FMODUnity.RuntimeManager.CoreSystem.createSound(exinfo.userdata, FMOD.MODE.LOOP_NORMAL | FMOD.MODE.OPENUSER, ref exinfo, out recordedSound);
- //Initialize Event Instance
- voiceEventCallback = new FMOD.Studio.EVENT_CALLBACK(VoiceEventCallback);
- voiceChatInstance = FMODUnity.RuntimeManager.CreateInstance(voiceChatEvent);
- voiceChatInstance.setCallback(voiceEventCallback);
- voiceChatInstance.start();
- if(isLocalPlayer) {
- chatGUI.gameObject.SetActive(true);
- }
- }
- //Clear instances and callback reference
- void OnDestroy() {
- voiceChatInstance.setCallback(null);
- voiceChatInstance.stop(FMOD.Studio.STOP_MODE.IMMEDIATE);
- voiceChatInstance.release();
- recordedSound.release();
- }
- // User Input: Automatically send data to server in CmdVoice()
- private void Update()
- {
- if(!isLocalPlayer) return;
- //Push to talk
- SteamUser.VoiceRecord = Input.GetKey(KeyCode.V);
- //Update Text GUI when pushing
- if(SteamUser.VoiceRecord) {
- talkingText.enabled = true;
- } else {
- talkingText.enabled = false;
- }
- //Send any recorded voice data
- if (SteamUser.HasVoiceData)
- {
- int compressedWritten = SteamUser.ReadVoiceData(stream);
- stream.Position = 0;
- CmdVoice(new ArraySegment<byte>(stream.GetBuffer(), 0, compressedWritten));
- //Update GUI to show voice detected
- talkingText.text = talkingTextHeard;
- } else {
- talkingText.text = talkingTextNoSound;
- }
- }
- // Executed on Server: Broadcast voice data to all players
- [Command(channel = 1)]
- public void CmdVoice(ArraySegment<byte> compressed)
- {
- if(!isServer) return;
- RpcVoiceData(compressed);
- }
- /* ### CALLED ON CLIENT WHEN DATA RECIEVED ### */
- [ClientRpc(channel = 1)]
- public void RpcVoiceData(ArraySegment<byte> compressed)
- {
- input.Write(compressed.ToArray(), 0, compressed.Count);
- input.Position = 0;
- //Decompress voice to output buffer
- int uncompressedWritten = SteamUser.DecompressVoice(input, compressed.Count, output);
- input.Position = 0;
- byte[] outputBuffer = output.GetBuffer();
- //Write data to Programmer Instrument
- WriteToClip(outputBuffer, uncompressedWritten);
- output.Position = 0;
- //Checking length of data
- //Debug.Log(compressed.Count + " " + uncompressedWritten + " " + outputBuffer.Length);
- }
- /* ### WRITES NEW AUDIO DATA INTO CLIP BUFFER ### */
- [Client]
- private void WriteToClip(byte[] uncompressed, int iSize)
- {
- exinfo.cbsize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(FMOD.CREATESOUNDEXINFO));
- exinfo.numchannels = 1;
- exinfo.format = FMOD.SOUND_FORMAT.PCM16;
- exinfo.defaultfrequency = 24000;
- exinfo.length = (uint)iSize;
- FMODUnity.RuntimeManager.CoreSystem.createSound(uncompressed, FMOD.MODE.OPENMEMORY | FMOD.MODE.OPENRAW | FMOD.MODE.CREATESAMPLE, ref exinfo, out recordedSound);
- }
- //PCM Read Callback, Not sure if i need this?
- FMOD.RESULT PcmReadCallback(IntPtr sound, IntPtr data, uint datalen) {
- Debug.Log("PCM Read Callback Fired"); //Test
- return FMOD.RESULT.OK;
- }
- //Programmer Instrument Callback
- [AOT.MonoPInvokeCallback(typeof(FMOD.Studio.EVENT_CALLBACK))]
- FMOD.RESULT VoiceEventCallback(FMOD.Studio.EVENT_CALLBACK_TYPE type, IntPtr instancePtr, IntPtr parameterPtr)
- {
- switch (type)
- {
- case EVENT_CALLBACK_TYPE.CREATE_PROGRAMMER_SOUND:
- {
- //get properties
- PROGRAMMER_SOUND_PROPERTIES properties = (PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure(parameterPtr, typeof(PROGRAMMER_SOUND_PROPERTIES));
- //Assign Sound
- properties.sound = recordedSound.handle;
- Marshal.StructureToPtr(properties, parameterPtr, false);
- break;
- }
- case FMOD.Studio.EVENT_CALLBACK_TYPE.DESTROY_PROGRAMMER_SOUND:
- {
- var parameter = (FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure(parameterPtr, typeof(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES));
- var sound = new FMOD.Sound(parameter.sound);
- sound.release();
- break;
- }
- }
- return FMOD.RESULT.OK;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement