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
Profile Class Reference

#include <Profile.h>

Public Member Functions

 Profile (const CommandSet &command_set) noexcept
 
bool CommandHasAssociatedMessage (const std::string &command) const
 
void FromXml (const juce::XmlElement *root)
 
const std::string & GetCommandForMessage (rsj::MidiMessageId message) const
 
rsj::MidiMessageId GetMessageForNumber (size_t num) const
 
std::vector< rsj::MidiMessageIdGetMessagesForCommand (const std::string &command) const
 
int GetRowForMessage (rsj::MidiMessageId message) const
 
void InsertOrAssign (const std::string &command, rsj::MidiMessageId message)
 
void InsertOrAssign (size_t command, rsj::MidiMessageId message)
 
void InsertUnassigned (rsj::MidiMessageId message)
 
bool MessageExistsInMap (rsj::MidiMessageId message) const
 
bool ProfileUnsaved () const
 
void RemoveAllRows ()
 
void RemoveMessage (rsj::MidiMessageId message)
 
void RemoveRow (size_t row)
 
void RemoveUnassignedMessages ()
 
void Resort (std::pair< int, bool > new_order)
 
size_t Size () const
 
void ToXmlFile (const juce::File &file)
 

Private Types

using mm_abbrv_lmnt_t = std::pair< rsj::MidiMessageId, std::string >
 

Private Member Functions

void InsertOrAssignI (const std::string &command, const rsj::MidiMessageId &message)
 
bool MessageExistsInMapI (rsj::MidiMessageId message) const
 
void SortI ()
 

Private Attributes

const CommandSetcommand_set_
 
std::pair< int, bool > current_sort_ {2, true}
 
std::vector< mm_abbrv_lmnt_tmm_abbrv_table_ {}
 
std::shared_mutex mutex_
 
bool profile_unsaved_ {false}
 
std::vector< mm_abbrv_lmnt_tsaved_mm_abbrv_table_ {}
 
std::mutex saved_table_mtx_
 

Member Typedef Documentation

◆ mm_abbrv_lmnt_t

using Profile::mm_abbrv_lmnt_t = std::pair<rsj::MidiMessageId, std::string>
private

Constructor & Destructor Documentation

◆ Profile()

Profile::Profile ( const CommandSet command_set)
inlineexplicitnoexcept
38: command_set_ {command_set} {}
const CommandSet & command_set_
Definition Profile.h:67

Member Function Documentation

◆ CommandHasAssociatedMessage()

bool Profile::CommandHasAssociatedMessage ( const std::string &  command) const
inline
79{
80 auto guard {std::shared_lock {mutex_}};
81#ifdef __cpp_lib_ranges_contains
82 return std::ranges::contains(mm_abbrv_table_, command, &mm_abbrv_lmnt_t::second);
83#else
84 return std::ranges::any_of(mm_abbrv_table_,
85 [&command](const auto& p) { return p.second == command; });
86#endif
87}
std::vector< mm_abbrv_lmnt_t > mm_abbrv_table_
Definition Profile.h:74
std::shared_mutex mutex_
Definition Profile.h:72

◆ FromXml()

void Profile::FromXml ( const juce::XmlElement *  root)
36{
37 /* external use only, but will either use external versions of Profile calls to lock individual
38 * accesses or manually lock any internal calls instead of using mutex for entire method */
39 try {
40 using namespace std::string_literals;
41 if (!root || root->getTagName().compare("settings") != 0) { return; }
43 for (const auto* setting : root->getChildIterator()) {
44 auto command {setting->getStringAttribute("command_string").toStdString()};
45 if (const auto b {command.back()}; b == '2' || b == 'e') { // assumes only e,2 end old
46 // strings
47 for (const auto& i : replace_me) {
48 if (command == i.first) {
49 command = i.second;
50 break;
51 }
52 }
53 }
54 if (setting->hasAttribute("controller")) {
55 const rsj::MidiMessageId message {setting->getIntAttribute("channel"),
56 setting->getIntAttribute("controller"), rsj::MessageType::kCc};
57 InsertOrAssign(command, message);
58 }
59 else if (setting->hasAttribute("note")) {
60 const rsj::MidiMessageId note {setting->getIntAttribute("channel"),
61 setting->getIntAttribute("note"), rsj::MessageType::kNoteOn};
62 InsertOrAssign(command, note);
63 }
64 else if (setting->hasAttribute("pitchbend")) {
65 const rsj::MidiMessageId pb {
66 setting->getIntAttribute("channel"), 0, rsj::MessageType::kPw};
67 InsertOrAssign(command, pb);
68 }
69 else { /* no action needed */
70 }
71 }
72 auto guard {std::unique_lock {mutex_}};
73 SortI();
75 profile_unsaved_ = false;
76 }
77 catch (const std::exception& e) {
79 throw;
80 }
81}
void InsertOrAssign(const std::string &command, rsj::MidiMessageId message)
Definition Profile.h:110
bool profile_unsaved_
Definition Profile.h:66
void RemoveAllRows()
Definition Profile.cpp:137
std::vector< mm_abbrv_lmnt_t > saved_mm_abbrv_table_
Definition Profile.h:75
void SortI()
Definition Profile.cpp:215
void ExceptionResponse(gsl::czstring id, gsl::czstring fu, const std::exception &e) noexcept
Definition MidiUtilities.h:145

◆ GetCommandForMessage()

const std::string & Profile::GetCommandForMessage ( rsj::MidiMessageId  message) const
inline
90{
91 auto guard {std::shared_lock {mutex_}};
92 const auto found {std::ranges::find(mm_abbrv_table_, message, &mm_abbrv_lmnt_t::first)};
93 if (found != mm_abbrv_table_.end()) { return found->second; }
95}
static const std::string kUnassigned
Definition CommandSet.h:63

◆ GetMessageForNumber()

rsj::MidiMessageId Profile::GetMessageForNumber ( size_t  num) const
inline
98{
99 auto guard {std::shared_lock {mutex_}};
100 return mm_abbrv_table_.at(num).first;
101}

◆ GetMessagesForCommand()

std::vector< rsj::MidiMessageId > Profile::GetMessagesForCommand ( const std::string &  command) const
84{
85 try {
86 auto guard {std::shared_lock {mutex_}};
87 const auto filt {[&command](const auto& p) { return p.second == command; }};
88#ifdef __cpp_lib_ranges_to_container
89 auto mm {mm_abbrv_table_ | std::views::filter(filt)
90 | std::views::elements<0> | std::ranges::to<std::vector>()};
91#else
92 std::vector<rsj::MidiMessageId> mm;
93 std::ranges::copy(mm_abbrv_table_ | std::views::filter(filt) | std::views::elements<0>,
94 std::back_inserter(mm));
95#endif
96 return mm;
97 }
98 catch (const std::exception& e) {
100 throw;
101 }
102}

◆ GetRowForMessage()

int Profile::GetRowForMessage ( rsj::MidiMessageId  message) const
inline
104{
105 auto guard {std::shared_lock {mutex_}};
106 return gsl::narrow_cast<int>(std::ranges::find(mm_abbrv_table_, message, &mm_abbrv_lmnt_t::first)
107 - mm_abbrv_table_.begin());
108}

◆ InsertOrAssign() [1/2]

void Profile::InsertOrAssign ( const std::string &  command,
rsj::MidiMessageId  message 
)
inline
111{
112 auto guard {std::unique_lock {mutex_}};
113 InsertOrAssignI(command, message);
114}
void InsertOrAssignI(const std::string &command, const rsj::MidiMessageId &message)
Definition Profile.cpp:104

◆ InsertOrAssign() [2/2]

void Profile::InsertOrAssign ( size_t  command,
rsj::MidiMessageId  message 
)
inline
117{
118 if (command < command_set_.CommandAbbrevSize()) {
119 auto guard {std::unique_lock {mutex_}};
121 }
122}
auto CommandAbbrevSize() const noexcept
Definition CommandSet.h:43
const auto & CommandAbbrevAt(size_t index) const
Definition CommandSet.h:38

◆ InsertOrAssignI()

void Profile::InsertOrAssignI ( const std::string &  command,
const rsj::MidiMessageId message 
)
private
105{
106 try {
107 const auto found {std::ranges::find(mm_abbrv_table_, message, &mm_abbrv_lmnt_t::first)};
108 if (found != mm_abbrv_table_.end()) { found->second = command; }
109 else {
110 mm_abbrv_table_.emplace_back(message, command);
111 }
112 SortI();
113 profile_unsaved_ = true;
114 }
115 catch (const std::exception& e) {
117 throw;
118 }
119}

◆ InsertUnassigned()

void Profile::InsertUnassigned ( rsj::MidiMessageId  message)
122{
123 try {
124 auto guard {std::unique_lock {mutex_}};
125 if (!MessageExistsInMapI(message)) {
126 mm_abbrv_table_.emplace_back(message, CommandSet::kUnassigned);
127 SortI();
128 profile_unsaved_ = true;
129 }
130 }
131 catch (const std::exception& e) {
133 throw;
134 }
135}
bool MessageExistsInMapI(rsj::MidiMessageId message) const
Definition Profile.h:130

◆ MessageExistsInMap()

bool Profile::MessageExistsInMap ( rsj::MidiMessageId  message) const
inline
125{
126 auto guard {std::shared_lock {mutex_}};
127 return MessageExistsInMapI(message);
128}

◆ MessageExistsInMapI()

bool Profile::MessageExistsInMapI ( rsj::MidiMessageId  message) const
inlineprivate
131{
132#ifdef __cpp_lib_ranges_contains
133 return std::ranges::contains(mm_abbrv_table_, message, &mm_abbrv_lmnt_t::first);
134#else
135 return std::ranges::any_of(mm_abbrv_table_, [message](auto& p) { return p.first == message; });
136#endif
137}

◆ ProfileUnsaved()

bool Profile::ProfileUnsaved ( ) const
inline
140{ /* technically, should check std::is_permutation, but much quicker to check if saved==current,
141 accept occasional false positive for much better program responsiveness */
142 auto abbrv_lock {std::scoped_lock {saved_table_mtx_}};
143 auto guard {std::shared_lock {mutex_}};
145}
std::mutex saved_table_mtx_
Definition Profile.h:71

◆ RemoveAllRows()

void Profile::RemoveAllRows ( )
138{
139 try {
140 auto guard {std::unique_lock {mutex_}};
141 mm_abbrv_table_.clear();
142 /*avoid repeated allocations when building*/
143 mm_abbrv_table_.reserve(128);
144 /* no reason for profile_unsaved_ here. nothing to save */
145 profile_unsaved_ = false;
146 }
147 catch (const std::exception& e) {
149 throw;
150 }
151}

◆ RemoveMessage()

void Profile::RemoveMessage ( rsj::MidiMessageId  message)
154{
155 try {
156 auto guard {std::unique_lock {mutex_}};
157 const auto found {std::ranges::find(mm_abbrv_table_, message, &mm_abbrv_lmnt_t::first)};
158 if (found != mm_abbrv_table_.end()) [[likely]] {
159 mm_abbrv_table_.erase(found);
160 profile_unsaved_ = true;
161 }
162 else {
163 rsj::Log(fmt::format(FMT_STRING("Error in Profile::RemoveMessage. Message not found. "
164 "Message is: channel {} control number {} type {}."),
165 message.channel, message.control_number, message.msg_id_type));
166 }
167 }
168 catch (const std::exception& e) {
170 throw;
171 }
172}
void Log(const juce::String &info, const std::source_location &location=std::source_location::current()) noexcept
Definition Misc.cpp:131
MessageType msg_id_type
Definition MidiUtilities.h:148
int channel
Definition MidiUtilities.h:146
int control_number
Definition MidiUtilities.h:147

◆ RemoveRow()

void Profile::RemoveRow ( size_t  row)
175{
176 try {
177 auto guard {std::unique_lock {mutex_}};
178 mm_abbrv_table_.erase(mm_abbrv_table_.begin() + gsl::narrow_cast<std::ptrdiff_t>(row));
179 profile_unsaved_ = true;
180 }
181 catch (const std::exception& e) {
183 throw;
184 }
185}

◆ RemoveUnassignedMessages()

void Profile::RemoveUnassignedMessages ( )
188{
189 try {
190 auto guard {std::unique_lock {mutex_}};
191 if (std::erase_if(mm_abbrv_table_,
192 [](const auto& p) { return p.second == CommandSet::kUnassigned; })) {
193 profile_unsaved_ = true;
194 }
195 }
196 catch (const std::exception& e) {
198 throw;
199 }
200}

◆ Resort()

void Profile::Resort ( std::pair< int, bool >  new_order)
203{
204 try {
205 auto guard {std::unique_lock {mutex_}};
206 current_sort_ = new_order;
207 SortI();
208 }
209 catch (const std::exception& e) {
211 throw;
212 }
213}
std::pair< int, bool > current_sort_
Definition Profile.h:73

◆ Size()

size_t Profile::Size ( ) const
inline
148{
149 auto guard {std::shared_lock {mutex_}};
150 return mm_abbrv_table_.size();
151}

◆ SortI()

void Profile::SortI ( )
private
216{
217 try {
218 const auto projection {
219 [this](const auto& a) { return command_set_.CommandTextIndex(a.second); }};
220 if (current_sort_.first == 1) {
221 if (current_sort_.second) { std::ranges::sort(mm_abbrv_table_); }
222 else {
223 std::ranges::sort(mm_abbrv_table_ | std::views::reverse);
224 }
225 }
226 else if (current_sort_.second) {
227 std::ranges::stable_sort(mm_abbrv_table_, {}, projection);
228 }
229 else {
230 std::ranges::stable_sort(mm_abbrv_table_ | std::views::reverse, {}, projection);
231 }
232 }
233 catch (const std::exception& e) {
235 throw;
236 }
237}
size_t CommandTextIndex(const std::string &command) const
Definition CommandSet.cpp:101

◆ ToXmlFile()

void Profile::ToXmlFile ( const juce::File &  file)
240{
241 try { /* except for saved_mm_abbrv_table_ and profile_unsaved_, doesn't alter anything, so to
242 allow for better responsiveness in slow serialization and file write, use shared_lock +
243 mtx solely guarding the two members that are overwritten */
244 auto abbrv_lock {std::scoped_lock {saved_table_mtx_}};
245 auto guard {std::shared_lock {mutex_}};
246 /* don't bother if map is empty */
247 if (!mm_abbrv_table_.empty()) {
248 /* save the contents of the command map to an xml file */
249 juce::XmlElement root {"settings"};
250 for (const auto& [msg_id, cmd_str] : mm_abbrv_table_) {
251 auto setting {std::make_unique<juce::XmlElement>("setting")};
252 setting->setAttribute("channel", msg_id.channel);
253 switch (msg_id.msg_id_type) {
255 setting->setAttribute("note", msg_id.control_number);
256 break;
258 setting->setAttribute("controller", msg_id.control_number);
259 break;
261 setting->setAttribute("pitchbend", 0);
262 break;
268 /* can't handle other types */
269 continue;
270 }
271 setting->setAttribute("command_string", cmd_str);
272 root.prependChildElement(setting.release());
273 }
274 if (!root.writeTo(file)) {
275 /* Give feedback if file-save doesn't work */
276 const auto& p {file.getFullPathName()};
277 rsj::LogAndAlertError(juce::translate("Unable to save file. Choose a different "
278 "location and try again.")
279 + ' ' + p,
280 "Unable to save file. Choose a different location and try again. " + p);
281 }
282 else {
283 /*could use shared_mutex above if it were upgradable to unique here*/
285 profile_unsaved_ = false;
286 }
287 }
288 }
289 catch (const std::exception& e) {
291 throw;
292 }
293}
void LogAndAlertError(const juce::String &error_text, const std::source_location &location=std::source_location::current()) noexcept
Definition Misc.cpp:160

Member Data Documentation

◆ command_set_

const CommandSet& Profile::command_set_
private

◆ current_sort_

std::pair<int, bool> Profile::current_sort_ {2, true}
private
73{2, true};

◆ mm_abbrv_table_

std::vector<mm_abbrv_lmnt_t> Profile::mm_abbrv_table_ {}
private
74{};

◆ mutex_

std::shared_mutex Profile::mutex_
mutableprivate

◆ profile_unsaved_

bool Profile::profile_unsaved_ {false}
private
66{false};

◆ saved_mm_abbrv_table_

std::vector<mm_abbrv_lmnt_t> Profile::saved_mm_abbrv_table_ {}
private
75{};

◆ saved_table_mtx_

std::mutex Profile::saved_table_mtx_
mutableprivate

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