link-Link-1.0.0/ 0000755 0001750 0001750 00000000000 12765602651 013604 5 ustar zmoelnig zmoelnig link-Link-1.0.0/AbletonLinkConfig.cmake 0000644 0001750 0001750 00000002675 12765602651 020150 0 ustar zmoelnig zmoelnig if(CMAKE_VERSION VERSION_LESS 3.0)
message(FATAL_ERROR "CMake 3.0 or greater is required")
endif()
add_library(Ableton::Link IMPORTED INTERFACE)
set_property(TARGET Ableton::Link APPEND PROPERTY
INTERFACE_INCLUDE_DIRECTORIES
${CMAKE_CURRENT_LIST_DIR}/include
)
# Force C++11 support for consuming targets
set_property(TARGET Ableton::Link APPEND PROPERTY
INTERFACE_COMPILE_FEATURES
cxx_generalized_initializers
)
if(UNIX)
set_property(TARGET Ableton::Link APPEND PROPERTY
INTERFACE_COMPILE_DEFINITIONS
LINK_PLATFORM_UNIX=1
)
endif()
if(APPLE)
set_property(TARGET Ableton::Link APPEND PROPERTY
INTERFACE_COMPILE_DEFINITIONS
LINK_PLATFORM_MACOSX=1
)
elseif(WIN32)
set_property(TARGET Ableton::Link APPEND PROPERTY
INTERFACE_COMPILE_DEFINITIONS
LINK_PLATFORM_WINDOWS=1
)
set_property(TARGET Ableton::Link APPEND PROPERTY
INTERFACE_COMPILE_OPTIONS
"/wd4503" # 'Identifier': decorated name length exceeded, name was truncated
)
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
set_property(TARGET Ableton::Link APPEND PROPERTY
INTERFACE_COMPILE_DEFINITIONS
LINK_PLATFORM_LINUX=1
)
endif()
include(${CMAKE_CURRENT_LIST_DIR}/cmake_include/AsioStandaloneConfig.cmake)
set_property(TARGET Ableton::Link APPEND PROPERTY
INTERFACE_LINK_LIBRARIES
AsioStandalone::AsioStandalone
)
set_property(TARGET Ableton::Link APPEND PROPERTY
INTERFACE_SOURCES
${CMAKE_CURRENT_LIST_DIR}/include/ableton/Link.hpp
)
link-Link-1.0.0/CMakeLists.txt 0000644 0001750 0001750 00000004004 12765602651 016342 0 ustar zmoelnig zmoelnig cmake_minimum_required(VERSION 3.0)
project(Link)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
# ___ _ _
# / _ \ _ __ | |_(_) ___ _ __ ___
# | | | | '_ \| __| |/ _ \| '_ \/ __|
# | |_| | |_) | |_| | (_) | | | \__ \
# \___/| .__/ \__|_|\___/|_| |_|___/
# |_|
# Note: Please use the LINK_* prefix for all project-specific options
option(LINK_BUILD_QT_EXAMPLES "Build examples (Requires Qt)" OFF)
if(UNIX)
set(LINK_WORD_SIZE "64" CACHE STRING "Set the word size (must be either 32 or 64)")
option(LINK_ENABLE_ASAN "Build with Address Sanitizier (ASan)" OFF)
endif()
if(WIN32)
if(${CMAKE_GENERATOR} MATCHES "Win64")
set(LINK_WORD_SIZE "64")
else()
set(LINK_WORD_SIZE "32")
endif()
option(LINK_BUILD_ASIO "Build example applications with ASIO driver" ON)
option(LINK_BUILD_VLD "Build with VLD support (VLD must be installed separately)" OFF)
endif()
# ____ _ _
# | _ \ __ _| |_| |__ ___
# | |_) / _` | __| '_ \/ __|
# | __/ (_| | |_| | | \__ \
# |_| \__,_|\__|_| |_|___/
#
# Other CMake files must be included only after declaring build options
include(cmake_include/ConfigureCompileFlags.cmake)
include(cmake_include/ConfigureWordSize.cmake)
include(cmake_include/CatchConfig.cmake)
include(AbletonLinkConfig.cmake)
add_subdirectory(include)
add_subdirectory(src)
add_subdirectory(examples)
# ____
# / ___| _ _ _ __ ___ _ __ ___ __ _ _ __ _ _
# \___ \| | | | '_ ` _ \| '_ ` _ \ / _` | '__| | | |
# ___) | |_| | | | | | | | | | | | (_| | | | |_| |
# |____/ \__,_|_| |_| |_|_| |_| |_|\__,_|_| \__, |
# |___/
message(STATUS "Build options")
get_cmake_property(all_variables VARIABLES)
string(REGEX MATCHALL "(^|;)LINK_[A-Z_]+" link_variables "${all_variables}")
foreach(variable ${link_variables})
message(" ${variable}: ${${variable}}")
endforeach()
message(STATUS "Build configuration")
if(CMAKE_BUILD_TYPE)
message(" Build type: ${CMAKE_BUILD_TYPE}")
else()
message(" Build type: Set by IDE")
endif()
link-Link-1.0.0/.travis.yml 0000644 0001750 0001750 00000010535 12765602651 015721 0 ustar zmoelnig zmoelnig # Copyright (c) 2016 Ableton. All Rights Reserved.
language: cpp
sudo: required
dist: trusty
branches:
only:
- master
matrix:
include:
###########################################################################
# Build with the main configuration on all the supported compilers
###########################################################################
# Mac OS X / XCode 7.1, 64-bit Debug
- os: osx
env: WORDSIZE=64 CONFIGURATION=Debug
osx_image: xcode7.1
compiler: clang
# Mac OS X / XCode 7.1, 64-bit Release
- os: osx
env: WORDSIZE=64 CONFIGURATION=Release
osx_image: xcode7.1
compiler: clang
# Linux with Clang 3.6, 64-bit Debug
- os: linux
compiler: clang
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['clang-3.6', 'g++-5', 'valgrind', 'p7zip-full', 'portaudio19-dev', 'cmake', 'cmake-data']
env: COMPILER=clang++-3.6 WORDSIZE=64 CONFIGURATION=Debug
# Linux with Clang 3.6, 64-bit Release
- os: linux
compiler: clang
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['clang-3.6', 'g++-5', 'valgrind', 'p7zip-full', 'portaudio19-dev', 'cmake', 'cmake-data']
env: COMPILER=clang++-3.6 WORDSIZE=64 CONFIGURATION=Release
# Linux with GCC 5.x, 32-bit Release
# - os: linux
# compiler: gcc
# addons:
# apt:
# sources: ['ubuntu-toolchain-r-test']
# packages: ['g++-5', 'g++-5-multilib', 'linux-libc-dev:i386', 'valgrind:i386',
# 'p7zip-full', 'libxext-dev:i386', 'libglapi-mesa:i386',
# 'libgl1-mesa-glx:i386', 'libgl1-mesa-dev:i386',
# 'portaudio19-dev:i386', 'libglib2.0-0:i386', 'cmake', 'cmake-data']
# env: COMPILER=g++-5 WORDSIZE=32 CONFIGURATION=Release
# Linux with GCC 5.x, 64-bit Release
- os: linux
compiler: gcc
addons:
apt:
sources: ['ubuntu-toolchain-r-test', 'george-edison55-precise-backports']
packages: ['g++-5', 'valgrind', 'p7zip-full', 'portaudio19-dev', 'cmake', 'cmake-data']
env: COMPILER=g++-5 WORDSIZE=64 CONFIGURATION=Release
# Code formatting verification
- os: linux
compiler: clang
env: CONFIGURATION=Formatting LLVM_VERSION=3.8.0
before_install:
# Do indentation check
- |
if [ "$CONFIGURATION" = "Formatting" ]; then
wget -O - http://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
echo deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-3.8 main | sudo tee --append /etc/apt/sources.list
sudo apt-get update && sudo apt-get -y install clang-format-3.8
python ci/check-formatting.py -c $(which clang-format-3.8)
exit $?
fi
# Override Travis' CXX Flag
- CXX=$COMPILER
# Install homebrew packages for Mac OS X
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew update && brew install cmake ninja qt5; fi
# Install QT for Linux manually, since the QT packages are not whitelisted by Travis
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then ./ci/install-qt-5.sh $WORDSIZE; fi
install:
- git submodule update --init --recursive
script:
- |
set -e
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
PATH=`brew --prefix qt5`/bin:${PATH}
python ci/configure.py --configuration $CONFIGURATION --generator Ninja --with-qt
python ci/build.py --configuration $CONFIGURATION --arguments "all -v"
else
if [ "$WORDSIZE" -eq 64 ]; then
PATH=$PWD/5.5/gcc_64:${PATH}
python ci/configure.py --configuration $CONFIGURATION --wordsize $WORDSIZE --with-qt
else
PATH=$PWD/5.5/gcc:${PATH}
python ci/configure.py --configuration $CONFIGURATION --wordsize $WORDSIZE --with-qt
fi
python ci/build.py --configuration $CONFIGURATION --arguments "VERBOSE=1 -j8"
fi
# Build Tests and run with Valgrind (Linux 64-bit only). Mac OSX supports
# valgrind via homebrew, but there is no bottle formula, so it must be
# compiled by brew and this takes way too much time on the build server.
- |
set -e
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
python ci/run-tests.py --target LinkCoreTest --valgrind
python ci/run-tests.py --target LinkDiscoveryTest --valgrind
else
python ci/run-tests.py --target LinkCoreTest
python ci/run-tests.py --target LinkDiscoveryTest
fi
link-Link-1.0.0/.clang-format 0000644 0001750 0001750 00000002600 12765602651 016155 0 ustar zmoelnig zmoelnig Language: Cpp
BasedOnStyle: LLVM
AccessModifierOffset: -2
AlignAfterOpenBracket: false
AlignEscapedNewlinesLeft: false
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: true
BinPackParameters: false
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Allman
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: true
ColumnLimit: 90
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 2
ContinuationIndentWidth: 2
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DerivePointerBinding: false
IndentCaseLabels: false
IndentFunctionDeclarationAfterType: false
IndentWidth: 2
KeepEmptyLinesAtTheStartOfBlocks: true
MaxEmptyLinesToKeep: 2
NamespaceIndentation: None
PenaltyBreakBeforeFirstCallParameter: 0
PenaltyReturnTypeOnItsOwnLine: 1000
PointerAlignment: Left
PointerBindsToType: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
Standard: C++11
UseTab: Never
link-Link-1.0.0/include/ 0000755 0001750 0001750 00000000000 12765602651 015227 5 ustar zmoelnig zmoelnig link-Link-1.0.0/include/CMakeLists.txt 0000644 0001750 0001750 00000011220 12765602651 017763 0 ustar zmoelnig zmoelnig cmake_minimum_required(VERSION 3.0)
project(LinkCore)
# ____
# / ___|___ _ __ ___
# | | / _ \| '__/ _ \
# | |__| (_) | | | __/
# \____\___/|_| \___|
#
set(link_core_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ableton/link)
set(link_core_HEADERS
${link_core_DIR}/Beats.hpp
${link_core_DIR}/ClientSessionTimelines.hpp
${link_core_DIR}/Controller.hpp
${link_core_DIR}/Gateway.hpp
${link_core_DIR}/GhostXForm.hpp
${link_core_DIR}/HostTimeFilter.hpp
${link_core_DIR}/Kalman.hpp
${link_core_DIR}/LinearRegression.hpp
${link_core_DIR}/Measurement.hpp
${link_core_DIR}/MeasurementEndpointV4.hpp
${link_core_DIR}/MeasurementService.hpp
${link_core_DIR}/NodeId.hpp
${link_core_DIR}/NodeState.hpp
${link_core_DIR}/PayloadEntries.hpp
${link_core_DIR}/Peers.hpp
${link_core_DIR}/PeerState.hpp
${link_core_DIR}/Phase.hpp
${link_core_DIR}/PingResponder.hpp
${link_core_DIR}/SessionId.hpp
${link_core_DIR}/Sessions.hpp
${link_core_DIR}/Tempo.hpp
${link_core_DIR}/Timeline.hpp
${link_core_DIR}/v1/Messages.hpp
PARENT_SCOPE
)
# ____ _
# | _ \(_)___ ___ _____ _____ _ __ _ _
# | | | | / __|/ __/ _ \ \ / / _ \ '__| | | |
# | |_| | \__ \ (_| (_) \ V / __/ | | |_| |
# |____/|_|___/\___\___/ \_/ \___|_| \__, |
# |___/
set(link_discovery_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ableton/discovery)
set(link_discovery_HEADERS
${link_discovery_DIR}/InterfaceScanner.hpp
${link_discovery_DIR}/IpV4Interface.hpp
${link_discovery_DIR}/MessageTypes.hpp
${link_discovery_DIR}/NetworkByteStreamSerializable.hpp
${link_discovery_DIR}/Payload.hpp
${link_discovery_DIR}/PeerGateway.hpp
${link_discovery_DIR}/PeerGateways.hpp
${link_discovery_DIR}/Service.hpp
${link_discovery_DIR}/Socket.hpp
${link_discovery_DIR}/UdpMessenger.hpp
${link_discovery_DIR}/v1/Messages.hpp
PARENT_SCOPE
)
# ____ _ _ __
# | _ \| | __ _| |_ / _| ___ _ __ _ __ ___
# | |_) | |/ _` | __| |_ / _ \| '__| '_ ` _ \
# | __/| | (_| | |_| _| (_) | | | | | | | |
# |_| |_|\__,_|\__|_| \___/|_| |_| |_| |_|
#
set(link_platform_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ableton/platforms)
set(link_platform_HEADERS
${link_platform_DIR}/Config.hpp
${link_platform_DIR}/asio/AsioService.hpp
${link_platform_DIR}/asio/AsioTimer.hpp
${link_platform_DIR}/asio/AsioWrapper.hpp
${link_platform_DIR}/asio/Context.hpp
${link_platform_DIR}/asio/PooledHandlerContext.hpp
${link_platform_DIR}/asio/Socket.hpp
${link_platform_DIR}/asio/Util.hpp
)
if(UNIX)
set(link_platform_HEADERS
${link_platform_HEADERS}
${link_platform_DIR}/posix/ScanIpIfAddrs.hpp
)
if(APPLE)
set(link_platform_HEADERS
${link_platform_HEADERS}
${link_platform_DIR}/darwin/Clock.hpp
${link_platform_DIR}/darwin/Darwin.hpp
)
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
set(link_platform_HEADERS
${link_platform_HEADERS}
${link_platform_DIR}/linux/Linux.hpp
${link_platform_DIR}/stl/Clock.hpp
)
endif()
elseif(WIN32)
set(link_platform_HEADERS
${link_platform_HEADERS}
${link_platform_DIR}/windows/Clock.hpp
${link_platform_DIR}/windows/ScanIpIfAddrs.hpp
)
endif()
set(link_platform_HEADERS
${link_platform_HEADERS}
PARENT_SCOPE
)
# _ _ _ _ _
# | | | | |_(_) |
# | | | | __| | |
# | |_| | |_| | |
# \___/ \__|_|_|
#
set(link_util_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ableton/util)
set(link_util_HEADERS
${link_util_DIR}/Injected.hpp
${link_util_DIR}/Log.hpp
${link_util_DIR}/SafeAsyncHandler.hpp
${link_util_DIR}/SampleTiming.hpp
PARENT_SCOPE
)
# _____ _ _
# | ____|_ ___ __ ___ _ __| |_ ___ __| |
# | _| \ \/ / '_ \ / _ \| '__| __/ _ \/ _` |
# | |___ > <| |_) | (_) | | | || __/ (_| |
# |_____/_/\_\ .__/ \___/|_| \__\___|\__,_|
# |_|
# This list contains all of the headers needed by most Link projects.
# Usually, just adding this variable to your linker targets will place
# all relevant Link headers in your project.
set(link_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ableton)
set(link_HEADERS
${link_core_HEADERS}
${link_discovery_HEADERS}
${link_platform_HEADERS}
${link_util_HEADERS}
${link_DIR}/Link.hpp
${link_DIR}/Link.ipp
PARENT_SCOPE
)
set(link_test_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ableton/test)
set(link_test_HEADERS
${link_discovery_DIR}/test/Interface.hpp
${link_discovery_DIR}/test/PayloadEntries.hpp
${link_discovery_DIR}/test/Socket.hpp
${link_util_DIR}/test/IoService.hpp
${link_util_DIR}/test/Timer.hpp
${link_test_DIR}/CatchWrapper.hpp
${link_test_DIR}/serial_io/Context.hpp
${link_test_DIR}/serial_io/Fixture.hpp
${link_test_DIR}/serial_io/SchedulerTree.hpp
${link_test_DIR}/serial_io/Timer.hpp
PARENT_SCOPE
)
link-Link-1.0.0/include/ableton/ 0000755 0001750 0001750 00000000000 12765602651 016653 5 ustar zmoelnig zmoelnig link-Link-1.0.0/include/ableton/Link.hpp 0000644 0001750 0001750 00000027165 12765602651 020274 0 ustar zmoelnig zmoelnig /*! @file Link.hpp
* @copyright 2016, Ableton AG, Berlin. All rights reserved.
* @brief Library for cross-device shared tempo and quantized beat grid
*
* @license:
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* If you would like to incorporate Link into a proprietary software application,
* please contact .
*/
#pragma once
#include
#include
#include
namespace ableton
{
/*! @class Link
* @brief Class that represents a participant in a Link session.
*
* @discussion Each Link instance has its own beat timeline that
* starts running from beat 0 at the initial tempo when
* constructed. A Link instance is initially disabled after
* construction, which means that it will not communicate on the
* network. Once enabled, a Link instance initiates network
* communication in an effort to discover other peers. When peers are
* discovered, they immediately become part of a shared Link session.
*
* Each method of the Link type documents its thread-safety and
* realtime-safety properties. When a method is marked thread-safe,
* it means it is safe to call from multiple threads
* concurrently. When a method is marked realtime-safe, it means that
* it does not block and is appropriate for use in the thread that
* performs audio IO.
*
* Link provides one Timeline capture/commit method pair for use in the
* audio thread and one for all other application contexts. In
* general, modifying the Link timeline should be done in the audio
* thread for the most accurate timing results. The ability to modify
* the Link timeline from application threads should only be used in
* cases where an application's audio thread is not actively running
* or if it doesn't generate audio at all. Modifying the Link
* timeline from both the audio thread and an application thread
* concurrently is not advised and will potentially lead to
* unexpected behavior.
*/
class Link
{
public:
using Clock = link::platform::Clock;
class Timeline;
/*! @brief Construct with an initial tempo. */
Link(double bpm);
/*! @brief Link instances cannot be copied or moved */
Link(const Link&) = delete;
Link& operator=(const Link&) = delete;
Link(Link&&) = delete;
Link& operator=(Link&&) = delete;
/*! @brief Is Link currently enabled?
* Thread-safe: yes
* Realtime-safe: yes
*/
bool isEnabled() const;
/*! @brief Enable/disable Link.
* Thread-safe: yes
* Realtime-safe: no
*/
void enable(bool bEnable);
/*! @brief How many peers are currently connected in a Link session?
* Thread-safe: yes
* Realtime-safe: yes
*/
std::size_t numPeers() const;
/*! @brief Register a callback to be notified when the number of
* peers in the Link session changes.
* Thread-safe: yes
* Realtime-safe: no
*
* @discussion The callback is invoked on a Link-managed thread.
*
* @param callback The callback signature is:
* void (std::size_t numPeers)
*/
template
void setNumPeersCallback(Callback callback);
/*! @brief Register a callback to be notified when the session
* tempo changes.
* Thread-safe: yes
* Realtime-safe: no
*
* @discussion The callback is invoked on a Link-managed thread.
*
* @param callback The callback signature is: void (double bpm)
*/
template
void setTempoCallback(Callback callback);
/*! @brief The clock used by Link.
* Thread-safe: yes
* Realtime-safe: yes
*
* @discussion The Clock type is a platform-dependent
* representation of the system clock. It exposes a ticks() method
* that returns the current ticks of the system clock as well as
* micros(), which is a normalized representation of the current system
* time in std::chrono::microseconds. It also provides conversion
* functions ticksToMicros() and microsToTicks() to faciliate
* converting between these units.
*/
Clock clock() const;
/*! @brief Capture the current Link timeline from the audio thread.
* Thread-safe: no
* Realtime-safe: yes
*
* @discussion This method should ONLY be called in the audio thread
* and must not be accessed from any other threads. The returned
* Timeline stores a snapshot of the current Link state, so it
* should be captured and used in a local scope. Storing the
* Timeline for later use in a different context is not advised
* because it will provide an outdated view on the Link state.
*/
Timeline captureAudioTimeline() const;
/*! @brief Commit the given timeline to the Link session from the
* audio thread.
* Thread-safe: no
* Realtime-safe: yes
*
* @discussion This method should ONLY be called in the audio
* thread. The given timeline will replace the current Link
* timeline. Modifications to the session based on the new timeline
* will be communicated to other peers in the session.
*/
void commitAudioTimeline(Timeline timeline);
/*! @brief Capture the current Link timeline from an application
* thread.
* Thread-safe: yes
* Realtime-safe: no
*
* @discussion Provides a mechanism for capturing the Link timeline
* from an application thread (other than the audio thread). The
* returned Timeline stores a snapshot of the current Link state,
* so it should be captured and used in a local scope. Storing the
* Timeline for later use in a different context is not advised
* because it will provide an outdated view on the Link state.
*/
Timeline captureAppTimeline() const;
/*! @brief Commit the given timeline to the Link session from an
* application thread.
* Thread-safe: yes
* Realtime-safe: no
*
* @discussion The given timeline will replace the current Link
* timeline. Modifications to the session based on the new timeline
* will be communicated to other peers in the session.
*/
void commitAppTimeline(Timeline timeline);
/*! @class Timeline
* @brief Representation of a mapping between time and beats for
* varying quanta.
*
* @discussion A Timeline object is intended for use in a local
* scope within a single thread - none of its methods are
* thread-safe. All of its methods are non-blocking, so it is safe
* to use from a realtime thread.
*/
class Timeline
{
public:
Timeline(const link::Timeline timeline, const bool bRespectQuantum);
/*! @brief: The tempo of the timeline, in bpm */
double tempo() const;
/*! @brief: Set the timeline tempo to the given bpm value, taking
* effect at the given time.
*/
void setTempo(double bpm, std::chrono::microseconds atTime);
/*! @brief: Get the beat value corresponding to the given time
* for the given quantum.
*
* @discussion: The magnitude of the resulting beat value is
* unique to this Link instance, but its phase with respect to
* the provided quantum is shared among all session
* peers. For non-negative beat values, the following
* property holds: fmod(beatAtTime(t, q), q) == phaseAtTime(t, q)
*/
double beatAtTime(std::chrono::microseconds time, double quantum) const;
/*! @brief: Get the session phase at the given time for the given
* quantum.
*
* @discussion: The result is in the interval [0, quantum). The
* result is equivalent to fmod(beatAtTime(t, q), q) for
* non-negative beat values. This method is convenient if the
* client is only interested in the phase and not the beat
* magnitude. Also, unlike fmod, it handles negative beat values
* correctly.
*/
double phaseAtTime(std::chrono::microseconds time, double quantum) const;
/*! @brief: Get the time at which the given beat occurs for the
* given quantum.
*
* @discussion: The inverse of beatAtTime, assuming a constant
* tempo. beatAtTime(timeAtBeat(b, q), q) === b.
*/
std::chrono::microseconds timeAtBeat(double beat, double quantum) const;
/*! @brief: Attempt to map the given beat to the given time in the
* context of the given quantum.
*
* @discussion: This method behaves differently depending on the
* state of the session. If no other peers are connected,
* then this instance is in a session by itself and is free to
* re-map the beat/time relationship whenever it pleases. In this
* case, beatAtTime(time, quantum) == beat after this method has
* been called.
*
* If there are other peers in the session, this instance
* should not abruptly re-map the beat/time relationship in the
* session because that would lead to beat discontinuities among
* the other peers. In this case, the given beat will be mapped
* to the next time value greater than the given time with the
* same phase as the given beat.
*
* This method is specifically designed to enable the concept of
* "quantized launch" in client applications. If there are no other
* peers in the session, then an event (such as starting
* transport) happens immediately when it is requested. If there
* are other peers, however, we wait until the next time at which
* the session phase matches the phase of the event, thereby
* executing the event in-phase with the other peers in the
* session. The client only needs to invoke this method to
* achieve this behavior and should not need to explicitly check
* the number of peers.
*/
void requestBeatAtTime(double beat, std::chrono::microseconds time, double quantum);
/*! @brief: Rudely re-map the beat/time relationship for all peers
* in a session.
*
* @discussion: DANGER: This method should only be needed in
* certain special circumstances. Most applications should not
* use it. It is very similar to requestBeatAtTime except that it
* does not fall back to the quantizing behavior when it is in a
* session with other peers. Calling this method will
* unconditionally map the given beat to the given time and
* broadcast the result to the session. This is very anti-social
* behavior and should be avoided.
*
* One of the few legitimate uses of this method is to
* synchronize a Link session with an external clock source. By
* periodically forcing the beat/time mapping according to an
* external clock source, a peer can effectively bridge that
* clock into a Link session. Much care must be taken at the
* application layer when implementing such a feature so that
* users do not accidentally disrupt Link sessions that they may
* join.
*/
void forceBeatAtTime(double beat, std::chrono::microseconds time, double quantum);
private:
friend Link;
link::Timeline mOriginalTimeline;
bool mbRespectQuantum;
link::Timeline mTimeline;
};
private:
std::mutex mCallbackMutex;
link::PeerCountCallback mPeerCountCallback;
link::TempoCallback mTempoCallback;
Clock mClock;
link::platform::Controller mController;
};
} // ableton
#include
link-Link-1.0.0/include/ableton/platforms/ 0000755 0001750 0001750 00000000000 12765602651 020662 5 ustar zmoelnig zmoelnig link-Link-1.0.0/include/ableton/platforms/linux/ 0000755 0001750 0001750 00000000000 12765602651 022021 5 ustar zmoelnig zmoelnig link-Link-1.0.0/include/ableton/platforms/linux/Linux.hpp 0000644 0001750 0001750 00000001744 12765602651 023637 0 ustar zmoelnig zmoelnig /* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* If you would like to incorporate Link into a proprietary software application,
* please contact .
*/
#pragma once
#include
#ifndef ntohll
#define ntohll(x) bswap_64(x)
#endif
#ifndef htonll
#define htonll(x) bswap_64(x)
#endif
link-Link-1.0.0/include/ableton/platforms/asio/ 0000755 0001750 0001750 00000000000 12765602651 021615 5 ustar zmoelnig zmoelnig link-Link-1.0.0/include/ableton/platforms/asio/AsioTimer.hpp 0000644 0001750 0001750 00000007011 12765602651 024221 0 ustar zmoelnig zmoelnig /* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* If you would like to incorporate Link into a proprietary software application,
* please contact .
*/
#pragma once
#include
#include
#include
namespace ableton
{
namespace platforms
{
namespace asio
{
// This implementation is based on the boost::asio::system_timer concept.
// Since boost::system_timer doesn't support move semantics, we create a wrapper
// with a unique_ptr to get a movable type. It also handles an inconvenient
// aspect of asio timers, which is that you must explicitly guard against the
// handler firing after cancellation. We handle this by use of the SafeAsyncHandler
// utility. AsioTimer therefore guarantees that a handler will not be called after
// the destruction of the timer, or after the timer has been canceled.
class AsioTimer
{
public:
using ErrorCode = ::asio::error_code;
using TimePoint = std::chrono::system_clock::time_point;
AsioTimer(::asio::io_service& io)
: mpTimer(new ::asio::system_timer(io))
, mpAsyncHandler(std::make_shared())
{
}
~AsioTimer()
{
// The timer may not be valid anymore if this instance was moved from
if (mpTimer != nullptr)
{
// Ignore errors during cancellation
cancel();
}
}
AsioTimer(const AsioTimer&) = delete;
AsioTimer& operator=(const AsioTimer&) = delete;
// Enable move construction but not move assignment. Move assignment
// would get weird - would have to handle outstanding handlers
AsioTimer(AsioTimer&& rhs)
: mpTimer(std::move(rhs.mpTimer))
, mpAsyncHandler(std::move(rhs.mpAsyncHandler))
{
}
void expires_at(std::chrono::system_clock::time_point tp)
{
mpTimer->expires_at(std::move(tp));
}
template
void expires_from_now(T duration)
{
mpTimer->expires_from_now(std::move(duration));
}
ErrorCode cancel()
{
ErrorCode ec;
mpTimer->cancel(ec);
mpAsyncHandler->mpHandler = nullptr;
return ec;
}
template
void async_wait(Handler handler)
{
*mpAsyncHandler = std::move(handler);
mpTimer->async_wait(util::makeAsyncSafe(mpAsyncHandler));
}
TimePoint now() const
{
return std::chrono::system_clock::now();
}
private:
struct AsyncHandler
{
template
AsyncHandler& operator=(Handler handler)
{
mpHandler = [handler](ErrorCode ec) { handler(std::move(ec)); };
return *this;
}
void operator()(ErrorCode ec)
{
if (mpHandler)
{
mpHandler(std::move(ec));
}
}
std::function mpHandler;
};
std::unique_ptr<::asio::system_timer> mpTimer;
std::shared_ptr mpAsyncHandler;
};
} // namespace asio
} // namespace platforms
} // namespace ableton
link-Link-1.0.0/include/ableton/platforms/asio/AsioService.hpp 0000644 0001750 0001750 00000005007 12765602651 024544 0 ustar zmoelnig zmoelnig /* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* If you would like to incorporate Link into a proprietary software application,
* please contact .
*/
#pragma once
#include
#include
#include
namespace ableton
{
namespace platforms
{
namespace asio
{
class AsioService
{
public:
using Timer = AsioTimer;
AsioService()
: AsioService(DefaultHandler{})
{
}
template
explicit AsioService(ExceptionHandler exceptHandler)
: mpWork(new ::asio::io_service::work(mService))
{
mThread =
std::thread{[](::asio::io_service& service, ExceptionHandler handler) {
for (;;)
{
try
{
service.run();
break;
}
catch (const typename ExceptionHandler::Exception& exception)
{
handler(exception);
}
}
},
std::ref(mService), std::move(exceptHandler)};
}
~AsioService()
{
mpWork.reset();
mThread.join();
}
AsioTimer makeTimer()
{
return {mService};
}
template
void post(Handler handler)
{
mService.post(std::move(handler));
}
::asio::io_service mService;
private:
// Default handler is hidden and defines a hidden exception type
// that will never be thrown by other code, so it effectively does
// not catch.
struct DefaultHandler
{
struct Exception
{
};
void operator()(const Exception&)
{
}
};
std::unique_ptr<::asio::io_service::work> mpWork;
std::thread mThread;
};
} // namespace asio
} // namespace platforms
} // namespace ableton
link-Link-1.0.0/include/ableton/platforms/asio/AsioWrapper.hpp 0000644 0001750 0001750 00000004104 12765602651 024561 0 ustar zmoelnig zmoelnig /* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* If you would like to incorporate Link into a proprietary software application,
* please contact .
*/
#pragma once
/*!
* \brief Wrapper file for AsioStandalone library
*
* This file includes all necessary headers from the AsioStandalone library which are used
* by Link.
*/
#pragma push_macro("ASIO_STANDALONE")
#define ASIO_STANDALONE 1
#pragma push_macro("ASIO_NO_TYPEID")
#define ASIO_NO_TYPEID 1
#if LINK_PLATFORM_WINDOWS
#pragma push_macro("INCL_EXTRA_HTON_FUNCTIONS")
#define INCL_EXTRA_HTON_FUNCTIONS 1
#endif
// Visual Studio: Disable Warnings
#if defined(_MSC_VER)
#pragma warning(push)
// C4191: 'operator/operation': unsafe conversion from 'type of expression' to
// 'type required'
#pragma warning(disable : 4191)
// C4548: expression before comma has no effect; expected expression with side-effect
#pragma warning(disable : 4548)
// C4619: #pragma warning : there is no warning number 'number'
#pragma warning(disable : 4619)
// C4675: 'function' : resolved overload was found by argument-dependent lookup
#pragma warning(disable : 4675)
#endif
#include
#include
#if LINK_PLATFORM_WINDOWS
#pragma pop_macro("INCL_EXTRA_HTON_FUNCTIONS")
#endif
#pragma pop_macro("ASIO_STANDALONE")
#pragma pop_macro("ASIO_NO_TYPEID")
// Visual Studio: Disable Warnings
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
link-Link-1.0.0/include/ableton/platforms/asio/Context.hpp 0000644 0001750 0001750 00000011542 12765602651 023755 0 ustar zmoelnig zmoelnig /* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* If you would like to incorporate Link into a proprietary software application,
* please contact .
*/
#pragma once
#include
#include
#include
#include
#include
#include
namespace ableton
{
namespace platforms
{
namespace asio
{
template
class Context
{
public:
using Timer = AsioTimer;
using Log = LogT;
template
using Socket = asio::Socket;
template
using RealTimeContext = PooledHandlerContext;
Context()
: Context(DefaultHandler{})
{
}
template
explicit Context(ExceptionHandler exceptHandler)
: mpService(new ::asio::io_service())
, mpWork(new ::asio::io_service::work(*mpService))
{
mThread =
std::thread{[](::asio::io_service& service, ExceptionHandler handler) {
for (;;)
{
try
{
service.run();
break;
}
catch (const typename ExceptionHandler::Exception& exception)
{
handler(exception);
}
}
},
std::ref(*mpService), std::move(exceptHandler)};
}
Context(const Context&) = delete;
Context(Context&& rhs)
: mpService(std::move(rhs.mpService))
, mpWork(std::move(rhs.mpWork))
, mThread(std::move(rhs.mThread))
, mLog(std::move(rhs.mLog))
, mScanIpIfAddrs(std::move(rhs.mScanIpIfAddrs))
{
}
~Context()
{
if (mpService)
{
mpWork.reset();
mThread.join();
}
}
template
Socket openUnicastSocket(const ::asio::ip::address_v4& addr)
{
auto socket = Socket{*mpService};
socket.mpImpl->mSocket.set_option(
::asio::ip::multicast::enable_loopback(addr.is_loopback()));
socket.mpImpl->mSocket.set_option(::asio::ip::multicast::outbound_interface(addr));
socket.mpImpl->mSocket.bind(::asio::ip::udp::endpoint{addr, 0});
return socket;
}
template
Socket openMulticastSocket(const ::asio::ip::address_v4& addr)
{
auto socket = Socket{*mpService};
socket.mpImpl->mSocket.set_option(::asio::ip::udp::socket::reuse_address(true));
socket.mpImpl->mSocket.set_option(
::asio::socket_base::broadcast(!addr.is_loopback()));
socket.mpImpl->mSocket.set_option(
::asio::ip::multicast::enable_loopback(addr.is_loopback()));
socket.mpImpl->mSocket.set_option(::asio::ip::multicast::outbound_interface(addr));
socket.mpImpl->mSocket.bind({::asio::ip::address::from_string("0.0.0.0"),
discovery::multicastEndpoint().port()});
socket.mpImpl->mSocket.set_option(::asio::ip::multicast::join_group(
discovery::multicastEndpoint().address().to_v4(), addr));
return socket;
}
std::vector<::asio::ip::address> scanNetworkInterfaces()
{
return mScanIpIfAddrs();
}
Timer makeTimer() const
{
return {*mpService};
}
Log& log()
{
return mLog;
}
template
void async(Handler handler)
{
mpService->post(std::move(handler));
}
Context clone() const
{
return {};
}
template
Context clone(ExceptionHandler handler) const
{
return Context{std::move(handler)};
}
private:
// Default handler is hidden and defines a hidden exception type
// that will never be thrown by other code, so it effectively does
// not catch.
struct DefaultHandler
{
struct Exception
{
};
void operator()(const Exception&)
{
}
};
std::unique_ptr<::asio::io_service> mpService;
std::unique_ptr<::asio::io_service::work> mpWork;
std::thread mThread;
Log mLog;
ScanIpIfAddrs mScanIpIfAddrs;
};
} // namespace asio
} // namespace platforms
} // namespace ableton
link-Link-1.0.0/include/ableton/platforms/asio/PooledHandlerContext.hpp 0000644 0001750 0001750 00000006106 12765602651 026416 0 ustar zmoelnig zmoelnig /* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* If you would like to incorporate Link into a proprietary software application,
* please contact .
*/
#pragma once
#include
#include
namespace ableton
{
namespace platforms
{
namespace asio
{
template
struct PooledHandlerContext
{
PooledHandlerContext(util::Injected io)
: mIo(std::move(io))
{
// initialize the handler free store
mFreeStack.reserve(MaxNumHandlers);
for (std::size_t i = 0; i < MaxNumHandlers; ++i)
{
mFreeStack.push_back(reinterpret_cast(mRaw + i));
}
}
template
void async(Handler handler)
{
try
{
mIo->async(HandlerWrapper{*this, std::move(handler)});
}
catch (std::bad_alloc)
{
warning(mIo->log()) << "Handler dropped due to low memory pool";
}
}
template
struct HandlerWrapper
{
HandlerWrapper(PooledHandlerContext& context, Handler handler)
: mContext(context)
, mHandler(std::move(handler))
{
}
void operator()()
{
mHandler();
}
// Use pooled allocation so that posting handlers will not cause
// system allocation
friend void* asio_handler_allocate(
const std::size_t size, HandlerWrapper* const pHandler)
{
if (size > MaxHandlerSize || pHandler->mContext.mFreeStack.empty())
{
// Going over the max handler size is a programming error, as
// this is not a dynamically variable value.
assert(size <= MaxHandlerSize);
throw std::bad_alloc();
}
else
{
const auto p = pHandler->mContext.mFreeStack.back();
pHandler->mContext.mFreeStack.pop_back();
return p;
}
}
friend void asio_handler_deallocate(
void* const p, std::size_t, HandlerWrapper* const pHandler)
{
pHandler->mContext.mFreeStack.push_back(p);
}
PooledHandlerContext& mContext;
Handler mHandler;
};
using MemChunk = typename std::aligned_storage::value>::type;
MemChunk mRaw[MaxNumHandlers];
std::vector mFreeStack;
util::Injected mIo;
};
} // namespace asio
} // namespace platforms
} // namespace ableton
link-Link-1.0.0/include/ableton/platforms/asio/Socket.hpp 0000644 0001750 0001750 00000005747 12765602651 023573 0 ustar zmoelnig zmoelnig /* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* If you would like to incorporate Link into a proprietary software application,
* please contact .
*/
#pragma once
#include
#include
#include
#include
namespace ableton
{
namespace platforms
{
namespace asio
{
template
struct Socket
{
Socket(::asio::io_service& io)
: mpImpl(std::make_shared(io))
{
}
Socket(const Socket&) = delete;
Socket& operator=(const Socket&) = delete;
Socket(Socket&& rhs)
: mpImpl(std::move(rhs.mpImpl))
{
}
std::size_t send(const uint8_t* const pData,
const size_t numBytes,
const ::asio::ip::udp::endpoint& to)
{
assert(numBytes < MaxPacketSize);
return mpImpl->mSocket.send_to(::asio::buffer(pData, numBytes), to);
}
template
void receive(Handler handler)
{
mpImpl->mHandler = std::move(handler);
mpImpl->mSocket.async_receive_from(
::asio::buffer(mpImpl->mReceiveBuffer, MaxPacketSize), mpImpl->mSenderEndpoint,
util::makeAsyncSafe(mpImpl));
}
::asio::ip::udp::endpoint endpoint() const
{
return mpImpl->mSocket.local_endpoint();
}
struct Impl
{
Impl(::asio::io_service& io)
: mSocket(io, ::asio::ip::udp::v4())
{
}
~Impl()
{
// Ignore error codes in shutdown and close as the socket may
// have already been forcibly closed
::asio::error_code ec;
mSocket.shutdown(::asio::ip::udp::socket::shutdown_both, ec);
mSocket.close(ec);
}
void operator()(const ::asio::error_code& error, const std::size_t numBytes)
{
if (!error && numBytes > 0 && numBytes <= MaxPacketSize)
{
const auto bufBegin = begin(mReceiveBuffer);
mHandler(mSenderEndpoint, bufBegin, bufBegin + static_cast(numBytes));
}
}
::asio::ip::udp::socket mSocket;
::asio::ip::udp::endpoint mSenderEndpoint;
using Buffer = std::array;
Buffer mReceiveBuffer;
using ByteIt = typename Buffer::const_iterator;
std::function mHandler;
};
std::shared_ptr mpImpl;
};
} // namespace asio
} // namespace platforms
} // namespace ableton
link-Link-1.0.0/include/ableton/platforms/asio/Util.hpp 0000644 0001750 0001750 00000002422 12765602651 023243 0 ustar zmoelnig zmoelnig /* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* If you would like to incorporate Link into a proprietary software application,
* please contact .
*/
#pragma once
#include
namespace ableton
{
namespace platforms
{
namespace asio
{
// Utility for making v4 or v6 ip addresses from raw bytes in network byte-order
template
AsioAddrType makeAddress(const char* pAddr)
{
using namespace std;
typename AsioAddrType::bytes_type bytes;
copy(pAddr, pAddr + bytes.size(), begin(bytes));
return AsioAddrType{bytes};
}
} // asio
} // platforms
} // ableton
link-Link-1.0.0/include/ableton/platforms/Config.hpp 0000644 0001750 0001750 00000004125 12765602651 022602 0 ustar zmoelnig zmoelnig /* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* If you would like to incorporate Link into a proprietary software application,
* please contact .
*/
#pragma once
#include
#include
#if LINK_PLATFORM_WINDOWS
#include
#include
#include
#elif LINK_PLATFORM_MACOSX
#include
#include
#include
#elif LINK_PLATFORM_LINUX
#include
#include
#include
#endif
namespace ableton
{
namespace link
{
namespace platform
{
#if LINK_PLATFORM_WINDOWS
using Clock = platforms::windows::Clock;
using IoContext =
platforms::asio::Context;
#elif LINK_PLATFORM_MACOSX
using Clock = platforms::darwin::Clock;
using IoContext =
platforms::asio::Context;
#elif LINK_PLATFORM_LINUX
using Clock = platforms::stl::Clock;
using IoContext =
platforms::asio::Context;
#endif
using Controller = Controller;
} // platform
} // link
} // ableton
link-Link-1.0.0/include/ableton/platforms/darwin/ 0000755 0001750 0001750 00000000000 12765602651 022146 5 ustar zmoelnig zmoelnig link-Link-1.0.0/include/ableton/platforms/darwin/Darwin.hpp 0000644 0001750 0001750 00000002101 12765602651 024075 0 ustar zmoelnig zmoelnig /* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* If you would like to incorporate Link into a proprietary software application,
* please contact .
*/
#pragma once
// ntohll and htonll are not defined in 10.7 SDK, so we provide a compatibility macro here
#ifndef ntohll
#define ntohll(x) __DARWIN_OSSwapInt64(x)
#endif
#ifndef htonll
#define htonll(x) __DARWIN_OSSwapInt64(x)
#endif
link-Link-1.0.0/include/ableton/platforms/darwin/Clock.hpp 0000644 0001750 0001750 00000003355 12765602651 023720 0 ustar zmoelnig zmoelnig /* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* If you would like to incorporate Link into a proprietary software application,
* please contact .
*/
#pragma once
#include
#include
namespace ableton
{
namespace platforms
{
namespace darwin
{
struct Clock
{
using Ticks = std::uint64_t;
using Micros = std::chrono::microseconds;
Clock()
{
mach_timebase_info_data_t timeInfo;
mach_timebase_info(&timeInfo);
// numer / denom gives nanoseconds, we want microseconds
mTicksToMicros = timeInfo.numer / (timeInfo.denom * 1000.);
}
Micros ticksToMicros(const Ticks ticks) const
{
return Micros{std::llround(mTicksToMicros * ticks)};
}
Ticks microsToTicks(const Micros micros) const
{
return static_cast(micros.count() / mTicksToMicros);
}
Ticks ticks() const
{
return mach_absolute_time();
}
std::chrono::microseconds micros() const
{
return ticksToMicros(ticks());
}
double mTicksToMicros;
};
} // namespace darwin
} // namespace platforms
} // namespace ableton
link-Link-1.0.0/include/ableton/platforms/posix/ 0000755 0001750 0001750 00000000000 12765602651 022024 5 ustar zmoelnig zmoelnig link-Link-1.0.0/include/ableton/platforms/posix/ScanIpIfAddrs.hpp 0000644 0001750 0001750 00000005702 12765602651 025153 0 ustar zmoelnig zmoelnig /* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* If you would like to incorporate Link into a proprietary software application,
* please contact .
*/
#pragma once
#include
#include
#include
#include
#include
#include
namespace ableton
{
namespace platforms
{
namespace posix
{
namespace detail
{
// RAII type to make [get,free]ifaddrs function pairs exception safe
class GetIfAddrs
{
public:
GetIfAddrs()
{
if (getifaddrs(&interfaces)) // returns 0 on success
{
interfaces = NULL;
}
}
~GetIfAddrs()
{
if (interfaces)
freeifaddrs(interfaces);
}
// RAII must not copy
GetIfAddrs(GetIfAddrs&) = delete;
GetIfAddrs& operator=(GetIfAddrs&) = delete;
template
void withIfAddrs(Function f)
{
if (interfaces)
f(*interfaces);
}
private:
struct ifaddrs* interfaces = NULL;
};
} // detail
// Posix implementation of ip interface address scanner
struct ScanIpIfAddrs
{
// Scan active network interfaces and return corresponding addresses
// for all ip-based interfaces.
std::vector<::asio::ip::address> operator()()
{
std::vector<::asio::ip::address> addrs;
detail::GetIfAddrs getIfAddrs;
getIfAddrs.withIfAddrs([&addrs](const struct ifaddrs& interfaces) {
const struct ifaddrs* interface;
for (interface = &interfaces; interface; interface = interface->ifa_next)
{
auto addr = reinterpret_cast(interface->ifa_addr);
if (addr && interface->ifa_flags & IFF_UP)
{
if (addr->sin_family == AF_INET)
{
auto bytes = reinterpret_cast(&addr->sin_addr);
addrs.emplace_back(asio::makeAddress<::asio::ip::address_v4>(bytes));
}
else if (addr->sin_family == AF_INET6)
{
auto addr6 = reinterpret_cast(addr);
auto bytes = reinterpret_cast(&addr6->sin6_addr);
addrs.emplace_back(asio::makeAddress<::asio::ip::address_v6>(bytes));
}
}
}
});
return addrs;
}
};
} // namespace posix
} // namespace platforms
} // namespace ableton
link-Link-1.0.0/include/ableton/platforms/windows/ 0000755 0001750 0001750 00000000000 12765602651 022354 5 ustar zmoelnig zmoelnig link-Link-1.0.0/include/ableton/platforms/windows/ScanIpIfAddrs.hpp 0000644 0001750 0001750 00000010033 12765602651 025474 0 ustar zmoelnig zmoelnig /* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* If you would like to incorporate Link into a proprietary software application,
* please contact .
*/
#pragma once
#include
#include
#include
#include
#include
#include
#include
#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "ws2_32.lib")
namespace ableton
{
namespace platforms
{
namespace windows
{
namespace detail
{
// RAII type to make [get,free]ifaddrs function pairs exception safe
class GetIfAddrs
{
public:
GetIfAddrs()
{
const int MAX_TRIES = 3; // MSFT recommendation
const int WORKING_BUFFER_SIZE = 15000; // MSFT recommendation
DWORD adapter_addrs_buffer_size = WORKING_BUFFER_SIZE;
for (int i = 0; i < MAX_TRIES; i++)
{
adapter_addrs = (IP_ADAPTER_ADDRESSES*)malloc(adapter_addrs_buffer_size);
assert(adapter_addrs);
DWORD error = ::GetAdaptersAddresses(AF_UNSPEC,
GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER
| GAA_FLAG_SKIP_FRIENDLY_NAME,
NULL, adapter_addrs, &adapter_addrs_buffer_size);
if (error == ERROR_SUCCESS)
{
break;
}
// if buffer too small, use new buffer size in next iteration
if (error == ERROR_BUFFER_OVERFLOW)
{
free(adapter_addrs);
adapter_addrs = NULL;
continue;
}
}
}
~GetIfAddrs()
{
if (adapter_addrs)
free(adapter_addrs);
}
// RAII must not copy
GetIfAddrs(GetIfAddrs&) = delete;
GetIfAddrs& operator=(GetIfAddrs&) = delete;
template
void withIfAddrs(Function f)
{
if (adapter_addrs)
f(*adapter_addrs);
}
private:
IP_ADAPTER_ADDRESSES* adapter_addrs;
IP_ADAPTER_ADDRESSES* adapter;
};
} // detail
struct ScanIpIfAddrs
{
// Scan active network interfaces and return corresponding addresses
// for all ip-based interfaces.
std::vector<::asio::ip::address> operator()()
{
std::vector<::asio::ip::address> addrs;
detail::GetIfAddrs getIfAddrs;
getIfAddrs.withIfAddrs([&addrs](const IP_ADAPTER_ADDRESSES& interfaces) {
const IP_ADAPTER_ADDRESSES* networkInterface;
for (networkInterface = &interfaces; networkInterface;
networkInterface = networkInterface->Next)
{
for (IP_ADAPTER_UNICAST_ADDRESS* address = networkInterface->FirstUnicastAddress;
NULL != address; address = address->Next)
{
auto family = address->Address.lpSockaddr->sa_family;
if (AF_INET == family)
{
// IPv4
SOCKADDR_IN* addr4 =
reinterpret_cast(address->Address.lpSockaddr);
auto bytes = reinterpret_cast(&addr4->sin_addr);
addrs.emplace_back(asio::makeAddress<::asio::ip::address_v4>(bytes));
}
else if (AF_INET6 == family)
{
SOCKADDR_IN6* addr6 =
reinterpret_cast(address->Address.lpSockaddr);
auto bytes = reinterpret_cast(&addr6->sin6_addr);
addrs.emplace_back(asio::makeAddress<::asio::ip::address_v6>(bytes));
}
}
}
});
return addrs;
}
};
} // namespace windows
} // namespace platforms
} // namespace ableton
link-Link-1.0.0/include/ableton/platforms/windows/Clock.hpp 0000644 0001750 0001750 00000003316 12765602651 024123 0 ustar zmoelnig zmoelnig /* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* If you would like to incorporate Link into a proprietary software application,
* please contact .
*/
#pragma once
#include
#include
namespace ableton
{
namespace platforms
{
namespace windows
{
struct Clock
{
using Ticks = std::int64_t;
using Micros = std::chrono::microseconds;
Clock()
{
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
mTicksToMicros = 1.0e6 / frequency.QuadPart;
}
Micros ticksToMicros(const Ticks ticks) const
{
return Micros{std::llround(mTicksToMicros * ticks)};
}
Ticks microsToTicks(const Micros micros) const
{
return static_cast(micros.count() / mTicksToMicros);
}
Ticks ticks() const
{
LARGE_INTEGER count;
QueryPerformanceCounter(&count);
return count.QuadPart;
}
std::chrono::microseconds micros() const
{
return ticksToMicros(ticks());
}
double mTicksToMicros;
};
} // namespace windows
} // namespace platforms
} // namespace ableton
link-Link-1.0.0/include/ableton/platforms/stl/ 0000755 0001750 0001750 00000000000 12765602651 021464 5 ustar zmoelnig zmoelnig link-Link-1.0.0/include/ableton/platforms/stl/Clock.hpp 0000644 0001750 0001750 00000002534 12765602651 023234 0 ustar zmoelnig zmoelnig /* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* If you would like to incorporate Link into a proprietary software application,
* please contact .
*/
#pragma once
#include
namespace ableton
{
namespace platforms
{
namespace stl
{
struct Clock
{
using Ticks = std::uint64_t;
Clock()
{
mStartTime = std::chrono::high_resolution_clock::now();
}
std::chrono::microseconds micros() const
{
using namespace std::chrono;
return duration_cast(high_resolution_clock::now() - mStartTime);
}
std::chrono::high_resolution_clock::time_point mStartTime;
};
} // namespace stl
} // namespace platforms
} // namespace ableton
link-Link-1.0.0/include/ableton/discovery/ 0000755 0001750 0001750 00000000000 12765602651 020662 5 ustar zmoelnig zmoelnig link-Link-1.0.0/include/ableton/discovery/v1/ 0000755 0001750 0001750 00000000000 12765602651 021210 5 ustar zmoelnig zmoelnig link-Link-1.0.0/include/ableton/discovery/v1/Messages.hpp 0000644 0001750 0001750 00000012770 12765602651 023477 0 ustar zmoelnig zmoelnig /* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* If you would like to incorporate Link into a proprietary software application,
* please contact .
*/
#pragma once
#include
#include
namespace ableton
{
namespace discovery
{
namespace v1
{
// The maximum size of a message, in bytes
const std::size_t kMaxMessageSize = 512;
// Utility typedef for an array of bytes of maximum message size
using MessageBuffer = std::array;
using MessageType = uint8_t;
using SessionGroupId = uint16_t;
const MessageType kInvalid = 0;
const MessageType kAlive = 1;
const MessageType kResponse = 2;
const MessageType kByeBye = 3;
template
struct MessageHeader
{
MessageType messageType;
uint8_t ttl;
SessionGroupId groupId;
NodeId ident;
friend std::uint32_t sizeInByteStream(const MessageHeader& header)
{
return discovery::sizeInByteStream(header.messageType)
+ discovery::sizeInByteStream(header.ttl)
+ discovery::sizeInByteStream(header.groupId)
+ discovery::sizeInByteStream(header.ident);
}
template
friend It toNetworkByteStream(const MessageHeader& header, It out)
{
return discovery::toNetworkByteStream(
header.ident, discovery::toNetworkByteStream(
header.groupId, discovery::toNetworkByteStream(header.ttl,
discovery::toNetworkByteStream(header.messageType,
std::move(out)))));
}
template
static std::pair fromNetworkByteStream(It begin, const It end)
{
using namespace std;
MessageHeader header;
tie(header.messageType, begin) =
Deserialize::fromNetworkByteStream(begin, end);
tie(header.ttl, begin) =
Deserialize::fromNetworkByteStream(begin, end);
tie(header.groupId, begin) =
Deserialize::fromNetworkByteStream(begin, end);
tie(header.ident, begin) =
Deserialize::fromNetworkByteStream(begin, end);
return make_pair(move(header), move(begin));
}
};
namespace detail
{
// Types that are only used in the sending/parsing of messages, not
// publicly exposed.
using ProtocolHeader = std::array;
const ProtocolHeader kProtocolHeader = {{'_', 'a', 's', 'd', 'p', '_', 'v', 1}};
// Must have at least kMaxMessageSize bytes available in the output stream
template
It encodeMessage(NodeId from,
const uint8_t ttl,
const MessageType messageType,
const Payload& payload,
It out)
{
using namespace std;
const MessageHeader header = {messageType, ttl, 0, std::move(from)};
const auto messageSize =
kProtocolHeader.size() + sizeInByteStream(header) + sizeInByteStream(payload);
if (messageSize < kMaxMessageSize)
{
return toNetworkByteStream(
payload, toNetworkByteStream(
header, copy(begin(kProtocolHeader), end(kProtocolHeader), move(out))));
}
else
{
throw range_error("Exceeded maximum message size");
}
}
} // namespace detail
template
It aliveMessage(NodeId from, const uint8_t ttl, const Payload& payload, It out)
{
return detail::encodeMessage(std::move(from), ttl, kAlive, payload, std::move(out));
}
template
It responseMessage(NodeId from, const uint8_t ttl, const Payload& payload, It out)
{
return detail::encodeMessage(std::move(from), ttl, kResponse, payload, std::move(out));
}
template
It byeByeMessage(NodeId from, It out)
{
return detail::encodeMessage(
std::move(from), 0, kByeBye, makePayload(), std::move(out));
}
template
std::pair, It> parseMessageHeader(It bytesBegin, const It bytesEnd)
{
using namespace std;
using ItDiff = typename iterator_traits::difference_type;
MessageHeader header = {};
const auto protocolHeaderSize = discovery::sizeInByteStream(detail::kProtocolHeader);
const auto minMessageSize =
static_cast(protocolHeaderSize + sizeInByteStream(header));
// If there are enough bytes in the stream to make a header and if
// the first bytes in the stream are the protocol header, then
// proceed to parse the stream.
if (distance(bytesBegin, bytesEnd) >= minMessageSize
&& equal(begin(detail::kProtocolHeader), end(detail::kProtocolHeader), bytesBegin))
{
tie(header, bytesBegin) = MessageHeader::fromNetworkByteStream(
bytesBegin + protocolHeaderSize, bytesEnd);
}
return make_pair(move(header), move(bytesBegin));
}
} // namespace v1
} // namespace discovery
} // namespace ableton
link-Link-1.0.0/include/ableton/discovery/PeerGateways.hpp 0000644 0001750 0001750 00000015250 12765602651 023776 0 ustar zmoelnig zmoelnig /* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* If you would like to incorporate Link into a proprietary software application,
* please contact .
*/
#pragma once
#include
#include
#include