packages feed

tidal-link 1.0.3 → 1.1.0

raw patch · 11 files changed

+549/−125 lines, 11 filesdep +hoscdep +mtldep +stmnew-component:exe:tidal-linktestnew-uploader

Dependencies added: hosc, mtl, stm

Files

link/include/ableton/discovery/PeerGateway.hpp view
@@ -213,23 +213,21 @@   return {std::move(messenger), std::move(observer), std::move(io)}; } -// IpV4 gateway types template <typename StateQuery, typename IoContext>-using IpV4Messenger = UdpMessenger<+using Messenger = UdpMessenger<   IpInterface<typename util::Injected<IoContext>::type&, v1::kMaxMessageSize>,   StateQuery,   IoContext>;  template <typename PeerObserver, typename StateQuery, typename IoContext>-using IpV4Gateway =-  PeerGateway<IpV4Messenger<StateQuery, typename util::Injected<IoContext>::type&>,+using Gateway =+  PeerGateway<Messenger<StateQuery, typename util::Injected<IoContext>::type&>,     PeerObserver,     IoContext>;  // Factory function to bind a PeerGateway to an IpInterface with the given address. template <typename PeerObserver, typename NodeState, typename IoContext>-IpV4Gateway<PeerObserver, NodeState, IoContext> makeIpV4Gateway(-  util::Injected<IoContext> io,+Gateway<PeerObserver, NodeState, IoContext> makeGateway(util::Injected<IoContext> io,   const IpAddress& addr,   util::Injected<PeerObserver> observer,   NodeState state)
link/include/ableton/discovery/Service.hpp view
@@ -33,16 +33,23 @@   using ServicePeerGateways = PeerGateways<NodeState, GatewayFactory, IoContext>;    Service(NodeState state, GatewayFactory factory, util::Injected<IoContext> io)-    : mGateways(+    : mEnabled(false)+    , mGateways(         std::chrono::seconds(5), std::move(state), std::move(factory), std::move(io))   {   }    void enable(const bool bEnable)   {+    mEnabled = bEnable;     mGateways.enable(bEnable);   } +  bool isEnabled() const+  {+    return mEnabled;+  }+   // Asynchronously operate on the current set of peer gateways. The   // handler will be invoked in the service's io context.   template <typename Handler>@@ -65,6 +72,7 @@   }  private:+  bool mEnabled;   ServicePeerGateways mGateways; }; 
link/include/ableton/discovery/UdpMessenger.hpp view
@@ -270,24 +270,42 @@       // Ignore messages from self and other groups       if (header.ident != mState.ident() && header.groupId == 0)       {-        debug(mIo->log()) << "Received message type "-                          << static_cast<int>(header.messageType) << " from peer "-                          << header.ident;+        // On Linux multicast messages are sent to all sockets registered to the multicast+        // group. To avoid duplicate message handling and invalid response messages we+        // check if the message is coming from an endpoint that is in the same subnet as+        // the interface.+        auto ignoreIpV4Message = false;+        if (from.address().is_v4() && mInterface->endpoint().address().is_v4())+        {+          const auto subnet = LINK_ASIO_NAMESPACE::ip::make_network_v4(+            mInterface->endpoint().address().to_v4(), 24);+          const auto fromAddr =+            LINK_ASIO_NAMESPACE::ip::make_network_v4(from.address().to_v4(), 32);+          ignoreIpV4Message = !fromAddr.is_subnet_of(subnet);+        } -        switch (header.messageType)+        if (!ignoreIpV4Message)         {-        case v1::kAlive:-          sendResponse(from);-          receivePeerState(std::move(result.first), result.second, messageEnd);-          break;-        case v1::kResponse:-          receivePeerState(std::move(result.first), result.second, messageEnd);-          break;-        case v1::kByeBye:-          receiveByeBye(std::move(result.first.ident));-          break;-        default:-          info(mIo->log()) << "Unknown message received of type: " << header.messageType;+          debug(mIo->log()) << "Received message type "+                            << static_cast<int>(header.messageType) << " from peer "+                            << header.ident;++          switch (header.messageType)+          {+          case v1::kAlive:+            sendResponse(from);+            receivePeerState(std::move(result.first), result.second, messageEnd);+            break;+          case v1::kResponse:+            receivePeerState(std::move(result.first), result.second, messageEnd);+            break;+          case v1::kByeBye:+            receiveByeBye(std::move(result.first.ident));+            break;+          default:+            info(mIo->log()) << "Unknown message received of type: "+                             << header.messageType;+          }         }       }       listen(tag);
link/include/ableton/discovery/test/Interface.hpp view
@@ -31,6 +31,13 @@ class Interface { public:+  Interface() = default;++  Interface(UdpEndpoint endpoint)+    : mEndpoint(std::move(endpoint))+  {+  }+   void send(     const uint8_t* const bytes, const size_t numBytes, const UdpEndpoint& endpoint)   {@@ -56,7 +63,7 @@    UdpEndpoint endpoint() const   {-    return UdpEndpoint({}, 0);+    return mEndpoint;   }    using SentMessage = std::pair<std::vector<uint8_t>, UdpEndpoint>;@@ -66,6 +73,7 @@   using ReceiveCallback =     std::function<void(const UdpEndpoint&, const std::vector<uint8_t>&)>;   ReceiveCallback mCallback;+  UdpEndpoint mEndpoint; };  } // namespace test
link/include/ableton/link/Controller.hpp view
@@ -176,7 +176,8 @@     auto stopped = false;      mIo->async([this, &mutex, &condition, &stopped]() {-      enable(false);+      mEnabled = false;+      mDiscovery.enable(false);       std::unique_lock<std::mutex> lock(mutex);       stopped = true;       condition.notify_one();@@ -193,18 +194,7 @@     const bool bWasEnabled = mEnabled.exchange(bEnable);     if (bWasEnabled != bEnable)     {-      mIo->async([this, bEnable] {-        if (bEnable)-        {-          // Process the pending client states to make sure we don't push one after we-          // have joined a running session-          mRtClientStateSetter.processPendingClientStates();-          // Always reset when first enabling to avoid hijacking-          // tempo in existing sessions-          resetState();-        }-        mDiscovery.enable(bEnable);-      });+      mRtClientStateSetter.invoke();     }   } @@ -570,11 +560,23 @@     RtClientStateSetter(Controller& controller)       : mController(controller)       , mCallbackDispatcher(-          [this] { mController.mIo->async([this]() { processPendingClientStates(); }); },+          [this] {+            mController.mIo->async([this]() {+              // Process the pending client states first to make sure we don't push one+              // after we have joined a running session when enabling+              processPendingClientStates();+              updateEnabled();+            });+          },           detail::kRtHandlerFallbackPeriod)     {     } +    void invoke()+    {+      mCallbackDispatcher.invoke();+    }+     void push(const IncomingClientState clientState)     {       if (clientState.timeline)@@ -598,6 +600,20 @@     {       const auto clientState = buildMergedPendingClientState();       mController.handleRtClientState(clientState);+    }++    void updateEnabled()+    {+      if (mController.mEnabled && !mController.mDiscovery.isEnabled())+      {+        // Always reset when first enabling to avoid hijacking tempo in existing sessions+        mController.resetState();+        mController.mDiscovery.enable(true);+      }+      else if (!mController.mEnabled && mController.mDiscovery.isEnabled())+      {+        mController.mDiscovery.enable(false);+      }     }    private:
link/include/ableton/link/Gateway.hpp view
@@ -44,7 +44,7 @@         std::move(ghostXForm),         std::move(clock),         util::injectRef(*mIo))-    , mPeerGateway(discovery::makeIpV4Gateway(util::injectRef(*mIo),+    , mPeerGateway(discovery::makeGateway(util::injectRef(*mIo),         std::move(addr),         std::move(observer),         PeerState{std::move(nodeState), mMeasurement.endpoint()}))@@ -84,9 +84,8 @@ private:   util::Injected<IoContext> mIo;   MeasurementService<Clock, typename util::Injected<IoContext>::type&> mMeasurement;-  discovery::-    IpV4Gateway<PeerObserver, PeerState, typename util::Injected<IoContext>::type&>-      mPeerGateway;+  discovery::Gateway<PeerObserver, PeerState, typename util::Injected<IoContext>::type&>+    mPeerGateway; };  } // namespace link
link/include/ableton/platforms/posix/ScanIpIfAddrs.hpp view
@@ -86,12 +86,13 @@       const struct ifaddrs* interface;       for (interface = &interfaces; interface; interface = interface->ifa_next)       {-        auto addr = reinterpret_cast<const struct sockaddr_in*>(interface->ifa_addr);+        const auto addr =+          reinterpret_cast<const struct sockaddr_in*>(interface->ifa_addr);         if (addr && interface->ifa_flags & IFF_RUNNING && addr->sin_family == AF_INET)         {-          auto bytes = reinterpret_cast<const char*>(&addr->sin_addr);-          auto address = discovery::makeAddress<discovery::IpAddressV4>(bytes);-          addrs.emplace_back(std::move(address));+          const auto bytes = reinterpret_cast<const char*>(&addr->sin_addr);+          const auto address = discovery::makeAddress<discovery::IpAddressV4>(bytes);+          addrs.emplace_back(address);           IpInterfaceNames.insert(std::make_pair(interface->ifa_name, address));         }       }@@ -101,17 +102,19 @@       const struct ifaddrs* interface;       for (interface = &interfaces; interface; interface = interface->ifa_next)       {-        auto addr = reinterpret_cast<const struct sockaddr_in*>(interface->ifa_addr);-        if (addr && interface->ifa_flags & IFF_RUNNING && addr->sin_family == AF_INET6)+        const auto addr =+          reinterpret_cast<const struct sockaddr_in*>(interface->ifa_addr);+        if (IpInterfaceNames.find(interface->ifa_name) != IpInterfaceNames.end() && addr+            && interface->ifa_flags & IFF_RUNNING && addr->sin_family == AF_INET6)         {-          auto addr6 = reinterpret_cast<const struct sockaddr_in6*>(addr);-          auto bytes = reinterpret_cast<const char*>(&addr6->sin6_addr);-          auto scopeId = addr6->sin6_scope_id;-          auto address = discovery::makeAddress<discovery::IpAddressV6>(bytes, scopeId);-          if (IpInterfaceNames.find(interface->ifa_name) != IpInterfaceNames.end()-              && !address.is_loopback() && address.is_link_local())+          const auto addr6 = reinterpret_cast<const struct sockaddr_in6*>(addr);+          const auto bytes = reinterpret_cast<const char*>(&addr6->sin6_addr);+          const auto scopeId = addr6->sin6_scope_id;+          const auto address =+            discovery::makeAddress<discovery::IpAddressV6>(bytes, scopeId);+          if (!address.is_loopback() && address.is_link_local())           {-            addrs.emplace_back(std::move(address));+            addrs.emplace_back(address);           }         }       }
link/include/ableton/platforms/windows/ScanIpIfAddrs.hpp view
@@ -120,8 +120,9 @@             // IPv4             SOCKADDR_IN* addr4 =               reinterpret_cast<SOCKADDR_IN*>(address->Address.lpSockaddr);-            auto bytes = reinterpret_cast<const char*>(&addr4->sin_addr);-            auto ipv4address = discovery::makeAddress<discovery::IpAddressV4>(bytes);+            const auto bytes = reinterpret_cast<const char*>(&addr4->sin_addr);+            const auto ipv4address =+              discovery::makeAddress<discovery::IpAddressV4>(bytes);             addrs.emplace_back(ipv4address);             IpInterfaceNames.insert(               std::make_pair(networkInterface->AdapterName, ipv4address));@@ -145,8 +146,10 @@           {             SOCKADDR_IN6* sockAddr =               reinterpret_cast<SOCKADDR_IN6*>(address->Address.lpSockaddr);-            auto bytes = reinterpret_cast<const char*>(&sockAddr->sin6_addr);-            auto addr6 = discovery::makeAddress<discovery::IpAddressV6>(bytes);+            const auto scopeId = sockAddr->sin6_scope_id;+            const auto bytes = reinterpret_cast<const char*>(&sockAddr->sin6_addr);+            const auto addr6 =+              discovery::makeAddress<discovery::IpAddressV6>(bytes, scopeId);             if (!addr6.is_loopback() && addr6.is_link_local())             {               addrs.emplace_back(addr6);
link/third_party/catch/catch.hpp view
@@ -1,9 +1,9 @@ /*- *  Catch v2.13.7- *  Generated: 2021-07-28 20:29:27.753164+ *  Catch v2.13.10+ *  Generated: 2022-10-16 11:01:23.452308  *  ----------------------------------------------------------  *  This file has been merged from multiple headers. Please don't edit it directly- *  Copyright (c) 2021 Two Blue Cubes Ltd. All rights reserved.+ *  Copyright (c) 2022 Two Blue Cubes Ltd. All rights reserved.  *  *  Distributed under the Boost Software License, Version 1.0. (See accompanying  *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)@@ -15,7 +15,7 @@  #define CATCH_VERSION_MAJOR 2 #define CATCH_VERSION_MINOR 13-#define CATCH_VERSION_PATCH 7+#define CATCH_VERSION_PATCH 10  #ifdef __clang__ #    pragma clang system_header@@ -240,9 +240,6 @@ // Visual C++ #if defined(_MSC_VER) -#  define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) )-#  define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION  __pragma( warning(pop) )- // Universal Windows platform does not support SEH // Or console colours (or console at all...) #  if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)@@ -251,13 +248,18 @@ #    define CATCH_INTERNAL_CONFIG_WINDOWS_SEH #  endif +#  if !defined(__clang__) // Handle Clang masquerading for msvc+ // MSVC traditional preprocessor needs some workaround for __VA_ARGS__ // _MSVC_TRADITIONAL == 0 means new conformant preprocessor // _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor-#  if !defined(__clang__) // Handle Clang masquerading for msvc #    if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) #      define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #    endif // MSVC_TRADITIONAL++// Only do this if we're not using clang on Windows, which uses `diagnostic push` & `diagnostic pop`+#    define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) )+#    define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION  __pragma( warning(pop) ) #  endif // __clang__  #endif // _MSC_VER@@ -1010,34 +1012,34 @@      #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR         #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \-            INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ )+            INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ )     #else         #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \-            INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ ) )+            INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ ) )     #endif      #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR         #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \-            INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ )+            INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ )     #else         #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \-            INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) )+            INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ ) )     #endif      #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR         #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \-            INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )+            INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )     #else         #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \-            INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )+            INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )     #endif      #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR         #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \-            INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )+            INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )     #else         #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \-            INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )+            INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )     #endif #endif @@ -1050,7 +1052,7 @@         CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \         static void TestName()     #define INTERNAL_CATCH_TESTCASE( ... ) \-        INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ )+        INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ), __VA_ARGS__ )      ///////////////////////////////////////////////////////////////////////////////     #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \@@ -1072,7 +1074,7 @@         CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \         void TestName::test()     #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \-        INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ )+        INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ), ClassName, __VA_ARGS__ )      ///////////////////////////////////////////////////////////////////////////////     #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \@@ -1113,18 +1115,18 @@  #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR     #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \-        INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ )+        INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ ) #else     #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \-        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ ) )+        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ ) ) #endif  #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR     #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \-        INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ )+        INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ ) #else     #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \-        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) )+        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ ) ) #endif      #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, Signature, TmplTypes, TypesList) \@@ -1162,18 +1164,18 @@  #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR     #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\-        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename T,__VA_ARGS__)+        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename T,__VA_ARGS__) #else     #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\-        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename T, __VA_ARGS__ ) )+        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename T, __VA_ARGS__ ) ) #endif  #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR     #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\-        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__)+        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__) #else     #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\-        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) )+        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ ) ) #endif      #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2(TestName, TestFunc, Name, Tags, TmplList)\@@ -1204,7 +1206,7 @@         static void TestFunc()      #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(Name, Tags, TmplList) \-        INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, TmplList )+        INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, TmplList )      #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... ) \         CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \@@ -1237,18 +1239,18 @@  #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR     #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \-        INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )+        INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) #else     #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \-        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )+        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) ) #endif  #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR     #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \-        INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )+        INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) #else     #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \-        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )+        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) ) #endif      #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, Signature, TmplTypes, TypesList)\@@ -1289,18 +1291,18 @@  #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR     #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\-        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, typename T, __VA_ARGS__ )+        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, typename T, __VA_ARGS__ ) #else     #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\-        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, typename T,__VA_ARGS__ ) )+        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, typename T,__VA_ARGS__ ) ) #endif  #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR     #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\-        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, Signature, __VA_ARGS__ )+        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, Signature, __VA_ARGS__ ) #else     #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\-        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, Signature,__VA_ARGS__ ) )+        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, Signature,__VA_ARGS__ ) ) #endif      #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, TmplList) \@@ -1334,7 +1336,7 @@         void TestName<TestType>::test()  #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD(ClassName, Name, Tags, TmplList) \-        INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, TmplList )+        INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, TmplList )  // end catch_test_registry.h // start catch_capture.hpp@@ -3091,7 +3093,7 @@         Approx operator-() const;          template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>-        Approx operator()( T const& value ) {+        Approx operator()( T const& value ) const {             Approx approx( static_cast<double>(value) );             approx.m_epsilon = m_epsilon;             approx.m_margin = m_margin;@@ -4163,7 +4165,7 @@             if (!m_predicate(m_generator.get())) {                 // It might happen that there are no values that pass the                 // filter. In that case we throw an exception.-                auto has_initial_value = next();+                auto has_initial_value = nextImpl();                 if (!has_initial_value) {                     Catch::throw_exception(GeneratorException("No valid value found in filtered generator"));                 }@@ -4175,6 +4177,11 @@         }          bool next() override {+            return nextImpl();+        }++    private:+        bool nextImpl() {             bool success = m_generator.next();             if (!success) {                 return false;@@ -7388,8 +7395,6 @@             template <typename T, bool Destruct>             struct ObjectStorage             {-                using TStorage = typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type;-                 ObjectStorage() : data() {}                  ObjectStorage(const ObjectStorage& other)@@ -7432,7 +7437,7 @@                     return *static_cast<T*>(static_cast<void*>(&data));                 } -                TStorage data;+                struct { alignas(T) unsigned char data[sizeof(T)]; }  data;             };         } @@ -7942,7 +7947,7 @@     #if defined(__i386__) || defined(__x86_64__)         #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */     #elif defined(__aarch64__)-        #define CATCH_TRAP()  __asm__(".inst 0xd4200000")+        #define CATCH_TRAP()  __asm__(".inst 0xd43e0000")     #endif  #elif defined(CATCH_PLATFORM_IPHONE)@@ -13385,6 +13390,10 @@                     filename.erase(0, lastSlash);                     filename[0] = '#';                 }+                else+                {+                    filename.insert(0, "#");+                }                  auto lastDot = filename.find_last_of('.');                 if (lastDot != std::string::npos) {@@ -13547,7 +13556,7 @@              // Handle list request             if( Option<std::size_t> listed = list( m_config ) )-                return static_cast<int>( *listed );+                return (std::min) (MaxExitCode, static_cast<int>(*listed));              TestGroup tests { m_config };             auto const totals = tests.execute();@@ -15380,7 +15389,7 @@     }      Version const& libraryVersion() {-        static Version version( 2, 13, 7, "", 0 );+        static Version version( 2, 13, 10, "", 0 );         return version;     } @@ -17515,12 +17524,20 @@  #ifndef __OBJC__ +#ifndef CATCH_INTERNAL_CDECL+#ifdef _MSC_VER+#define CATCH_INTERNAL_CDECL __cdecl+#else+#define CATCH_INTERNAL_CDECL+#endif+#endif+ #if defined(CATCH_CONFIG_WCHAR) && defined(CATCH_PLATFORM_WINDOWS) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN) // Standard C/C++ Win32 Unicode wmain entry point-extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) {+extern "C" int CATCH_INTERNAL_CDECL wmain (int argc, wchar_t * argv[], wchar_t * []) { #else // Standard C/C++ main entry point-int main (int argc, char * argv[]) {+int CATCH_INTERNAL_CDECL main (int argc, char * argv[]) { #endif      return Catch::Session().run( argc, argv );@@ -17648,9 +17665,9 @@  #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) #define CATCH_BENCHMARK(...) \-    INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,))+    INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,)) #define CATCH_BENCHMARK_ADVANCED(name) \-    INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), name)+    INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), name) #endif // CATCH_CONFIG_ENABLE_BENCHMARKING  // If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required@@ -17752,9 +17769,9 @@  #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) #define BENCHMARK(...) \-    INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,))+    INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,)) #define BENCHMARK_ADVANCED(name) \-    INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), name)+    INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), name) #endif // CATCH_CONFIG_ENABLE_BENCHMARKING  using Catch::Detail::Approx;@@ -17801,8 +17818,8 @@ #define CATCH_WARN( msg )          (void)(0) #define CATCH_CAPTURE( msg )       (void)(0) -#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))-#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))+#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ))+#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ )) #define CATCH_METHOD_AS_TEST_CASE( method, ... ) #define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0) #define CATCH_SECTION( ... )@@ -17811,7 +17828,7 @@ #define CATCH_FAIL_CHECK( ... ) (void)(0) #define CATCH_SUCCEED( ... ) (void)(0) -#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))+#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ))  #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__)@@ -17834,8 +17851,8 @@ #endif  // "BDD-style" convenience wrappers-#define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))-#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className )+#define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ))+#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ), className ) #define CATCH_GIVEN( desc ) #define CATCH_AND_GIVEN( desc ) #define CATCH_WHEN( desc )@@ -17883,10 +17900,10 @@ #define INFO( msg ) (void)(0) #define UNSCOPED_INFO( msg ) (void)(0) #define WARN( msg ) (void)(0)-#define CAPTURE( msg ) (void)(0)+#define CAPTURE( ... ) (void)(0) -#define TEST_CASE( ... )  INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))-#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))+#define TEST_CASE( ... )  INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ))+#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ )) #define METHOD_AS_TEST_CASE( method, ... ) #define REGISTER_TEST_CASE( Function, ... ) (void)(0) #define SECTION( ... )@@ -17894,7 +17911,7 @@ #define FAIL( ... ) (void)(0) #define FAIL_CHECK( ... ) (void)(0) #define SUCCEED( ... ) (void)(0)-#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))+#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ))  #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__)@@ -17924,8 +17941,8 @@ #define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature )  // "BDD-style" convenience wrappers-#define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) )-#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className )+#define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ) )+#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ), className )  #define GIVEN( desc ) #define AND_GIVEN( desc )
+ src/hs/Sound/Tidal/Clock.hs view
@@ -0,0 +1,350 @@+module Sound.Tidal.Clock where++import Control.Concurrent (forkIO, threadDelay)+import Control.Concurrent.STM (TVar, atomically, modifyTVar', newTVar, readTVar, retry, swapTVar)+import Control.Monad (when)+import Control.Monad.Reader (ReaderT, ask, runReaderT)+import Control.Monad.State (StateT, evalStateT, get, liftIO, modify, put)+import Data.Coerce (coerce)+import Data.Int (Int64)+import Foreign.C.Types (CDouble (..))+import qualified Sound.Osc.Fd as O+import qualified Sound.Tidal.Link as Link+import System.IO (hPutStrLn, stderr)++type Time = Rational++-- | representation of a tick based clock+type Clock =+  ReaderT ClockMemory (StateT ClockState IO)++-- | internal read-only memory of the clock+data ClockMemory = ClockMemory+  { clockConfig :: ClockConfig,+    clockRef :: ClockRef,+    clockAction :: TickAction+  }++-- | internal mutable state of the clock+data ClockState = ClockState+  { ticks :: Int64,+    start :: Link.Micros,+    nowArc :: (Time, Time),+    nudged :: Double+  }+  deriving (Show)++-- | reference to interact with the clock, while it is running+data ClockRef = ClockRef+  { rAction :: TVar ClockAction,+    rAbletonLink :: Link.AbletonLink+  }++-- | configuration of the clock+data ClockConfig = ClockConfig+  { cQuantum :: CDouble,+    cBeatsPerCycle :: CDouble,+    cFrameTimespan :: Double,+    cEnableLink :: Bool,+    cSkipTicks :: Int64,+    cProcessAhead :: Double+  }++-- | action to be executed on a tick,+-- | given the current timespan, nudge and reference to the clock+type TickAction =+  (Time, Time) -> Double -> ClockConfig -> ClockRef -> (Link.SessionState, Link.SessionState) -> IO ()++-- | possible actions for interacting with the clock+data ClockAction+  = NoAction+  | SetCycle Time+  | SetTempo Time+  | SetNudge Double++defaultCps :: Double+defaultCps = 0.575++defaultConfig :: ClockConfig+defaultConfig =+  ClockConfig+    { cFrameTimespan = 1 / 20,+      cEnableLink = False,+      cProcessAhead = 3 / 10,+      cSkipTicks = 10,+      cQuantum = 4,+      cBeatsPerCycle = 4+    }++-- | creates a clock according to the config and runs it+-- | in a seperate thread+clocked :: ClockConfig -> TickAction -> IO ClockRef+clocked config ac = runClock config ac clockCheck++-- | runs the clock on the initial state and memory as given+-- | by initClock, hands the ClockRef for interaction from outside+runClock :: ClockConfig -> TickAction -> Clock () -> IO ClockRef+runClock config ac clock = do+  (mem, st) <- initClock config ac+  _ <- forkIO $ evalStateT (runReaderT clock mem) st+  return (clockRef mem)++-- | creates a ableton link instance and an MVar for interacting+-- | with the clock from outside and computes the initial clock state+initClock :: ClockConfig -> TickAction -> IO (ClockMemory, ClockState)+initClock config ac = do+  abletonLink <- Link.create bpm+  when (cEnableLink config) $ Link.enable abletonLink+  sessionState <- Link.createAndCaptureAppSessionState abletonLink+  now <- Link.clock abletonLink+  let startAt = now + processAhead+  Link.requestBeatAtTime sessionState 0 startAt (cQuantum config)+  Link.commitAndDestroyAppSessionState abletonLink sessionState+  clockMV <- atomically $ newTVar NoAction+  let st =+        ClockState+          { ticks = 0,+            start = now,+            nowArc = (0, 0),+            nudged = 0+          }+  return (ClockMemory config (ClockRef clockMV abletonLink) ac, st)+  where+    processAhead = round $ (cProcessAhead config) * 1000000+    bpm = (coerce defaultCps) * 60 * (cBeatsPerCycle config)++-- The reference time Link uses,+-- is the time the audio for a certain beat hits the speaker.+-- Processing of the nowArc should happen early enough for+-- all events in the nowArc to hit the speaker, but not too early.+-- Processing thus needs to happen a short while before the start+-- of nowArc. How far ahead is controlled by cProcessAhead.++-- previously called checkArc+clockCheck :: Clock ()+clockCheck = do+  (ClockMemory config (ClockRef clockMV abletonLink) _) <- ask++  action <- liftIO $ atomically $ swapTVar clockMV NoAction+  processAction action++  st <- get++  let logicalEnd = logicalTime config (start st) $ ticks st + 1+      nextArcStartCycle = arcEnd $ nowArc st++  ss <- liftIO $ Link.createAndCaptureAppSessionState abletonLink+  arcStartTime <- liftIO $ cyclesToTime config ss nextArcStartCycle+  liftIO $ Link.destroySessionState ss++  if (arcStartTime < logicalEnd)+    then clockProcess+    else tick++-- tick moves the logical time forward or recalculates the ticks in case+-- the logical time is out of sync with Link time.+-- tick delays the thread when logical time is ahead of Link time.+tick :: Clock ()+tick = do+  (ClockMemory config (ClockRef _ abletonLink) _) <- ask+  st <- get+  now <- liftIO $ Link.clock abletonLink+  let processAhead = round $ (cProcessAhead config) * 1000000+      frameTimespan = round $ (cFrameTimespan config) * 1000000+      preferredNewTick = ticks st + 1+      logicalNow = logicalTime config (start st) preferredNewTick+      aheadOfNow = now + processAhead+      actualTick = (aheadOfNow - start st) `div` frameTimespan+      drifted = abs (actualTick - preferredNewTick) > (cSkipTicks config)+      newTick+        | drifted = actualTick+        | otherwise = preferredNewTick+      delta = min frameTimespan (logicalNow - aheadOfNow)++  put $ st {ticks = newTick}++  if drifted+    then liftIO $ hPutStrLn stderr $ "skip: " ++ (show (actualTick - ticks st))+    else when (delta > 0) $ liftIO $ threadDelay $ fromIntegral delta++  clockCheck++-- previously called processArc+-- hands the current link operations to the TickAction+clockProcess :: Clock ()+clockProcess = do+  (ClockMemory config ref@(ClockRef _ abletonLink) action) <- ask+  st <- get+  let logicalEnd = logicalTime config (start st) $ ticks st + 1+      startCycle = arcEnd $ nowArc st++  sessionState <- liftIO $ Link.createAndCaptureAppSessionState abletonLink+  endCycle <- liftIO $ timeToCycles config sessionState logicalEnd++  liftIO $ action (startCycle, endCycle) (nudged st) config ref (sessionState, sessionState)++  liftIO $ Link.commitAndDestroyAppSessionState abletonLink sessionState++  put (st {nowArc = (startCycle, endCycle)})+  tick++processAction :: ClockAction -> Clock ()+processAction NoAction = return ()+processAction (SetNudge n) = modify (\st -> st {nudged = n})+processAction (SetTempo bpm) = do+  (ClockMemory _ (ClockRef _ abletonLink) _) <- ask+  sessionState <- liftIO $ Link.createAndCaptureAppSessionState abletonLink+  now <- liftIO $ Link.clock abletonLink+  liftIO $ Link.setTempo sessionState (fromRational bpm) now+  liftIO $ Link.commitAndDestroyAppSessionState abletonLink sessionState+processAction (SetCycle cyc) = do+  (ClockMemory config (ClockRef _ abletonLink) _) <- ask+  sessionState <- liftIO $ Link.createAndCaptureAppSessionState abletonLink++  now <- liftIO $ Link.clock abletonLink+  let processAhead = round $ (cProcessAhead config) * 1000000+      startAt = now + processAhead+      beat = (fromRational cyc) * (cBeatsPerCycle config)+  liftIO $ Link.requestBeatAtTime sessionState beat startAt (cQuantum config)+  liftIO $ Link.commitAndDestroyAppSessionState abletonLink sessionState++  modify (\st -> st {ticks = 0, start = now, nowArc = (cyc, cyc)})++---------------------------------------------------------------+----------- functions representing link operations ------------+---------------------------------------------------------------++arcStart :: (Time, Time) -> Time+arcStart = fst++arcEnd :: (Time, Time) -> Time+arcEnd = snd++beatToCycles :: ClockConfig -> Double -> Double+beatToCycles config beat = beat / (coerce $ cBeatsPerCycle config)++cyclesToBeat :: ClockConfig -> Double -> Double+cyclesToBeat config cyc = cyc * (coerce $ cBeatsPerCycle config)++getSessionState :: ClockRef -> IO Link.SessionState+getSessionState (ClockRef _ abletonLink) = Link.createAndCaptureAppSessionState abletonLink++-- onSingleTick assumes it runs at beat 0.+-- The best way to achieve that is to use forceBeatAtTime.+-- But using forceBeatAtTime means we can not commit its session state.+getZeroedSessionState :: ClockConfig -> ClockRef -> IO Link.SessionState+getZeroedSessionState config (ClockRef _ abletonLink) = do+  ss <- Link.createAndCaptureAppSessionState abletonLink+  nowLink <- liftIO $ Link.clock abletonLink+  Link.forceBeatAtTime ss 0 (nowLink + processAhead) (cQuantum config)+  return ss+  where+    processAhead = round $ (cProcessAhead config) * 1000000++getTempo :: Link.SessionState -> IO Time+getTempo ss = fmap toRational $ Link.getTempo ss++setTempoCPS :: Time -> Link.Micros -> ClockConfig -> Link.SessionState -> IO ()+setTempoCPS cps now conf ss = Link.setTempo ss (coerce $ cyclesToBeat conf ((fromRational cps) * 60)) now++timeAtBeat :: ClockConfig -> Link.SessionState -> Double -> IO Link.Micros+timeAtBeat config ss beat = Link.timeAtBeat ss (coerce beat) (cQuantum config)++timeToCycles :: ClockConfig -> Link.SessionState -> Link.Micros -> IO Time+timeToCycles config ss time = do+  beat <- Link.beatAtTime ss time (cQuantum config)+  return $! (toRational beat) / (toRational (cBeatsPerCycle config))++-- At what time does the cycle occur according to Link?+cyclesToTime :: ClockConfig -> Link.SessionState -> Time -> IO Link.Micros+cyclesToTime config ss cyc = do+  let beat = (fromRational cyc) * (cBeatsPerCycle config)+  Link.timeAtBeat ss beat (cQuantum config)++linkToOscTime :: ClockRef -> Link.Micros -> IO O.Time+linkToOscTime (ClockRef _ abletonLink) lt = do+  nowOsc <- O.time+  nowLink <- liftIO $ Link.clock abletonLink+  return $ addMicrosToOsc (lt - nowLink) nowOsc++addMicrosToOsc :: Link.Micros -> O.Time -> O.Time+addMicrosToOsc m t = ((fromIntegral m) / 1000000) + t++-- Time is processed at a fixed rate according to configuration+-- logicalTime gives the time when a tick starts based on when+-- processing first started.+logicalTime :: ClockConfig -> Link.Micros -> Int64 -> Link.Micros+logicalTime config startTime ticks' = startTime + ticks' * frameTimespan+  where+    frameTimespan = round $ (cFrameTimespan config) * 1000000++---------------------------------------------------------------+----------- functions for interacting with the clock ----------+---------------------------------------------------------------++getBPM :: ClockRef -> IO Time+getBPM (ClockRef _ abletonLink) = do+  ss <- Link.createAndCaptureAppSessionState abletonLink+  bpm <- Link.getTempo ss+  Link.destroySessionState ss+  return $! toRational bpm++getCPS :: ClockConfig -> ClockRef -> IO Time+getCPS config ref = fmap (\bpm -> bpm / (toRational $ cBeatsPerCycle config) / 60) (getBPM ref)++getCycleTime :: ClockConfig -> ClockRef -> IO Time+getCycleTime config (ClockRef _ abletonLink) = do+  now <- Link.clock abletonLink+  ss <- Link.createAndCaptureAppSessionState abletonLink+  c <- timeToCycles config ss now+  Link.destroySessionState ss+  return $! c++resetClock :: ClockRef -> IO ()+resetClock clock = setClock clock 0++setClock :: ClockRef -> Time -> IO ()+setClock (ClockRef clock _) t = atomically $ do+  action <- readTVar clock+  case action of+    NoAction -> modifyTVar' clock (const $ SetCycle t)+    _ -> retry++setBPM :: ClockRef -> Time -> IO ()+setBPM (ClockRef clock _) t = atomically $ do+  action <- readTVar clock+  case action of+    NoAction -> modifyTVar' clock (const $ SetTempo t)+    _ -> retry++setCPS :: ClockConfig -> ClockRef -> Time -> IO ()+setCPS config ref cps = setBPM ref bpm+  where+    bpm = cps * 60 * (toRational $ cBeatsPerCycle config)++setNudge :: ClockRef -> Double -> IO ()+setNudge (ClockRef clock _) n = atomically $ do+  action <- readTVar clock+  case action of+    NoAction -> modifyTVar' clock (const $ SetNudge n)+    _ -> retry++-- Used for Tempo callback+-- Tempo changes will be applied.+-- However, since the full arc is processed at once and since Link does not support+-- scheduling, tempo change may affect scheduling of events that happen earlier+-- in the normal stream (the one handled by onTick).+clockOnce :: TickAction -> ClockConfig -> ClockRef -> IO ()+clockOnce action config ref@(ClockRef _ abletonLink) = do+  ss <- getZeroedSessionState config ref+  temposs <- Link.createAndCaptureAppSessionState abletonLink+  -- The nowArc is a full cycle+  action (0, 1) 0 config ref (ss, temposs)+  Link.destroySessionState ss+  Link.commitAndDestroyAppSessionState abletonLink temposs++disableLink :: ClockRef -> IO ()+disableLink (ClockRef _ abletonLink) = Link.disable abletonLink++enableLink :: ClockRef -> IO ()+enableLink (ClockRef _ abletonLink) = Link.enable abletonLink
tidal-link.cabal view
@@ -1,6 +1,6 @@ cabal-version:       2.4 name:                tidal-link-version:             1.0.3+version:             1.1.0 synopsis:            Ableton Link integration for Tidal -- description: homepage:            http://tidalcycles.org/@@ -12,7 +12,7 @@ Copyright:           (c) Pierre Krafft and contributors, 2021 category:            Sound build-type:          Simple-tested-with:         GHC == 9.2.4+tested-with:         GHC == 9.8.2  extra-source-files:   README.md@@ -29,9 +29,13 @@   default-language:    Haskell2010    exposed-modules:     Sound.Tidal.Link+                       Sound.Tidal.Clock    build-depends:-      base >=4.8 && <5+      base >=4.8 && < 5,+      hosc >= 0.21 && < 0.22,+      mtl >= 2.2 && < 2.4,+      stm >= 2.5 && < 2.6    if os(windows)     extra-libraries:@@ -46,7 +50,7 @@   else     cxx-options:       -DLINK_PLATFORM_LINUX=1 -std=c++14 -Wno-multichar -Wno-subobject-linkage-  +   if impl(ghc >= 9.4)     build-depends: system-cxx-std-lib   else@@ -65,7 +69,7 @@   type:     git   location: https://github.com/tidalcycles/Tidal -executable linktest+executable tidal-linktest   ghc-options: -Wall   hs-source-dirs:                  src/hs