Newer
Older
HoloAnatomy / Assets / HoloToolkit / Sharing / Scripts / VoiceChat / MicrophoneTransmitter.cs
SURFACEBOOK2\jackwynne on 25 May 2018 11 KB v1
  1. // Copyright (c) Microsoft Corporation. All rights reserved.
  2. // Licensed under the MIT License. See LICENSE in the project root for license information.
  3.  
  4. using System;
  5. using System.Globalization;
  6. using System.Threading;
  7. using UnityEngine;
  8. using HoloToolkit.Unity;
  9. using HoloToolkit.Unity.InputModule;
  10.  
  11. namespace HoloToolkit.Sharing.VoiceChat
  12. {
  13. /// <summary>
  14. /// Transmits data from your microphone to other clients connected to a SessionServer. Requires any receiving client to be running the MicrophoneReceiver script.
  15. /// </summary>
  16. [RequireComponent(typeof(AudioSource))]
  17. public class MicrophoneTransmitter : MonoBehaviour
  18. {
  19. /// <summary>
  20. /// Which type of microphone/quality to access
  21. /// </summary>
  22. public MicStream.StreamCategory Streamtype = MicStream.StreamCategory.HIGH_QUALITY_VOICE;
  23.  
  24. /// <summary>
  25. /// You can boost volume here as desired. 1 is default but probably too quiet. You can change during operation.
  26. /// </summary>
  27. public float InputGain = 2;
  28.  
  29. /// <summary>
  30. /// Whether or not to send the microphone data across the network
  31. /// </summary>
  32. public bool ShouldTransmitAudio = true;
  33.  
  34. /// <summary>
  35. /// Whether other users should be able to hear the transmitted audio
  36. /// </summary>
  37. public bool Mute;
  38.  
  39. public Transform GlobalAnchorTransform;
  40.  
  41. public bool ShowInterPacketTime;
  42.  
  43. private DateTime timeOfLastPacketSend;
  44. private float worstTimeBetweenPackets;
  45.  
  46. private int sequenceNumber;
  47.  
  48. private int sampleRateType = 3; // 48000Hz
  49.  
  50. private AudioSource audioSource;
  51.  
  52. private bool hasServerConnection;
  53. private bool micStarted;
  54.  
  55. public const int AudioPacketSize = 960;
  56. private CircularBuffer micBuffer = new CircularBuffer(AudioPacketSize * 10 * 2 * 4, true);
  57. private byte[] packetSamples = new byte[AudioPacketSize * 4];
  58.  
  59. // bit packers
  60. private readonly BitManipulator versionPacker = new BitManipulator(0x7, 0); // 3 bits, 0 shift
  61. private readonly BitManipulator audioStreamCountPacker = new BitManipulator(0x38, 3); // 3 bits, 3 shift
  62. private readonly BitManipulator channelCountPacker = new BitManipulator(0x1c0, 6); // 3 bits, 6 shift
  63. private readonly BitManipulator sampleRatePacker = new BitManipulator(0x600, 9); // 2 bits, 9 shift
  64. private readonly BitManipulator sampleTypePacker = new BitManipulator(0x1800, 11); // 2 bits, 11 shift
  65. private readonly BitManipulator sampleCountPacker = new BitManipulator(0x7fe000, 13); // 10 bits, 13 shift
  66. private readonly BitManipulator codecTypePacker = new BitManipulator(0x1800000, 23); // 2 bits, 23 shift
  67. private readonly BitManipulator mutePacker = new BitManipulator(0x2000000, 25); // 1 bits, 25 shift
  68. private readonly BitManipulator sequenceNumberPacker = new BitManipulator(0x7C000000, 26); // 6 bits, 26 shift
  69.  
  70. private readonly Mutex audioDataMutex = new Mutex();
  71.  
  72. #region DebugVariables
  73. public bool HearSelf;
  74.  
  75. private readonly CircularBuffer testCircularBuffer = new CircularBuffer(48000 * 2 * 4 * 3, true);
  76. private AudioSource testSource;
  77. public AudioClip TestClip;
  78. public bool SaveTestClip;
  79. #endregion
  80.  
  81. private NetworkConnection GetActiveConnection()
  82. {
  83. NetworkConnection connection = null;
  84. var stage = SharingStage.Instance;
  85. if (stage && stage.Manager != null)
  86. {
  87. connection = stage.Manager.GetServerConnection();
  88. }
  89. if (connection == null || !connection.IsConnected())
  90. {
  91. return null;
  92. }
  93. return connection;
  94. }
  95.  
  96. private void Awake()
  97. {
  98. audioSource = GetComponent<AudioSource>();
  99.  
  100. int errorCode = MicStream.MicInitializeCustomRate((int)Streamtype, AudioSettings.outputSampleRate);
  101. CheckForErrorOnCall(errorCode);
  102. if (errorCode == 0 || errorCode == (int)MicStream.ErrorCodes.ALREADY_RUNNING)
  103. {
  104. if (CheckForErrorOnCall(MicStream.MicSetGain(InputGain)))
  105. {
  106. audioSource.volume = HearSelf ? 1.0f : 0.0f;
  107. micStarted = CheckForErrorOnCall(MicStream.MicStartStream(false, false));
  108. }
  109. }
  110. }
  111.  
  112. private void OnAudioFilterRead(float[] buffer, int numChannels)
  113. {
  114. try
  115. {
  116. audioDataMutex.WaitOne();
  117.  
  118. if (micStarted && hasServerConnection)
  119. {
  120. if (CheckForErrorOnCall(MicStream.MicGetFrame(buffer, buffer.Length, numChannels)))
  121. {
  122. int dataSize = buffer.Length * 4;
  123. if (micBuffer.Write(buffer, 0, dataSize) != dataSize)
  124. {
  125. Debug.LogError("Send buffer filled up. Some audio will be lost.");
  126. }
  127. }
  128. }
  129. }
  130. catch (Exception e)
  131. {
  132. Debug.LogError(e.Message);
  133. }
  134. finally
  135. {
  136. audioDataMutex.ReleaseMutex();
  137. }
  138. }
  139.  
  140. private void Update()
  141. {
  142. CheckForErrorOnCall(MicStream.MicSetGain(InputGain));
  143. audioSource.volume = HearSelf ? 1.0f : 0.0f;
  144.  
  145. try
  146. {
  147. audioDataMutex.WaitOne();
  148.  
  149. var connection = GetActiveConnection();
  150. hasServerConnection = (connection != null);
  151. if (hasServerConnection)
  152. {
  153. while (micBuffer.UsedCapacity >= 4 * AudioPacketSize)
  154. {
  155. TransmitAudio(connection);
  156. }
  157. }
  158. }
  159. catch (Exception e)
  160. {
  161. Debug.LogError(e.Message);
  162. }
  163. finally
  164. {
  165. audioDataMutex.ReleaseMutex();
  166. }
  167.  
  168. #region DebugInfo
  169. if (SaveTestClip && testCircularBuffer.UsedCapacity == testCircularBuffer.TotalCapacity)
  170. {
  171. float[] testBuffer = new float[testCircularBuffer.UsedCapacity / 4];
  172. testCircularBuffer.Read(testBuffer, 0, testBuffer.Length * 4);
  173. testCircularBuffer.Reset();
  174. TestClip = AudioClip.Create("testclip", testBuffer.Length / 2, 2, 48000, false);
  175. TestClip.SetData(testBuffer, 0);
  176. if (!testSource)
  177. {
  178. GameObject testObj = new GameObject("testclip");
  179. testObj.transform.parent = transform;
  180. testSource = testObj.AddComponent<AudioSource>();
  181. }
  182. testSource.PlayClip(TestClip);
  183. SaveTestClip = false;
  184. }
  185. #endregion
  186. }
  187.  
  188. private void TransmitAudio(NetworkConnection connection)
  189. {
  190. micBuffer.Read(packetSamples, 0, 4 * AudioPacketSize);
  191. SendFixedSizedChunk(connection, packetSamples, packetSamples.Length);
  192.  
  193. if (SaveTestClip)
  194. {
  195. testCircularBuffer.Write(packetSamples, 0, packetSamples.Length);
  196. }
  197. }
  198.  
  199. private void SendFixedSizedChunk(NetworkConnection connection, byte[] data, int dataSize)
  200. {
  201. DateTime currentTime = DateTime.Now;
  202. float seconds = (float)(currentTime - timeOfLastPacketSend).TotalSeconds;
  203. timeOfLastPacketSend = currentTime;
  204. if (seconds < 10.0)
  205. {
  206. if (worstTimeBetweenPackets < seconds)
  207. {
  208. worstTimeBetweenPackets = seconds;
  209. }
  210.  
  211. if (ShowInterPacketTime)
  212. {
  213. Debug.LogFormat("Microphone: Milliseconds since last sent: {0}, Worst: {1}",
  214. (seconds * 1000.0).ToString(CultureInfo.InvariantCulture),
  215. (worstTimeBetweenPackets * 1000.0).ToString(CultureInfo.InvariantCulture));
  216. }
  217. }
  218.  
  219. int clientId = SharingStage.Instance.Manager.GetLocalUser().GetID();
  220.  
  221. // pack the header
  222. NetworkOutMessage msg = connection.CreateMessage((byte)MessageID.AudioSamples);
  223.  
  224. int dataCountFloats = dataSize / 4;
  225.  
  226. msg.Write((byte)5); // 8 byte header size
  227.  
  228. Int32 pack = 0;
  229. versionPacker.SetBits(ref pack, 1); // version
  230. audioStreamCountPacker.SetBits(ref pack, 1); // AudioStreamCount
  231. channelCountPacker.SetBits(ref pack, 1); // ChannelCount
  232. sampleRatePacker.SetBits(ref pack, sampleRateType); // SampleRate: 1 = 16000, 3 = 48000
  233. sampleTypePacker.SetBits(ref pack, 0); // SampleType
  234. sampleCountPacker.SetBits(ref pack, dataCountFloats); // SampleCount (data count is in bytes and the actual data is in floats, so div by 4)
  235. codecTypePacker.SetBits(ref pack, 0); // CodecType
  236. mutePacker.SetBits(ref pack, Mute ? 1 : 0);
  237. sequenceNumberPacker.SetBits(ref pack, sequenceNumber++);
  238. sequenceNumber %= 32;
  239.  
  240. msg.Write(pack); // the packed bits
  241.  
  242. // This is where stream data starts. Write all data for one stream
  243.  
  244. msg.Write(0.0f); // average amplitude. Not needed in direction from client to server.
  245. msg.Write(clientId); // non-zero client ID for this client.
  246.  
  247. // HRTF position bits
  248.  
  249. Vector3 cameraPosRelativeToGlobalAnchor = Vector3.zero;
  250. Vector3 cameraDirectionRelativeToGlobalAnchor = Vector3.zero;
  251.  
  252. if (GlobalAnchorTransform != null)
  253. {
  254. cameraPosRelativeToGlobalAnchor = MathUtils.TransformPointFromTo(
  255. null,
  256. GlobalAnchorTransform,
  257. CameraCache.Main.transform.position);
  258.  
  259. cameraDirectionRelativeToGlobalAnchor = MathUtils.TransformDirectionFromTo(
  260. null,
  261. GlobalAnchorTransform,
  262. CameraCache.Main.transform.position);
  263. }
  264.  
  265. cameraPosRelativeToGlobalAnchor.Normalize();
  266. cameraDirectionRelativeToGlobalAnchor.Normalize();
  267.  
  268. // Camera position
  269. msg.Write(cameraPosRelativeToGlobalAnchor.x);
  270. msg.Write(cameraPosRelativeToGlobalAnchor.y);
  271. msg.Write(cameraPosRelativeToGlobalAnchor.z);
  272.  
  273. // HRTF direction bits
  274. msg.Write(cameraDirectionRelativeToGlobalAnchor.x);
  275. msg.Write(cameraDirectionRelativeToGlobalAnchor.y);
  276. msg.Write(cameraDirectionRelativeToGlobalAnchor.z);
  277.  
  278. msg.WriteArray(data, (uint)dataCountFloats * 4);
  279.  
  280. connection.Send(msg, MessagePriority.Immediate, MessageReliability.ReliableOrdered, MessageChannel.Audio, true);
  281. }
  282.  
  283. private void OnDestroy()
  284. {
  285. CheckForErrorOnCall(MicStream.MicDestroy());
  286. }
  287.  
  288. private bool CheckForErrorOnCall(int returnCode)
  289. {
  290. return MicStream.CheckForErrorOnCall(returnCode);
  291. }
  292.  
  293. #if DOTNET_FX
  294. // on device, deal with all the ways that we could suspend our program in as few lines as possible
  295. private void OnApplicationPause(bool pause)
  296. {
  297. if (pause)
  298. {
  299. CheckForErrorOnCall(MicStream.MicPause());
  300. }
  301. else
  302. {
  303. CheckForErrorOnCall(MicStream.MicResume());
  304. }
  305. }
  306.  
  307. private void OnApplicationFocus(bool focused)
  308. {
  309. OnApplicationPause(!focused);
  310. }
  311.  
  312. private void OnDisable()
  313. {
  314. OnApplicationPause(true);
  315. }
  316.  
  317. private void OnEnable()
  318. {
  319. OnApplicationPause(false);
  320. }
  321. #endif
  322. }
  323. }