//----------------------------------------------------------------------------- // Copyright 2015-2023 RenderHeads Ltd. All rights reserved. //----------------------------------------------------------------------------- using System.Collections.Generic; using UnityEngine; using System; namespace RenderHeads.Media.AVProVideo { /// /// A singleton to handle multiple instances of the AudioOutput component /// public class AudioOutputManager { private static AudioOutputManager _instance = null; public static AudioOutputManager Instance { get { if (_instance == null) { _instance = new AudioOutputManager(); } return _instance; } } protected class PlayerInstance { public HashSet outputs; public float[] pcmData; public bool isPcmDataReady; } private Dictionary _instances; private AudioOutputManager() { _instances = new Dictionary(); } public void RequestAudio(AudioOutput outputComponent, MediaPlayer mediaPlayer, float[] audioData, int audioChannelCount, int channelMask, AudioOutput.AudioOutputMode audioOutputMode, bool supportPositionalAudio) { if (mediaPlayer == null || mediaPlayer.Control == null) { if (supportPositionalAudio) { ZeroAudio(audioData, 0); } return; } int channels = mediaPlayer.Control.GetAudioChannelCount(); if (channels <= 0) { if (supportPositionalAudio) { ZeroAudio(audioData, 0); } return; } // total samples requested should be multiple of channels Debug.Assert(audioData.Length % audioChannelCount == 0); // Find or create an instance PlayerInstance instance = null; if (!_instances.TryGetValue(mediaPlayer, out instance)) { instance = _instances[mediaPlayer] = new PlayerInstance() { outputs = new HashSet(), pcmData = null }; } // requests data if it hasn't been requested yet for the current cycle if (instance.outputs.Count == 0 || instance.outputs.Contains(outputComponent) || instance.pcmData == null) { instance.outputs.Clear(); int actualDataRequired = (audioData.Length * channels) / audioChannelCount; if (instance.pcmData == null || actualDataRequired != instance.pcmData.Length) { instance.pcmData = new float[actualDataRequired]; } instance.isPcmDataReady = GrabAudio(mediaPlayer, instance.pcmData, channels); instance.outputs.Add(outputComponent); } if (instance.isPcmDataReady) { // calculate how many samples and what channels are needed and then copy over the data int samples = Math.Min(audioData.Length / audioChannelCount, instance.pcmData.Length / channels); int storedPos = 0; int requestedPos = 0; // multiple mode, copies over audio from desired channels into the same channels on the audiosource if (audioOutputMode == AudioOutput.AudioOutputMode.MultipleChannels) { int lesserChannels = Math.Min(channels, audioChannelCount); if (!supportPositionalAudio) { for (int i = 0; i < samples; ++i) { for (int j = 0; j < lesserChannels; ++j) { if ((1 << j & channelMask) > 0) { audioData[requestedPos + j] = instance.pcmData[storedPos + j]; } } storedPos += channels; requestedPos += audioChannelCount; } } else { for (int i = 0; i < samples; ++i) { for (int j = 0; j < lesserChannels; ++j) { if ((1 << j & channelMask) > 0) { audioData[requestedPos + j] *= instance.pcmData[storedPos + j]; } } storedPos += channels; requestedPos += audioChannelCount; } } } //Mono mode, copies over single channel to all output channels else if (audioOutputMode == AudioOutput.AudioOutputMode.OneToAllChannels) { int desiredChannel = 0; for (int i = 0; i < 8; ++i) { if ((channelMask & (1 << i)) > 0) { desiredChannel = i; break; } } if (desiredChannel < channels) { if (!supportPositionalAudio) { for (int i = 0; i < samples; ++i) { for (int j = 0; j < audioChannelCount; ++j) { audioData[requestedPos + j] = instance.pcmData[storedPos + desiredChannel]; } storedPos += channels; requestedPos += audioChannelCount; } } else { for (int i = 0; i < samples; ++i) { for (int j = 0; j < audioChannelCount; ++j) { audioData[requestedPos + j] *= instance.pcmData[storedPos + desiredChannel]; } storedPos += channels; requestedPos += audioChannelCount; } } } } // If there is left over audio if (supportPositionalAudio && requestedPos != audioData.Length) { // Zero the remaining audio data otherwise there are pops ZeroAudio(audioData, requestedPos); } } else { if (supportPositionalAudio) { // Zero the remaining audio data otherwise there are pops ZeroAudio(audioData, 0); } } } private void ZeroAudio(float[] audioData, int startPosition) { for (int i = startPosition; i < audioData.Length; i++) { audioData[i] = 0f; } } private bool GrabAudio(MediaPlayer player, float[] audioData, int channelCount) { return (0 != player.Control.GrabAudio(audioData, audioData.Length, channelCount)); } } }