s4l4x
1/19/2016 - 12:07 AM

Gets PCM audio data from a CoherentUI browser in Unity3D and plays it through a Unity audio filter.

Gets PCM audio data from a CoherentUI browser in Unity3D and plays it through a Unity audio filter.

using UnityEngine;
using System.Collections;
using Coherent.UI;
using System.IO;
using System;
using System.Runtime.InteropServices;

[RequireComponent(typeof(CoherentUIView))]
[RequireComponent(typeof(AudioSource))]
unsafe public class CoherentAudioPlayerFilter : MonoBehaviour
{
    public int BufferSize = 8192;
    [Range(0, 2f)] public float gain = 1f;

    View m_View;
    UnityViewListener m_ViewListener;
    new AudioSource audio;    

    int m_StreamId;
    int m_BitDepth;
    int m_Frequency;
    int m_Channels;

    bool isPlaying;

    void Start ()
    {
        audio = GetComponent<AudioSource>();
        GetComponent<CoherentUIView>().OnViewCreated += InitBrowser;
    }

    void InitBrowser(View view)
    {
        m_View = view;
        m_ViewListener = GetComponent<CoherentUIView>().Listener;

        m_ViewListener.AudioStreamCreated += this.OnAudioStreamCreated;
        m_ViewListener.AudioStreamPlay += this.OnAudioStreamPlay;
        m_ViewListener.AudioStreamPause += this.OnAudioStreamPause;
        m_ViewListener.AudioStreamClose += this.OnAudioStreamClose;
    }

    void OnAudioFilterRead(float[] data, int channels)
    {
        if (!isPlaying) return;

        int numBytes = data.Length * 2;
        IntPtr m_AudioBuffer = Marshal.AllocHGlobal(numBytes);

        // When you need more data to play for stream #streamID
        int timeout = 0; // A zero timeout means block until data is received
        int received = m_View.GetAudioData(m_StreamId, m_AudioBuffer, numBytes, timeout);
        if (received <= 0)
        {
            // Negative values indicate an error; Zero is a valid return value if no data is present
            return;
        }

        // Get a byte pointer from the unmanaged memory block. 
        byte* audioBufferBytePtr = (byte*)m_AudioBuffer.ToPointer();
        UnmanagedMemoryStream readStream = new UnmanagedMemoryStream(audioBufferBytePtr, received, numBytes, FileAccess.Read);
        // Create a byte array to hold data from unmanaged memory. 
        byte[] managedData = new byte[received];

        // Read from unmanaged memory to the byte array.
        readStream.Read(managedData, 0, received);
        readStream.Close();
        // You now have your data in *managedData* and you can set it to the audio clip.

        //WriteToConsole("manageddata", ArrayToString(managedData));

        float[] audioFloats = PCMBytesToFloats(managedData);

        for (int i = 0; i < audioFloats.Length; i++)
        {
            data[i] = audioFloats[i] * gain;
        }
    }

    public void OnAudioStreamCreated(int streamId, int channels, int bitDepth, int frequency)
    {
        Debug.Log("sid = " + streamId + " channels = " + channels + " bitdepth = " + bitDepth + " frequency = " + frequency);
        m_StreamId = streamId;
        m_Channels = channels;
        m_BitDepth = bitDepth;
        m_Frequency = frequency;

        AudioClip audioClip = AudioClip.Create("browser-audio-" + streamId, BufferSize, m_Channels, 44000, false);
        audio.clip = audioClip;
    }

    public void OnAudioStreamPlay(int streamId)
    {
        Debug.Log("on audio stream play");
        isPlaying = true;
        audio.Play();
    }

    public void OnAudioStreamPause(int streamId)
    {
        Debug.Log("on audio stream pause");
        isPlaying = false;
        audio.Pause();
    }

    public void OnAudioStreamClose(int streamId)
    {
        Debug.Log("on audio stream close");
        isPlaying = false;
        audio.Stop();
    }

    static float[] PCMBytesToFloats(byte[] bytes)
    {
        float[] floats = new float[bytes.Length / 2];
        for (int i = 0; i < bytes.Length; i += 2)
        {
            short s = BitConverter.ToInt16(bytes, i); // convert 2 bytes to short
            floats[i / 2] = ((float)s) / (float)32768; // convert short to float
        }
        return floats;
    }
  }
}