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 +4/−6
- link/include/ableton/discovery/Service.hpp +9/−1
- link/include/ableton/discovery/UdpMessenger.hpp +34/−16
- link/include/ableton/discovery/test/Interface.hpp +9/−1
- link/include/ableton/link/Controller.hpp +30/−14
- link/include/ableton/link/Gateway.hpp +3/−4
- link/include/ableton/platforms/posix/ScanIpIfAddrs.hpp +16/−13
- link/include/ableton/platforms/windows/ScanIpIfAddrs.hpp +7/−4
- link/third_party/catch/catch.hpp +78/−61
- src/hs/Sound/Tidal/Clock.hs +350/−0
- tidal-link.cabal +9/−5
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