MIDI2LR 6.2.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 
)
66 : midi_sender_ {midi_sender}, profile_ {profile}, controls_model_ {c_model},
67 profile_manager_ {profile_manager},
68 lr_ipc_in_shared_ {std::make_shared<LrIpcInShared>(io_context)}
69{
70}
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

◆ ~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
130{
131 try {
132 lr_ipc_shared->socket_.async_connect(asio::ip::tcp::endpoint(asio::ip::address_v4::loopback(),
133 kLrInPort),
134 [lr_ipc_shared](const asio::error_code& error) mutable {
135 if (!error) {
136 rsj::Log("Socket connected in LR_IPC_In.", std::source_location::current());
137 LrIpcInShared::Read(std::move(lr_ipc_shared));
138 }
139 else {
140 rsj::Log(fmt::format(FMT_STRING("LR_IPC_In did not connect. {}."), error.message()),
141 std::source_location::current());
142 asio::error_code ec2;
143 std::ignore = lr_ipc_shared->socket_.close(ec2);
144 if (ec2) {
145 rsj::Log(fmt::format(FMT_STRING("LR_IPC_In socket close error {}."), ec2.message()),
146 std::source_location::current());
147 }
148 }
149 });
150 }
151 catch (const std::exception& e) {
152 rsj::ExceptionResponse(e, std::source_location::current());
153 throw;
154 }
155}
static void Read(std::shared_ptr< LrIpcInShared > lr_ipc_shared)
Definition LR_IPC_In.cpp:276
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:113

◆ 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
184{
185 try {
186 decltype(lr_ipc_shared->line_)::value_type line_copy;
187 while ((line_copy = lr_ipc_shared->line_.pop()) != kTerminate) {
188#pragma warning(suppress : 26445) /* copying views; otherwise dangling references */
189 auto [command_view, value_view] = SplitLine(line_copy);
190
191 /* Fast path for known commands */
192 if (command_view == "TerminateApplication") {
193 juce::JUCEApplication::getInstance()->systemRequestedQuit();
194 return;
195 }
196
197 if (value_view.empty()) {
198 rsj::Log(fmt::format(FMT_STRING("No value attached to message. Message from plugin was "
199 "\"{}\"."),
200 rsj::ReplaceInvisibleChars(line_copy)),
201 std::source_location::current());
202 continue;
203 }
204
205 if (command_view == "SwitchProfile") {
206 profile_manager_.SwitchToProfile(std::string(value_view));
207 continue;
208 }
209
210 if (command_view == "Log") {
211 rsj::Log(fmt::format(FMT_STRING("Plugin: {}."), value_view),
212 std::source_location::current());
213 continue;
214 }
215
216 if (command_view == "SendKey") {
217 int modifiers {0};
218 auto [ptr, ec] {std::from_chars(value_view.data(),
219 value_view.data() + value_view.size(), modifiers)};
220 /* Find the first non-digit after parsing modifiers */
221 std::string_view key_view {value_view};
222 key_view.remove_prefix(static_cast<size_t>(ptr - value_view.data()));
223 if (!key_view.empty() && (key_view.front() == ' ' || key_view.front() == '\t')) {
224 key_view.remove_prefix(1);
225 }
226 if (!key_view.empty() && ec == std::errc()) {
227 rsj::SendKeyDownUp(std::string(key_view),
229 continue;
230 }
231 rsj::LogAndAlertError(fmt::format(FMT_STRING("SendKey couldn't identify keystroke. "
232 "Message from plugin was \"{}\"."),
233 rsj::ReplaceInvisibleChars(line_copy)),
234 std::source_location::current());
235 continue;
236 }
237
238 /* Default: send associated messages to MIDI OUT devices */
239 double original_value {0.0};
240#if defined(_MSC_VER) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_13_3)
241 auto [ptr, ec] {std::from_chars(value_view.data(), value_view.data() + value_view.size(),
242 original_value)};
243 if (ec != std::errc()) {
244 rsj::LogAndAlertError(fmt::format(FMT_STRING("Failed to parse double from \"{}\"."),
245 value_view),
246 std::source_location::current());
247 continue;
248 }
249#else
250 try {
251 original_value = std::stod(std::string(value_view));
252 }
253 catch (const std::exception& ex) {
254 rsj::LogAndAlertError(fmt::format(FMT_STRING("Failed to parse double from \"{}\": {}"),
255 value_view, ex.what()),
256 std::source_location::current());
257 continue;
258 }
259#endif
260 const auto messages {profile_.GetMessagesForCommand(std::string(command_view))};
261 for (const auto& msg : messages) {
262 const auto value {controls_model_.PluginToController(msg, original_value)};
263 if (msg.msg_id_type != rsj::MessageType::kCc
265 midi_sender_.Send(msg, value);
266 }
267 }
268 }
269 }
270 catch (const std::exception& e) {
271 rsj::ExceptionResponse(e, std::source_location::current());
272 throw;
273 }
274}
rsj::CCmethod GetCcMethod(int channel, int controlnumber) const
Definition ControlsModel.h:244
int PluginToController(rsj::MidiMessageId msg_id, double value)
Definition ControlsModel.h:270
void Send(rsj::MidiMessageId id, int value) const
Definition MIDISender.cpp:53
std::vector< rsj::MidiMessageId > GetMessagesForCommand(const std::string &command) const
Definition Profile.cpp:78
void SwitchToProfile(int profile_index)
Definition ProfileManager.cpp:57
std::string ReplaceInvisibleChars(std::string_view in)
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:142
static ActiveModifiers FromMidi2LR(int from) noexcept
Definition SendKeys.cpp:30

◆ Start()

void LrIpcIn::Start ( )
73{
74 try {
76 std::async(std::launch::async, [this, shared = lr_ipc_in_shared_]() mutable {
77 rsj::LabelThread(MIDI2LR_UC_LITERAL("LrIpcIn ProcessLine thread"));
78 MIDI2LR_FAST_FLOATS;
79 ProcessLine(std::move(shared));
80 });
82 }
83 catch (const std::exception& e) {
84 rsj::ExceptionResponse(e, std::source_location::current());
85 throw;
86 }
87}
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:183
void Connect(std::shared_ptr< LrIpcInShared > lr_ipc_shared)
Definition LR_IPC_In.cpp:129
void LabelThread(gsl::czstring threadname)
Definition Misc.cpp:48

◆ Stop()

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

Member Data Documentation

◆ controls_model_

ControlsModel& LrIpcIn::controls_model_
private

◆ lr_ipc_in_shared_

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

◆ midi_sender_

const MidiSender& LrIpcIn::midi_sender_
private

◆ process_line_future_

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

◆ profile_

const Profile& LrIpcIn::profile_
private

◆ profile_manager_

ProfileManager& LrIpcIn::profile_manager_
private

The documentation for this class was generated from the following files: