MIDI2LR 6.1.0.0
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 
)
67 : midi_sender_ {midi_sender}, profile_ {profile}, controls_model_ {c_model},
68 profile_manager_ {profile_manager},
69 lr_ipc_in_shared_ {std::make_shared<LrIpcInShared>(io_context)}
70{
71}
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
127{
128 try {
129 lr_ipc_shared->socket_.async_connect(asio::ip::tcp::endpoint(asio::ip::address_v4::loopback(),
130 kLrInPort),
131 [lr_ipc_shared](const asio::error_code& error) mutable {
132 if (!error) {
133 rsj::Log("Socket connected in LR_IPC_In.");
134 LrIpcInShared::Read(std::move(lr_ipc_shared));
135 }
136 else {
137 rsj::Log(fmt::format(FMT_STRING("LR_IPC_In did not connect. {}."), error.message()));
138 asio::error_code ec2;
139 std::ignore = lr_ipc_shared->socket_.close(ec2);
140 if (ec2) {
141 rsj::Log(fmt::format(FMT_STRING("LR_IPC_In socket close error {}."), ec2.message()));
142 }
143 }
144 });
145 }
146 catch (const std::exception& e) {
148 throw;
149 }
150}
static void Read(std::shared_ptr< LrIpcInShared > lr_ipc_shared)
Definition LR_IPC_In.cpp:229
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:131

◆ 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
166{
167 try {
168 for (auto line_copy = lr_ipc_shared->line_.pop(); line_copy != kTerminate;
169 line_copy = lr_ipc_shared->line_.pop()) {
170 auto [command_view, value_view] {SplitLine(line_copy)};
171 if (command_view == "TerminateApplication") {
172 juce::JUCEApplication::getInstance()->systemRequestedQuit();
173 return;
174 }
175 if (value_view.empty()) {
176 rsj::Log(fmt::format(FMT_STRING("No value attached to message. Message from plugin was "
177 "\"{}\"."),
178 rsj::ReplaceInvisibleChars(line_copy)));
179 }
180 else if (command_view == "SwitchProfile") {
181 profile_manager_.SwitchToProfile(std::string(value_view));
182 }
183 else if (command_view == "Log") {
184 rsj::Log(fmt::format(FMT_STRING("Plugin: {}."), value_view));
185 }
186 else if (command_view == "SendKey") {
187 int modifiers = 0;
188 std::from_chars(value_view.data(), value_view.data() + value_view.size(), modifiers);
189 /* trim twice on purpose: first modifiers digits, then one space (fixed delimiter) */
190 const auto first_not_digit {value_view.find_first_not_of("0123456789")};
191 if (first_not_digit != std::string_view::npos) {
192 value_view.remove_prefix(first_not_digit + 1);
193 if (!value_view.empty()) {
194 rsj::SendKeyDownUp(std::string(value_view),
196 continue; /* skip log and alert error */
197 }
198 }
199 rsj::LogAndAlertError(fmt::format(FMT_STRING("SendKey couldn't identify keystroke. "
200 "Message from plugin was \"{}\"."),
201 rsj::ReplaceInvisibleChars(line_copy)));
202 }
203 else { /* send associated messages to MIDI OUT devices */
204#ifdef _MSC_VER
205 double original_value = 0.0;
206 std::from_chars(value_view.data(), value_view.data() + value_view.size(),
207 original_value);
208#else //Xcode doesn't support double from_chars yet (15.2)
209 const auto original_value {std::stod(std::string(value_view))};
210#endif
211 const auto messages {profile_.GetMessagesForCommand(std::string(command_view))};
212 for (const auto& msg : messages) {
213 /* following needs to run for all controls: sets saved value */
214 const auto value {controls_model_.PluginToController(msg, original_value)};
215 if (msg.msg_id_type != rsj::MessageType::kCc
217 midi_sender_.Send(msg, value);
218 }
219 }
220 }
221 }
222 }
223 catch (const std::exception& e) {
225 throw;
226 }
227}
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:52
std::vector< rsj::MidiMessageId > GetMessagesForCommand(const std::string &command) const
Definition Profile.cpp:83
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:160
static ActiveModifiers FromMidi2LR(int from) noexcept
Definition SendKeys.cpp:30

◆ Start()

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

◆ Stop()

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

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: