From b3e77c4384ec5a097ed782fd72aca2639a188a2d Mon Sep 17 00:00:00 2001
From: Jack Andersen <jackoalan@gmail.com>
Date: Tue, 10 May 2016 18:50:26 -1000
Subject: [PATCH] DeferredWindowEvents moved to boo

---
 CMakeLists.txt                       |   1 +
 include/boo/DeferredWindowEvents.hpp | 273 +++++++++++++++++++++++++++
 include/boo/audiodev/IAudioVoice.hpp |   3 +
 include/boo/boo.hpp                  |   1 +
 lib/audiodev/AudioVoice.cpp          |  29 ++-
 lib/audiodev/AudioVoice.hpp          |   5 +-
 6 files changed, 305 insertions(+), 7 deletions(-)
 create mode 100644 include/boo/DeferredWindowEvents.hpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2346ea2..298c6ff 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -200,6 +200,7 @@ add_library(boo
             include/boo/IWindow.hpp
             include/boo/IApplication.hpp
             include/boo/ThreadLocalPtr.hpp
+            include/boo/DeferredWindowEvents.hpp
             include/boo/System.hpp
             include/boo/boo.hpp
             InputDeviceClasses.cpp
diff --git a/include/boo/DeferredWindowEvents.hpp b/include/boo/DeferredWindowEvents.hpp
new file mode 100644
index 0000000..289840f
--- /dev/null
+++ b/include/boo/DeferredWindowEvents.hpp
@@ -0,0 +1,273 @@
+#ifndef BOO_DEFERREDWINDOWEVENTS_HPP
+#define BOO_DEFERREDWINDOWEVENTS_HPP
+
+#include <boo/boo.hpp>
+#include <mutex>
+#include <condition_variable>
+
+namespace boo
+{
+
+template <class Receiver>
+struct DeferredWindowEvents : public IWindowCallback
+{
+    Receiver& m_rec;
+    std::mutex m_mt;
+    std::condition_variable m_resizeCv;
+    DeferredWindowEvents(Receiver& rec) : m_rec(rec) {}
+
+    bool m_destroyed = false;
+    void destroyed()
+    {
+        m_destroyed = true;
+    }
+
+    bool m_hasResize = false;
+    SWindowRect m_latestResize;
+    void resized(const SWindowRect& rect)
+    {
+        std::unique_lock<std::mutex> lk(m_mt);
+        m_latestResize = rect;
+        m_hasResize = true;
+        m_resizeCv.wait_for(lk, std::chrono::milliseconds(500));
+    }
+
+    struct Command
+    {
+        enum class Type
+        {
+            MouseDown,
+            MouseUp,
+            MouseMove,
+            MouseEnter,
+            MouseLeave,
+            Scroll,
+            TouchDown,
+            TouchUp,
+            TouchMove,
+            CharKeyDown,
+            CharKeyUp,
+            SpecialKeyDown,
+            SpecialKeyUp,
+            ModKeyDown,
+            ModKeyUp
+        } m_type;
+
+        SWindowCoord m_coord;
+        EMouseButton m_button;
+        EModifierKey m_mods;
+        SScrollDelta m_scroll;
+        STouchCoord m_tCoord;
+        uintptr_t m_tid;
+        unsigned long m_charcode;
+        ESpecialKey m_special;
+        bool m_isRepeat;
+
+        void dispatch(Receiver& rec) const
+        {
+            switch (m_type)
+            {
+            case Type::MouseDown:
+                rec.mouseDown(m_coord, m_button, m_mods);
+                break;
+            case Type::MouseUp:
+                rec.mouseUp(m_coord, m_button, m_mods);
+                break;
+            case Type::MouseMove:
+                rec.mouseMove(m_coord);
+                break;
+            case Type::MouseEnter:
+                rec.mouseEnter(m_coord);
+                break;
+            case Type::MouseLeave:
+                rec.mouseLeave(m_coord);
+                break;
+            case Type::Scroll:
+                rec.scroll(m_coord, m_scroll);
+                break;
+            case Type::TouchDown:
+                rec.touchDown(m_tCoord, m_tid);
+                break;
+            case Type::TouchUp:
+                rec.touchUp(m_tCoord, m_tid);
+                break;
+            case Type::TouchMove:
+                rec.touchMove(m_tCoord, m_tid);
+                break;
+            case Type::CharKeyDown:
+                rec.charKeyDown(m_charcode, m_mods, m_isRepeat);
+                break;
+            case Type::CharKeyUp:
+                rec.charKeyUp(m_charcode, m_mods);
+                break;
+            case Type::SpecialKeyDown:
+                rec.specialKeyDown(m_special, m_mods, m_isRepeat);
+                break;
+            case Type::SpecialKeyUp:
+                rec.specialKeyUp(m_special, m_mods);
+                break;
+            case Type::ModKeyDown:
+                rec.modKeyDown(m_mods, m_isRepeat);
+                break;
+            case Type::ModKeyUp:
+                rec.modKeyUp(m_mods);
+                break;
+            default: break;
+            }
+        }
+
+        Command(Type tp) : m_type(tp) {}
+    };
+    std::vector<Command> m_cmds;
+
+    void mouseDown(const SWindowCoord& coord, EMouseButton button, EModifierKey mods)
+    {
+        std::unique_lock<std::mutex> lk(m_mt);
+        m_cmds.emplace_back(Command::Type::MouseDown);
+        m_cmds.back().m_coord = coord;
+        m_cmds.back().m_button = button;
+        m_cmds.back().m_mods = mods;
+    }
+
+    void mouseUp(const SWindowCoord& coord, EMouseButton button, EModifierKey mods)
+    {
+        std::unique_lock<std::mutex> lk(m_mt);
+        m_cmds.emplace_back(Command::Type::MouseUp);
+        m_cmds.back().m_coord = coord;
+        m_cmds.back().m_button = button;
+        m_cmds.back().m_mods = mods;
+    }
+
+    void mouseMove(const SWindowCoord& coord)
+    {
+        std::unique_lock<std::mutex> lk(m_mt);
+        m_cmds.emplace_back(Command::Type::MouseMove);
+        m_cmds.back().m_coord = coord;
+    }
+
+    void mouseEnter(const SWindowCoord& coord)
+    {
+        std::unique_lock<std::mutex> lk(m_mt);
+        m_cmds.emplace_back(Command::Type::MouseEnter);
+        m_cmds.back().m_coord = coord;
+    }
+
+    void mouseLeave(const SWindowCoord& coord)
+    {
+        std::unique_lock<std::mutex> lk(m_mt);
+        m_cmds.emplace_back(Command::Type::MouseLeave);
+        m_cmds.back().m_coord = coord;
+    }
+
+    void scroll(const SWindowCoord& coord, const SScrollDelta& scroll)
+    {
+        std::unique_lock<std::mutex> lk(m_mt);
+        m_cmds.emplace_back(Command::Type::Scroll);
+        m_cmds.back().m_coord = coord;
+        m_cmds.back().m_scroll = scroll;
+    }
+
+    void touchDown(const STouchCoord& coord, uintptr_t tid)
+    {
+        std::unique_lock<std::mutex> lk(m_mt);
+        m_cmds.emplace_back(Command::Type::TouchDown);
+        m_cmds.back().m_tCoord = coord;
+        m_cmds.back().m_tid = tid;
+    }
+
+    void touchUp(const STouchCoord& coord, uintptr_t tid)
+    {
+        std::unique_lock<std::mutex> lk(m_mt);
+        m_cmds.emplace_back(Command::Type::TouchUp);
+        m_cmds.back().m_tCoord = coord;
+        m_cmds.back().m_tid = tid;
+    }
+
+    void touchMove(const STouchCoord& coord, uintptr_t tid)
+    {
+        std::unique_lock<std::mutex> lk(m_mt);
+        m_cmds.emplace_back(Command::Type::TouchMove);
+        m_cmds.back().m_tCoord = coord;
+        m_cmds.back().m_tid = tid;
+    }
+
+    void charKeyDown(unsigned long charCode, EModifierKey mods, bool isRepeat)
+    {
+        std::unique_lock<std::mutex> lk(m_mt);
+        m_cmds.emplace_back(Command::Type::CharKeyDown);
+        m_cmds.back().m_charcode = charCode;
+        m_cmds.back().m_mods = mods;
+        m_cmds.back().m_isRepeat = isRepeat;
+    }
+
+    void charKeyUp(unsigned long charCode, EModifierKey mods)
+    {
+        std::unique_lock<std::mutex> lk(m_mt);
+        m_cmds.emplace_back(Command::Type::CharKeyUp);
+        m_cmds.back().m_charcode = charCode;
+        m_cmds.back().m_mods = mods;
+    }
+
+    void specialKeyDown(ESpecialKey key, EModifierKey mods, bool isRepeat)
+    {
+        std::unique_lock<std::mutex> lk(m_mt);
+        m_cmds.emplace_back(Command::Type::SpecialKeyDown);
+        m_cmds.back().m_special = key;
+        m_cmds.back().m_mods = mods;
+        m_cmds.back().m_isRepeat = isRepeat;
+    }
+
+    void specialKeyUp(ESpecialKey key, EModifierKey mods)
+    {
+        std::unique_lock<std::mutex> lk(m_mt);
+        m_cmds.emplace_back(Command::Type::SpecialKeyUp);
+        m_cmds.back().m_special = key;
+        m_cmds.back().m_mods = mods;
+    }
+
+    void modKeyDown(EModifierKey mod, bool isRepeat)
+    {
+        std::unique_lock<std::mutex> lk(m_mt);
+        m_cmds.emplace_back(Command::Type::ModKeyDown);
+        m_cmds.back().m_mods = mod;
+        m_cmds.back().m_isRepeat = isRepeat;
+    }
+
+    void modKeyUp(EModifierKey mod)
+    {
+        std::unique_lock<std::mutex> lk(m_mt);
+        m_cmds.emplace_back(Command::Type::ModKeyUp);
+        m_cmds.back().m_mods = mod;
+    }
+
+    ITextInputCallback* getTextInputCallback() {return m_rec.getTextInputCallback();}
+
+    void dispatchEvents()
+    {
+        std::unique_lock<std::mutex> lk(m_mt);
+        bool destroyed = m_destroyed;
+        bool hasResize = m_hasResize;
+        if (hasResize)
+            m_hasResize = false;
+        SWindowRect latestResize = m_latestResize;
+        std::vector<Command> cmds;
+        m_cmds.swap(cmds);
+        lk.unlock();
+
+        if (destroyed)
+        {
+            m_rec.destroyed();
+            return;
+        }
+
+        if (hasResize)
+            m_rec.resized(latestResize, latestResize);
+
+        for (const Command& cmd : cmds)
+            cmd.dispatch(m_rec);
+    }
+};
+
+}
+
+#endif // BOO_DEFERREDWINDOWEVENTS_HPP
diff --git a/include/boo/audiodev/IAudioVoice.hpp b/include/boo/audiodev/IAudioVoice.hpp
index 6c02d1f..e4ab240 100644
--- a/include/boo/audiodev/IAudioVoice.hpp
+++ b/include/boo/audiodev/IAudioVoice.hpp
@@ -56,6 +56,9 @@ struct IAudioVoice
 {
     virtual ~IAudioVoice() = default;
 
+    /** Set sample rate into voice (may result in audio discontinuities) */
+    virtual void resetSampleRate(double sampleRate)=0;
+
     /** Reset channel-gains to voice defaults */
     virtual void setDefaultMatrixCoefficients()=0;
 
diff --git a/include/boo/boo.hpp b/include/boo/boo.hpp
index 91000c2..195bfc1 100644
--- a/include/boo/boo.hpp
+++ b/include/boo/boo.hpp
@@ -8,5 +8,6 @@
 #include "inputdev/DualshockPad.hpp"
 #include "graphicsdev/IGraphicsCommandQueue.hpp"
 #include "graphicsdev/IGraphicsDataFactory.hpp"
+#include "DeferredWindowEvents.hpp"
 
 #endif // BOO_HPP
diff --git a/lib/audiodev/AudioVoice.cpp b/lib/audiodev/AudioVoice.cpp
index afcc883..69a57d5 100644
--- a/lib/audiodev/AudioVoice.cpp
+++ b/lib/audiodev/AudioVoice.cpp
@@ -18,6 +18,7 @@ AudioVoice::~AudioVoice()
 
 void AudioVoice::setPitchRatio(double ratio)
 {
+    m_pitchRatio = ratio;
     if (m_dynamicRate)
     {
         soxr_error_t err = soxr_set_io_ratio(m_src, ratio, m_parent.mixInfo().m_periodFrames);
@@ -52,11 +53,18 @@ AudioVoiceMono::AudioVoiceMono(BaseAudioVoiceEngine& root, IAudioMix& parent, IA
                                double sampleRate, bool dynamicRate)
 : AudioVoice(root, parent, cb, dynamicRate)
 {
-    soxr_io_spec_t ioSpec = soxr_io_spec(SOXR_INT16_I, parent.mixInfo().m_sampleFormat);
-    soxr_quality_spec_t qSpec = soxr_quality_spec(SOXR_20_BITQ, dynamicRate ? SOXR_VR : 0);
+    resetSampleRate(sampleRate);
+}
+
+void AudioVoiceMono::resetSampleRate(double sampleRate)
+{
+    soxr_delete(m_src);
+
+    soxr_io_spec_t ioSpec = soxr_io_spec(SOXR_INT16_I, m_parent.mixInfo().m_sampleFormat);
+    soxr_quality_spec_t qSpec = soxr_quality_spec(SOXR_20_BITQ, m_dynamicRate ? SOXR_VR : 0);
 
     soxr_error_t err;
-    m_src = soxr_create(sampleRate, parent.mixInfo().m_sampleRate, 1,
+    m_src = soxr_create(sampleRate, m_parent.mixInfo().m_sampleRate, 1,
                         &err, &ioSpec, &qSpec, nullptr);
 
     if (err)
@@ -66,6 +74,7 @@ AudioVoiceMono::AudioVoiceMono(BaseAudioVoiceEngine& root, IAudioMix& parent, IA
     }
 
     soxr_set_input_fn(m_src, soxr_input_fn_t(SRCCallback), this, 0);
+    setPitchRatio(m_pitchRatio);
 }
 
 size_t AudioVoiceMono::SRCCallback(AudioVoiceMono* ctx, int16_t** data, size_t frames)
@@ -146,11 +155,18 @@ AudioVoiceStereo::AudioVoiceStereo(BaseAudioVoiceEngine& root, IAudioMix& parent
                                    double sampleRate, bool dynamicRate)
 : AudioVoice(root, parent, cb, dynamicRate)
 {
-    soxr_io_spec_t ioSpec = soxr_io_spec(SOXR_INT16_I, parent.mixInfo().m_sampleFormat);
-    soxr_quality_spec_t qSpec = soxr_quality_spec(SOXR_20_BITQ, dynamicRate ? SOXR_VR : 0);
+    resetSampleRate(sampleRate);
+}
+
+void AudioVoiceStereo::resetSampleRate(double sampleRate)
+{
+    soxr_delete(m_src);
+
+    soxr_io_spec_t ioSpec = soxr_io_spec(SOXR_INT16_I, m_parent.mixInfo().m_sampleFormat);
+    soxr_quality_spec_t qSpec = soxr_quality_spec(SOXR_20_BITQ, m_dynamicRate ? SOXR_VR : 0);
 
     soxr_error_t err;
-    m_src = soxr_create(sampleRate, parent.mixInfo().m_sampleRate, 2,
+    m_src = soxr_create(sampleRate, m_parent.mixInfo().m_sampleRate, 2,
                         &err, &ioSpec, &qSpec, nullptr);
 
     if (!m_src)
@@ -160,6 +176,7 @@ AudioVoiceStereo::AudioVoiceStereo(BaseAudioVoiceEngine& root, IAudioMix& parent
     }
 
     soxr_set_input_fn(m_src, soxr_input_fn_t(SRCCallback), this, 0);
+    setPitchRatio(m_pitchRatio);
 }
 
 size_t AudioVoiceStereo::SRCCallback(AudioVoiceStereo* ctx, int16_t** data, size_t frames)
diff --git a/lib/audiodev/AudioVoice.hpp b/lib/audiodev/AudioVoice.hpp
index 2419a59..5c3eab6 100644
--- a/lib/audiodev/AudioVoice.hpp
+++ b/lib/audiodev/AudioVoice.hpp
@@ -33,7 +33,8 @@ protected:
     IAudioVoiceCallback* m_cb;
 
     /* Sample-rate converter */
-    soxr_t m_src;
+    soxr_t m_src = nullptr;
+    double m_pitchRatio = 1.0;
     bool m_dynamicRate;
 
     /* Running bool */
@@ -56,6 +57,7 @@ public:
 class AudioVoiceMono : public AudioVoice
 {
     AudioMatrixMono m_matrix;
+    void resetSampleRate(double sampleRate);
 
     static size_t SRCCallback(AudioVoiceMono* ctx,
                               int16_t** data, size_t requestedLen);
@@ -75,6 +77,7 @@ public:
 class AudioVoiceStereo : public AudioVoice
 {
     AudioMatrixStereo m_matrix;
+    void resetSampleRate(double sampleRate);
 
     static size_t SRCCallback(AudioVoiceStereo* ctx,
                               int16_t** data, size_t requestedLen);