wit-ai / wit

Natural Language Interface for apps and devices
https://wit.ai/
931 stars 91 forks source link

Multiple Wit.Ai configurations used in the same game seem not to work. I am trying this with Unity, but gets stuck on the first one. If anyone can help, much obliged. #2588

Open kuffcof opened 1 year ago

kuffcof commented 1 year ago

Do you want to request a feature, report a bug, or ask a question about wit?

What is the current behavior?

If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem.

What is the expected behavior?

If applicable, what is the App ID where you are experiencing this issue? If you do not provide this, we cannot help.

kuffcof commented 1 year ago

Do you want to request a feature, report a bug, or ask a question about wit?

What is the current behavior?

If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem.

What is the expected behavior?

If applicable, what is the App ID where you are experiencing this issue? If you do not provide this, we cannot help.

All this does not apply, there isn't an ID. There are 24.

ChrisyShine commented 1 year ago

@kuffcof Can you provide more details of the problem? Maybe with screenshots? And you are using multiple wit apps in a single game?

kuffcof commented 1 year ago

Thank you very much for your reply.

Yes, precisely. Due to the data base structure we use many small configurations of wit. For some reason, when I go back to the main menu and select another level(which implies another wit config), the thing goes blank - error, no wit config. They do work well by their own.

I will add a video example of the problem, but can t right now.

Again, Thank you very much. (It could also be a Unity problem, so that would be on my end, but I am not sure where the problem is)

On Tue, 7 Feb 2023, 20:22 Christina Zheng, @.***> wrote:

@kuffcof https://github.com/kuffcof Can you provide more details of the problem? Maybe with screenshots? And you are using multiple wit apps in a single game?

— Reply to this email directly, view it on GitHub https://github.com/wit-ai/wit/issues/2588#issuecomment-1421323496, or unsubscribe https://github.com/notifications/unsubscribe-auth/AQJDTZMBI4DWQQ5EYSGRTO3WWKOGXANCNFSM6AAAAAAUUIP5DE . You are receiving this because you were mentioned.Message ID: @.***>

kuffcof commented 1 year ago

The message above got sliced.

Thank you very much for your reply.

Yes, precisely. Due to the data base structure we use many small configurations of wit. For some reason, when I go back to the main menu and select another level(which implies another wit config), the thing goes blank - error, no wit config. They do work well by their own.

This is a better version.

I will add a video example of the problem, but can t right now.

Again, Thank you very much. (It could also be a Unity problem, so that would be on my end, but I am not sure where the problem is)

yolanother commented 1 year ago

