MIDI2LR 6.3.0.1
MIDI2LR is an application that interfaces MIDI controllers with Lightroom 6+/CC Classic. It processes MIDI input into develop parameter updates and photo actions, and sends MIDI output when parameters are changed for motorized feedback (on controllers that have motorized faders). A listing of available LightRoom commands is in the Wiki. Assistance on the code and design is welcome.
Loading...
Searching...
No Matches
LrIpcIn Class Reference

#include <LR_IPC_In.h>

Public Member Functions

 LrIpcIn (const LrIpcIn &other)=delete
 LrIpcIn (ControlsModel &c_model, ProfileManager &profile_manager, const Profile &profile, const MidiSender &midi_sender, asio::io_context &io_context)
 LrIpcIn (LrIpcIn &&other)=delete
 ~LrIpcIn ()=default
LrIpcInoperator= (const LrIpcIn &other)=delete
LrIpcInoperator= (LrIpcIn &&other)=delete
void Start ()
void Stop ()

Private Member Functions

void Connect (std::shared_ptr< LrIpcInShared > lr_ipc_shared)
void ProcessLine (std::shared_ptr< LrIpcInShared > lr_ipc_shared)

Private Attributes

ControlsModelcontrols_model_
std::shared_ptr< LrIpcInSharedlr_ipc_in_shared_
const MidiSendermidi_sender_
std::future< void > process_line_future_
const Profileprofile_
ProfileManagerprofile_manager_

Constructor & Destructor Documentation

◆ LrIpcIn() [1/3]

LrIpcIn::LrIpcIn ( ControlsModel & c_model,
ProfileManager & profile_manager,
const Profile & profile,
const MidiSender & midi_sender,
asio::io_context & io_context )
71 : midi_sender_ {midi_sender}, profile_ {profile}, controls_model_ {c_model},
72 profile_manager_ {profile_manager},
73 lr_ipc_in_shared_ {std::make_shared<LrIpcInShared>(io_context)}
74{
75}
std::shared_ptr< LrIpcInShared > lr_ipc_in_shared_
Definition LR_IPC_In.h:51
ControlsModel & controls_model_
Definition LR_IPC_In.h:48
const Profile & profile_
Definition LR_IPC_In.h:47
ProfileManager & profile_manager_
Definition LR_IPC_In.h:49
const MidiSender & midi_sender_
Definition LR_IPC_In.h:46

References midi_sender_.

◆ ~LrIpcIn()

LrIpcIn::~LrIpcIn ( )
default

◆ LrIpcIn() [2/3]

LrIpcIn::LrIpcIn ( const LrIpcIn & other)
delete

◆ LrIpcIn() [3/3]

LrIpcIn::LrIpcIn ( LrIpcIn && other)
delete

Member Function Documentation

◆ Connect()

void LrIpcIn::Connect ( std::shared_ptr< LrIpcInShared > lr_ipc_shared)
private
139{
140 try {
141 lr_ipc_shared->socket_.async_connect(asio::ip::tcp::endpoint(asio::ip::address_v4::loopback(),
142 kLrInPort),
143 [lr_ipc_shared](const asio::error_code& error) mutable {
144 // capture a copy of the alive token so handler can detect owner shutdown
145 const auto alive {lr_ipc_shared->owner_alive_};
146 if (!alive || !alive->load(std::memory_order_acquire)) { return; }
147
148 if (!error) {
149 rsj::Log("Socket connected in LR_IPC_In.", std::source_location::current());
150 // only proceed if owner still alive
151 if (alive->load(std::memory_order_acquire)) {
152 LrIpcInShared::Read(std::move(lr_ipc_shared));
153 }
154 }
155 else {
156 rsj::Log(fmt::format("LR_IPC_In did not connect. {}.", error.message()),
157 std::source_location::current());
158 asio::error_code ec2;
159 std::ignore = lr_ipc_shared->socket_.close(ec2);
160 if (ec2) {
161 rsj::Log(fmt::format("LR_IPC_In socket close error {}.", ec2.message()),
162 std::source_location::current());
163 }
164 }
165 });
166 }
167 catch (const std::exception& e) {
168 rsj::ExceptionResponse(e, std::source_location::current());
169 throw;
170 }
171}
asio::ip::tcp::socket socket_
Definition LR_IPC_In.cpp:55
std::shared_ptr< std::atomic< bool > > owner_alive_
Definition LR_IPC_In.cpp:62
static void Read(std::shared_ptr< LrIpcInShared > lr_ipc_shared)
Definition LR_IPC_In.cpp:289
void ExceptionResponse(gsl::czstring id, gsl::czstring fu, const std::exception &e) noexcept
void Log(const juce::String &info, const std::source_location &location=std::source_location::current()) noexcept
Definition Misc.cpp:112

