Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

140 lines
4.3KB

  1. /* Copyright 2016, Ableton AG, Berlin. All rights reserved.
  2. *
  3. * This program is free software: you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation, either version 2 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. *
  16. * If you would like to incorporate Link into a proprietary software application,
  17. * please contact <link-devs@ableton.com>.
  18. */
  19. #pragma once
  20. #include <ableton/platforms/asio/AsioService.hpp>
  21. #include <ableton/platforms/asio/AsioWrapper.hpp>
  22. #include <ableton/util/SafeAsyncHandler.hpp>
  23. #include <array>
  24. #include <cassert>
  25. namespace ableton
  26. {
  27. namespace discovery
  28. {
  29. template <std::size_t MaxPacketSize>
  30. struct Socket
  31. {
  32. Socket(platforms::asio::AsioService& io)
  33. : mpImpl(std::make_shared<Impl>(io))
  34. {
  35. }
  36. Socket(const Socket&) = delete;
  37. Socket& operator=(const Socket&) = delete;
  38. Socket(Socket&& rhs)
  39. : mpImpl(std::move(rhs.mpImpl))
  40. {
  41. }
  42. std::size_t send(
  43. const uint8_t* const pData, const size_t numBytes, const asio::ip::udp::endpoint& to)
  44. {
  45. assert(numBytes < MaxPacketSize);
  46. return mpImpl->mSocket.send_to(asio::buffer(pData, numBytes), to);
  47. }
  48. template <typename Handler>
  49. void receive(Handler handler)
  50. {
  51. mpImpl->mHandler = std::move(handler);
  52. mpImpl->mSocket.async_receive_from(
  53. asio::buffer(mpImpl->mReceiveBuffer, MaxPacketSize), mpImpl->mSenderEndpoint,
  54. util::makeAsyncSafe(mpImpl));
  55. }
  56. asio::ip::udp::endpoint endpoint() const
  57. {
  58. return mpImpl->mSocket.local_endpoint();
  59. }
  60. struct Impl
  61. {
  62. Impl(platforms::asio::AsioService& io)
  63. : mSocket(io.mService, asio::ip::udp::v4())
  64. {
  65. }
  66. ~Impl()
  67. {
  68. // Ignore error codes in shutdown and close as the socket may
  69. // have already been forcibly closed
  70. asio::error_code ec;
  71. mSocket.shutdown(asio::ip::udp::socket::shutdown_both, ec);
  72. mSocket.close(ec);
  73. }
  74. void operator()(const asio::error_code& error, const std::size_t numBytes)
  75. {
  76. if (!error && numBytes > 0 && numBytes <= MaxPacketSize)
  77. {
  78. const auto bufBegin = begin(mReceiveBuffer);
  79. mHandler(mSenderEndpoint, bufBegin, bufBegin + static_cast<ptrdiff_t>(numBytes));
  80. }
  81. }
  82. asio::ip::udp::socket mSocket;
  83. asio::ip::udp::endpoint mSenderEndpoint;
  84. using Buffer = std::array<uint8_t, MaxPacketSize>;
  85. Buffer mReceiveBuffer;
  86. using ByteIt = typename Buffer::const_iterator;
  87. std::function<void(const asio::ip::udp::endpoint&, ByteIt, ByteIt)> mHandler;
  88. };
  89. std::shared_ptr<Impl> mpImpl;
  90. };
  91. // Configure an asio socket for receiving multicast messages
  92. template <std::size_t MaxPacketSize>
  93. void configureMulticastSocket(Socket<MaxPacketSize>& socket,
  94. const asio::ip::address_v4& addr,
  95. const asio::ip::udp::endpoint& multicastEndpoint)
  96. {
  97. socket.mpImpl->mSocket.set_option(asio::ip::udp::socket::reuse_address(true));
  98. // ???
  99. socket.mpImpl->mSocket.set_option(asio::socket_base::broadcast(!addr.is_loopback()));
  100. // ???
  101. socket.mpImpl->mSocket.set_option(
  102. asio::ip::multicast::enable_loopback(addr.is_loopback()));
  103. socket.mpImpl->mSocket.set_option(asio::ip::multicast::outbound_interface(addr));
  104. // Is from_string("0.0.0.0") best approach?
  105. socket.mpImpl->mSocket.bind(
  106. {asio::ip::address::from_string("0.0.0.0"), multicastEndpoint.port()});
  107. socket.mpImpl->mSocket.set_option(
  108. asio::ip::multicast::join_group(multicastEndpoint.address().to_v4(), addr));
  109. }
  110. // Configure an asio socket for receiving unicast messages
  111. template <std::size_t MaxPacketSize>
  112. void configureUnicastSocket(
  113. Socket<MaxPacketSize>& socket, const asio::ip::address_v4& addr)
  114. {
  115. // ??? really necessary?
  116. socket.mpImpl->mSocket.set_option(
  117. asio::ip::multicast::enable_loopback(addr.is_loopback()));
  118. socket.mpImpl->mSocket.set_option(asio::ip::multicast::outbound_interface(addr));
  119. socket.mpImpl->mSocket.bind(asio::ip::udp::endpoint{addr, 0});
  120. }
  121. } // namespace discovery
  122. } // namespace ableton