@kuffcof how are you changing the Wit config? Did you set up prefabs with individual AppVoiceExperiences (assuming you're using Voice SDK - recommended). Are you setting up the wit config after the scene loads by setting the value on the app voice experience directly?

kuffcof commented 1 year ago

Hello, thank you guys for trying to help with the issue.

Further investigating shows that the WitConfiguration loads ok. For some reason, when I do change the scene, and it correctly loads a new Wit Configuration, I get an error regarding the AudioBuffer

NullReferenceException: Object reference not set to an instance of an object Facebook.WitAi.WitService.OnEnable () (at Assets/Oculus/Voice/Lib/Wit.ai/Scripts/Runtime/WitService.cs:156) UnityEngine.GameObject:AddComponent() Facebook.WitAi.Wit:Awake() (at Assets/Oculus/Voice/Lib/Wit.ai/Scripts/Runtime/Wit.cs:76) UnityEngine.GameObject:AddComponent() Oculus.Voice.AppVoiceExperience:RevertToWitUnity() (at Assets/Oculus/Voice/Scripts/Runtime/Service/AppVoiceExperience.cs:205) Oculus.Voice.AppVoiceExperience:InitVoiceSDK() (at Assets/Oculus/Voice/Scripts/Runtime/Service/AppVoiceExperience.cs:191) Oculus.Voice.AppVoiceExperience:OnEnable() (at Assets/Oculus/Voice/Scripts/Runtime/Service/AppVoiceExperience.cs:224)

Actually, at line 76, the first one it is related with witService = gameObject.AddComponent();

So I guess that actually the witservece is not loading.

They are all related with AudioBuffer.Instance.Events, as I can not click "Activate", in order to speak.

I think this should help narrowing it down. For some reason this AudioBuffer Instance is the problem. (

Thank you.

kuffcof commented 1 year ago

Hello again.

I found a solution, not elegant at all and it required a lot of testing. Yesterday a new Oculus Integration package was released, an update. Using that update, I had no troubles with the Audio Buffer, but overall it wasn´t sending messages to WIT.

So what I do, I reversed to the previous version and only updated the code in the AudioBuffer, which now does not get destroyed when changing scenes.

If you would like to take a look, here is the code. All // is the previous one. I can´t make much sense of it all.

/*

using System.Collections; using System.Collections.Generic; using Facebook.WitAi.Events; using Facebook.WitAi.Interfaces; using Facebook.WitAi.Lib; using UnityEngine;

namespace Facebook.WitAi.Data {

// THE PREVIOUS ONE 
//    public class AudioBuffer : MonoBehaviour
//    {
//        #region Singleton
//        private static AudioBuffer _instance;
//        private static bool _instanceInit = false;
//        public static AudioBuffer Instance
//        {
//            get
//            {
//                if (!_instance) _instance = FindObjectOfType<AudioBuffer>();
//                if (!_instance && !_instanceInit)
//                {
//                    var audioBufferObject = new GameObject("AudioBuffer");
//                    _instance = audioBufferObject.AddComponent<AudioBuffer>();
//                }
//                return _instance;
//            }
//        }
//        #endregion

//        [SerializeField] private bool alwaysRecording;
//        [SerializeField] private AudioBufferConfiguration audioBufferConfiguration = new AudioBufferConfiguration();
//        [SerializeField] private AudioBufferEvents events = new AudioBufferEvents();

//        public AudioBufferEvents Events => events;

//        private IAudioInputSource _micInput;
//        private RingBuffer<byte> _micDataBuffer;

//        private byte[] _byteDataBuffer;

//        private HashSet<Component> _activeRecorders = new HashSet<Component>();

//        public bool IsRecording(Component component) => _activeRecorders.Contains(component);
//        public bool IsInputAvailable => _micInput.IsInputAvailable;
//        public void CheckForInput() => _micInput.CheckForInput();
//        public AudioEncoding AudioEncoding => _micInput.AudioEncoding;

//        private void Awake()
//        {
//            _instance = this;
//            _instanceInit = true;
//            // Check this gameobject & it's children for audio input
//            _micInput = gameObject.GetComponentInChildren<IAudioInputSource>();
//            // Check all roots for Mic Input JIC
//            if (_micInput == null)
//            {
//                foreach (var root in gameObject.scene.GetRootGameObjects())
//                {
//                    _micInput = root.GetComponentInChildren<IAudioInputSource>();
//                    if (_micInput != null)
//                    {
//                        break;
//                    }
//                }
//            }
//            // Use default mic script
//            if (_micInput == null)
//            {
//                _micInput = gameObject.AddComponent<Mic>();
//            }

//            InitializeMicDataBuffer();
//        }

//        private void OnEnable()
//        {
//#if UNITY_EDITOR
//            // Make sure we have a mic input after a script recompile
//            if (null == _micInput)
//            {
//                _micInput = GetComponent<IAudioInputSource>();
//            }
//#endif

//            _micInput.OnSampleReady += OnMicSampleReady;

//            if (alwaysRecording) StartRecording(this);
//        }

//        // Remove mic delegates
//        private void OnDisable()
//        {
//            _micInput.OnSampleReady -= OnMicSampleReady;

//            if (alwaysRecording) StopRecording(this);

//            _instanceInit = false;
//        }

//        // Callback for mic sample ready
//        private void OnMicSampleReady(int sampleCount, float[] sample, float levelMax)
//        {
//            events.OnMicLevelChanged.Invoke(levelMax);

//            var marker = CreateMarker();
//            Convert(sample);
//            if (null != events.OnByteDataReady)
//            {
//                marker.Clone().ReadIntoWriters(events.OnByteDataReady.Invoke);
//            }
//            events.OnSampleReady?.Invoke(marker, levelMax);
//        }

//        // Generate mic data buffer if needed
//        private void InitializeMicDataBuffer()
//        {
//            if (null == _micDataBuffer && audioBufferConfiguration.micBufferLengthInSeconds > 0)
//            {
//                var bufferSize = (int)Mathf.Ceil(2 *
//                                                  audioBufferConfiguration
//                                                      .micBufferLengthInSeconds * 1000 *
//                                                  audioBufferConfiguration.sampleLengthInMs);
//                if (bufferSize <= 0)
//                {
//                    bufferSize = 1024;
//                }
//                _micDataBuffer = new RingBuffer<byte>(bufferSize);
//            }
//        }

//        // Convert
//        private void Convert(float[] samples)
//        {
//            var sampleCount = samples.Length;
//            const int rescaleFactor = 32767; //to convert float to Int16

//            for (int i = 0; i < sampleCount; i++)
//            {
//                short data = (short)(samples[i] * rescaleFactor);
//                _micDataBuffer.Push((byte)data);
//                _micDataBuffer.Push((byte)(data >> 8));
//            }
//        }

//        public RingBuffer<byte>.Marker CreateMarker()
//        {
//            return _micDataBuffer.CreateMarker();
//        }

//        /// <summary>
//        /// Creates a marker with an offset
//        /// </summary>
//        /// <param name="offset">Number of seconds to offset the marker by</param>
//        /// <returns></returns>
//        public RingBuffer<byte>.Marker CreateMarker(float offset)
//        {
//            var samples = (int)(AudioEncoding.samplerate * offset);
//            return _micDataBuffer.CreateMarker(samples);
//        }

//        public void StartRecording(Component component)
//        {
//            StartCoroutine(WaitForMicToStart(component));
//        }

//        private IEnumerator WaitForMicToStart(Component component)
//        {
//            yield return new WaitUntil(() => null != _micInput);
//            yield return new WaitUntil(() => _micInput.IsInputAvailable);

//            _activeRecorders.Add(component);
//            if (!_micInput.IsRecording)
//            {
//                _micInput.StartRecording(audioBufferConfiguration.sampleLengthInMs);
//            }

//            if (component is IVoiceEventProvider v)
//            {
//                v.VoiceEvents.OnStartListening?.Invoke();
//            }
//        }

//        public void StopRecording(Component component)
//        {
//            _activeRecorders.Remove(component);
//            if (_activeRecorders.Count == 0)
//            {
//                _micInput.StopRecording();
//            }

//            if (component is IVoiceEventProvider v)
//            {
//                v.VoiceEvents.OnStoppedListening?.Invoke();
//            }
//        }
//    }

public class AudioBuffer : MonoBehaviour
{
    #region Singleton
    private static AudioBuffer _instance;
    private static bool _instanceInit = false;
    public static AudioBuffer Instance
    {
        get
        {
            if (!_instance) _instance = FindObjectOfType<AudioBuffer>();
            if (!_instance && !_instanceInit)
            {
                var audioBufferObject = new GameObject("AudioBuffer");
                _instance = audioBufferObject.AddComponent<AudioBuffer>();
            }
            return _instance;
        }
    }
    #endregion

    [SerializeField] private bool alwaysRecording;
    [SerializeField] private AudioBufferConfiguration audioBufferConfiguration = new AudioBufferConfiguration();
    [SerializeField] private AudioBufferEvents events = new AudioBufferEvents();

    public AudioBufferEvents Events => events;

    private IAudioInputSource _micInput;
    private RingBuffer<byte> _micDataBuffer;

    private byte[] _byteDataBuffer;

    private HashSet<Component> _activeRecorders = new HashSet<Component>();

    public bool IsRecording(Component component) => _activeRecorders.Contains(component);
    public bool IsInputAvailable => _micInput.IsInputAvailable;
    public void CheckForInput() => _micInput.CheckForInput();
    public AudioEncoding AudioEncoding => _micInput.AudioEncoding;

    private void Awake()
    {
        _instance = this;
        _instanceInit = true;
        // Check this gameobject & it's children for audio input
        _micInput = gameObject.GetComponentInChildren<IAudioInputSource>();
        // Check all roots for Mic Input JIC
        if (_micInput == null)
        {
            foreach (var root in gameObject.scene.GetRootGameObjects())
            {
                _micInput = root.GetComponentInChildren<IAudioInputSource>();
                if (_micInput != null)
                {
                    break;
                }
            }
        }
        // Use default mic script
        if (_micInput == null)
        {
            _micInput = gameObject.AddComponent<Mic>();
        }

        InitializeMicDataBuffer();
    }

    private void OnEnable()
    {

if UNITY_EDITOR

        // Make sure we have a mic input after a script recompile
        if (null == _micInput)
        {
            _micInput = GetComponent<IAudioInputSource>();
        }

endif

        _micInput.OnSampleReady += OnMicSampleReady;

        if (alwaysRecording) StartRecording(this);
    }

    // Remove mic delegates
    private void OnDisable()
    {
        _micInput.OnSampleReady -= OnMicSampleReady;

        if (alwaysRecording) StopRecording(this);

        _instanceInit = false;
    }

    // Callback for mic sample ready
    private void OnMicSampleReady(int sampleCount, float[] sample, float levelMax)
    {
        events.OnMicLevelChanged.Invoke(levelMax);

        var marker = CreateMarker();
        Convert(sample);
        if (null != events.OnByteDataReady)
        {
            marker.Clone().ReadIntoWriters(events.OnByteDataReady.Invoke);
        }
        events.OnSampleReady?.Invoke(marker, levelMax);
    }

    // Generate mic data buffer if needed
    private void InitializeMicDataBuffer()
    {
        if (null == _micDataBuffer && audioBufferConfiguration.micBufferLengthInSeconds > 0)
        {
            var bufferSize = (int)Mathf.Ceil(2 *
                                              audioBufferConfiguration
                                                  .micBufferLengthInSeconds * 1000 *
                                              audioBufferConfiguration.sampleLengthInMs);
            if (bufferSize <= 0)
            {
                bufferSize = 1024;
            }
            _micDataBuffer = new RingBuffer<byte>(bufferSize);
        }
    }

    // Convert
    private void Convert(float[] samples)
    {
        var sampleCount = samples.Length;
        const int rescaleFactor = 32767; //to convert float to Int16

        for (int i = 0; i < sampleCount; i++)
        {
            short data = (short)(samples[i] * rescaleFactor);
            _micDataBuffer.Push((byte)data);
            _micDataBuffer.Push((byte)(data >> 8));
        }
    }

    public RingBuffer<byte>.Marker CreateMarker()
    {
        return _micDataBuffer.CreateMarker();
    }

    /// <summary>
    /// Creates a marker with an offset
    /// </summary>
    /// <param name="offset">Number of seconds to offset the marker by</param>
    /// <returns></returns>
    public RingBuffer<byte>.Marker CreateMarker(float offset)
    {
        var samples = (int)(AudioEncoding.samplerate * offset);
        return _micDataBuffer.CreateMarker(samples);
    }

    public void StartRecording(Component component)
    {
        StartCoroutine(WaitForMicToStart(component));
    }

    private IEnumerator WaitForMicToStart(Component component)
    {
        yield return new WaitUntil(() => null != _micInput);
        yield return new WaitUntil(() => _micInput.IsInputAvailable);

        _activeRecorders.Add(component);
        if (!_micInput.IsRecording)
        {
            _micInput.StartRecording(audioBufferConfiguration.sampleLengthInMs);
        }

        if (component is IVoiceEventProvider v)
        {
            v.VoiceEvents.OnStartListening?.Invoke();
        }
    }

    public void StopRecording(Component component)
    {
        _activeRecorders.Remove(component);
        if (_activeRecorders.Count == 0)
        {
            _micInput.StopRecording();
        }

        if (component is IVoiceEventProvider v)
        {
            v.VoiceEvents.OnStoppedListening?.Invoke();
        }
    }
}

}

kuffcof commented 1 year ago

Sorry about all the spam. it is actually 1 line that is different.

_instanceInit = false;

That is why the Audio Buffer wasn´t instantiating again. Alright, thank you, hope it helps someone eventually.

SportsImproVR-Joost commented 3 days ago

@kuffcof I know this is an old thread, but I hope you still remember what you did.

Because I don't see a difference between the commented out Previous one and the current active one with regards to _instanceInit.

In the declaration it is set to false In Awake it is set to true In OnDisable it is set to false

And that's also how it is in the script that I have in my project.

Did you change it from false to true somewhere? I'm trying to figure out if this might be the solution to my issue (https://github.com/wit-ai/wit/issues/2732)

Thanks!

kuffcof commented 2 days ago

Hi, this was a long time ago :)

I have sadly stopped developing since then.

However, I had many many instances of Wit, but only 1 for each scene. And they worked ...eventually. I don t even have my Wit credentials, but to place it bluntly: Each scene required a new Wit project (I got to create like 30 Wit Projects in order to satisfy all the scenes)

Further investigating, I can see that I answered the WRONG QUESTION, or no question at all.

Yes, there are different Wit versions (I am sorry, I was referring to the oculus plugin in my case which used to mess everything up if updated) and different Unity compatibilities. I spotted that line by mistake and I remember it helped at the time.

I am sorry I can´t give a more in-depth answer atm.

kuffcof commented 2 days ago

so basically I think is that line that needs to be changed in a script called Audio Buffer

kuffcof commented 2 days ago

I think it would help to see if you are getting any error from the same script, because otherwise this may not help. Uff, I miss developing :D ....at least the investigation

kuffcof commented 2 days ago

best of luck and please tell how it goes :)

SportsImproVR-Joost commented 2 days ago

Thanks for your reactions. For me this is the frustrating part about developing... troubleshooting thrid party code and waiting/hoping for a reaction on the forum or bug report.

But your responses made me recheck the code in my project and I found that in the OnDisable method there was actually no line for _instanceInit=false (don't know why I earlier thought there was, too many stuff going on and as a single developer you are always the single point of failure). So when I added the line to OnDisable it worked.

So thanks for being my sounding board :-)

kuffcof commented 1 day ago

Hi, really glad to hear you got it solved.

I know what you mean about developing alone ->it is a long hard way and it takes loads of investigations.

Best would be to get into a team, but in order to get into a team you need experience, so there is a vicious circle for which I had to interrupt my projects and pursue a job that puts food on the table ¨now¨.

Good luck with you game or app, hope it´s gonna make it big! :D