Referenced by Start().

◆ operator=() [1/2]

LrIpcIn & LrIpcIn::operator= ( const LrIpcIn & other)
delete

◆ operator=() [2/2]

LrIpcIn & LrIpcIn::operator= ( LrIpcIn && other)
delete

◆ ProcessLine()

void LrIpcIn::ProcessLine ( std::shared_ptr< LrIpcInShared > lr_ipc_shared)
private
200{
201 try {
202 decltype(lr_ipc_shared->line_)::value_type line_copy;
203 while ((line_copy = lr_ipc_shared->line_.pop()) != kTerminate) {
204#pragma warning(suppress : 26445) /* copying views; otherwise dangling references */
205 auto [command_view, value_view] {SplitLine(line_copy)};
206
207 /* Fast path for known commands */
208 if (command_view == "TerminateApplication") [[unlikely]] {
209 if (auto* app = juce::JUCEApplication::getInstance()) { app->systemRequestedQuit(); }
210 return;
211 }
212
213 if (value_view.empty()) [[unlikely]] {
214 rsj::Log(fmt::format("No value attached to message. Message from plugin was \"{}\".",
215 rsj::ReplaceInvisibleChars(line_copy)),
216 std::source_location::current());
217 continue;
218 }
219
220 if (command_view == "SwitchProfile") {
221 profile_manager_.SwitchToProfile(std::string(value_view));
222 continue;
223 }
224
225 if (command_view == "Log") {
226 rsj::Log(fmt::format("Plugin: {}.", value_view), std::source_location::current());
227 continue;
228 }
229
230 if (command_view == "SendKey") {
231 int modifiers {0};
232 auto [ptr, ec] {std::from_chars(value_view.data(),
233 value_view.data() + value_view.size(), modifiers)};
234 /* Find the first non-digit after parsing modifiers */
235 std::string_view key_view {value_view};
236 key_view.remove_prefix(static_cast<size_t>(ptr - value_view.data()));
237 if (!key_view.empty() && (key_view.front() == ' ' || key_view.front() == '\t')) {
238 key_view.remove_prefix(1);
239 }
240 if (!key_view.empty() && ec == std::errc()) {
241 rsj::SendKeyDownUp(std::string(key_view),
243 continue;
244 }
245 rsj::LogAndAlertError(fmt::format("SendKey couldn't identify keystroke. Message from "
246 "plugin was \"{}\".",
247 rsj::ReplaceInvisibleChars(line_copy)),
248 std::source_location::current());
249 continue;
250 }
251
252 /* Default: send associated messages to MIDI OUT devices */
253 double original_value {0.0};
254#if defined(_MSC_VER) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_26_0)
255 auto [ptr, ec] {std::from_chars(value_view.data(), value_view.data() + value_view.size(),
256 original_value)};
257 if (ec != std::errc()) {
258 rsj::LogAndAlertError(fmt::format("Failed to parse double from \"{}\".", value_view),
259 std::source_location::current());
260 continue;
261 }
262#else
263 try {
264 original_value = std::stod(std::string(value_view));
265 }
266 catch (const std::exception& ex) {
267 rsj::LogAndAlertError(fmt::format("Failed to parse double from \"{}\": {}", value_view,
268 ex.what()),
269 std::source_location::current());
270 continue;
271 }
272#endif
273 const auto messages {profile_.GetMessagesForCommand(std::string(command_view))};
274 for (const auto& msg : messages) {
275 const auto value {controls_model_.PluginToController(msg, original_value)};
276 if (msg.msg_id_type != rsj::MessageType::kCc
277 || controls_model_.GetCcMethod(msg) == rsj::CCmethod::kAbsolute) {
278 midi_sender_.Send(msg, value);
279 }
280 }
281 }
282 }
283 catch (const std::exception& e) {
284 rsj::ExceptionResponse(e, std::source_location::current());
285 throw;
286 }
287}
rsj::ConcurrentQueue< std::string > line_
Definition LR_IPC_In.cpp:57
T pop()
Definition Concurrency.h:190
std::string ReplaceInvisibleChars(std::string_view in)
@ kAbsolute
Definition ControlsModel.h:33
@ kCc
Definition MidiUtilities.h:45
void SendKeyDownUp(const std::string &key, rsj::ActiveModifiers mods) noexcept
Definition SendKeysMac.cpp:378
void LogAndAlertError(const juce::String &error_text, const std::source_location &location=std::source_location::current()) noexcept
Definition Misc.cpp:141
static ActiveModifiers FromMidi2LR(int from) noexcept
Definition SendKeys.cpp:30

References profile_, profile_manager_, and ProfileManager::SwitchToProfile().

Referenced by Start().

◆ Start()

void LrIpcIn::Start ( )
78{
79 try {
81 std::async(std::launch::async, [this, shared = lr_ipc_in_shared_]() mutable {
82 rsj::LabelThread(MIDI2LR_UC_LITERAL("LrIpcIn ProcessLine thread"));
83 MIDI2LR_FAST_FLOATS;
84 ProcessLine(std::move(shared));
85 });
87 }
88 catch (const std::exception& e) {
89 rsj::ExceptionResponse(e, std::source_location::current());
90 throw;
91 }
92}
std::future< void > process_line_future_
Definition LR_IPC_In.h:50
void ProcessLine(std::shared_ptr< LrIpcInShared > lr_ipc_shared)
Definition LR_IPC_In.cpp:199
void Connect(std::shared_ptr< LrIpcInShared > lr_ipc_shared)
Definition LR_IPC_In.cpp:138
void LabelThread(gsl::czstring threadname)
Definition Misc.cpp:48

References Connect(), lr_ipc_in_shared_, process_line_future_, and ProcessLine().

◆ Stop()

void LrIpcIn::Stop ( )
95{
96 try {
97 // prevent async handlers from using owner state
99 lr_ipc_in_shared_->thread_should_exit_.store(true, std::memory_order_release);
100 lr_ipc_in_shared_->owner_alive_->store(false, std::memory_order_release);
101
102 if (auto& sock {lr_ipc_in_shared_->socket_}; sock.is_open()) {
103 asio::error_code ec;
104 /* For portable behaviour with respect to graceful closure of a connected socket, call
105 * shutdown() before closing the socket. */
106 try { /* ignore exceptions from shutdown, always close */
107 std::ignore = sock.shutdown(asio::ip::tcp::socket::shutdown_both, ec);
108 if (ec) {
109 rsj::Log(fmt::format("LR_IPC_In socket shutdown error {}.", ec.message()),
110 std::source_location::current());
111 ec.clear();
112 }
113 }
114 catch (const std::exception& e) {
115 rsj::Log(fmt::format("Exception during socket shutdown: {}", e.what()),
116 std::source_location::current());
117 }
118 std::ignore = sock.close(ec);
119 if (ec) {
120 rsj::Log(fmt::format("LR_IPC_In socket close error {}.", ec.message()),
121 std::source_location::current());
122 }
123 }
124 /* clear input queue after port closed */
125 if (const auto m {lr_ipc_in_shared_->line_.clear_count_emplace(kTerminate)}) {
126 rsj::Log(fmt::format("{} left in queue in LrIpcIn destructor.", m),
127 std::source_location::current());
128 }
129 }
130 }
131
132 catch (const std::exception& e) {
133 rsj::ExceptionResponse(e, std::source_location::current());
134 throw;
135 }
136}

References lr_ipc_in_shared_.

Member Data Documentation

◆ controls_model_

ControlsModel& LrIpcIn::controls_model_
private

◆ lr_ipc_in_shared_

std::shared_ptr<LrIpcInShared> LrIpcIn::lr_ipc_in_shared_
private

Referenced by Start(), and Stop().

◆ midi_sender_

const MidiSender& LrIpcIn::midi_sender_
private

Referenced by LrIpcIn().

◆ process_line_future_

std::future<void> LrIpcIn::process_line_future_
private

Referenced by Start().

◆ profile_

const Profile& LrIpcIn::profile_
private

Referenced by ProcessLine().

◆ profile_manager_

ProfileManager& LrIpcIn::profile_manager_
private

Referenced by ProcessLine().


The documentation for this class was generated from the following files:
  • C:/Users/rsjaf/source/repos/MIDI2LR/src/application/LR_IPC_In.h
  • C:/Users/rsjaf/source/repos/MIDI2LR/src/application/LR_IPC_In.cpp