hydrogen 1.2.6
MidiMap.cpp
Go to the documentation of this file.
1/*
2 * Hydrogen
3 * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net]
4 * Copyright(c) 2008-2025 The hydrogen development team [hydrogen-devel@lists.sourceforge.net]
5 *
6 * http://www.hydrogen-music.org
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY, without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see https://www.gnu.org/licenses
20 *
21 */
22
23#include <core/MidiAction.h>
24#include "MidiMap.h"
25#include <map>
26#include <QMutexLocker>
27
43
45
47{
48 __instance = this;
49 QMutexLocker mx(&__mutex);
50
51 // Constructor
52 m_pcActionVector.resize( 1 );
53 m_pcActionVector[ 0 ] = std::make_shared<Action>(
55}
56
58{
59 QMutexLocker mx(&__mutex);
60
61 __instance = nullptr;
62}
63
65{
66 if( __instance == nullptr ) {
67 __instance = new MidiMap;
68 }
69}
70
72{
74 __instance->reset();
75}
76
77
83{
84 QMutexLocker mx(&__mutex);
85
86 m_mmcActionMap.clear();
87 m_noteActionMap.clear();
88 m_ccActionMap.clear();
89
90 m_pcActionVector.clear();
91 m_pcActionVector.resize( 1 );
92 m_pcActionVector[ 0 ] = std::make_shared<Action>(
94}
95
96void MidiMap::registerMMCEvent( QString sEventString, std::shared_ptr<Action> pAction )
97{
98 QMutexLocker mx(&__mutex);
99
100 if ( pAction == nullptr || pAction->isNull() ) {
101 ERRORLOG( "Invalid action" );
102 return;
103 }
104
105 const auto event = H2Core::MidiMessage::QStringToEvent( sEventString );
106 if ( event == H2Core::MidiMessage::Event::Null ||
110 ERRORLOG( QString( "Provided event string [%1] is no supported MMC event" )
111 .arg( sEventString ) );
112 return;
113 }
114
115 for ( const auto& [ssType, ppAction] : m_mmcActionMap ) {
116 if ( ppAction != nullptr && ssType == sEventString &&
117 ppAction->isEquivalentTo( pAction ) ) {
118 WARNINGLOG( QString( "MMC event [%1] for Action [%2: Param1: [%3], Param2: [%4], Param3: [%5]] was already registered" )
119 .arg( sEventString ).arg( pAction->getType() )
120 .arg( pAction->getParameter1() )
121 .arg( pAction->getParameter2() )
122 .arg( pAction->getParameter3() ) );
123 return;
124 }
125 }
126
127 m_mmcActionMap.insert( { sEventString, pAction } );
128}
129
130void MidiMap::registerNoteEvent( int nNote, std::shared_ptr<Action> pAction )
131{
132 QMutexLocker mx(&__mutex);
133
134 if ( pAction == nullptr || pAction->isNull() ) {
135 ERRORLOG( "Invalid action" );
136 return;
137 }
138
139 if ( nNote < MIDI_OUT_NOTE_MIN || nNote > MIDI_OUT_NOTE_MAX ) {
140 ERRORLOG( QString( "Unable to register Note MIDI [%1]: Provided note [%2] out of bound [%3,%4]" )
141 .arg( pAction->toQString() ).arg( nNote )
142 .arg( MIDI_OUT_NOTE_MIN ).arg( MIDI_OUT_NOTE_MAX ) );
143 return;
144 }
145
146 for ( const auto& [nnPitch, ppAction] : m_noteActionMap ) {
147 if ( ppAction != nullptr && nnPitch == nNote &&
148 ppAction->isEquivalentTo( pAction ) ) {
149 WARNINGLOG( QString( "NOTE event [%1] for Action [%2: Param1: [%3], Param2: [%4], Param3: [%5]] was already registered" )
150 .arg( nNote ).arg( pAction->getType() )
151 .arg( pAction->getParameter1() )
152 .arg( pAction->getParameter2() )
153 .arg( pAction->getParameter3() ) );
154 return;
155 }
156 }
157
158 m_noteActionMap.insert( { nNote, pAction } );
159}
160
161void MidiMap::registerCCEvent( int nParameter, std::shared_ptr<Action> pAction ){
162 QMutexLocker mx(&__mutex);
163
164 if ( pAction == nullptr || pAction->isNull() ) {
165 ERRORLOG( "Invalid action" );
166 return;
167 }
168
169 if ( nParameter < 0 || nParameter > 127 ) {
170 ERRORLOG( QString( "Unable to register CC MIDI [%1]: Provided parameter [%2] out of bound [0,127]" )
171 .arg( pAction->toQString() ).arg( nParameter ) );
172 return;
173 }
174
175 for ( const auto& [nnParam, ppAction] : m_ccActionMap ) {
176 if ( ppAction != nullptr && nnParam == nParameter &&
177 ppAction->isEquivalentTo( pAction ) ) {
178 WARNINGLOG( QString( "CC event [%1] for Action [%2: Param1: [%3], Param2: [%4], Param3: [%5]] was already registered" )
179 .arg( nParameter ).arg( pAction->getType() )
180 .arg( pAction->getParameter1() )
181 .arg( pAction->getParameter2() )
182 .arg( pAction->getParameter3() ) );
183 return;
184 }
185 }
186
187 m_ccActionMap.insert( { nParameter, pAction } );
188}
189
190void MidiMap::registerPCEvent( std::shared_ptr<Action> pAction ){
191 QMutexLocker mx(&__mutex);
192
193 if ( pAction == nullptr || pAction->isNull() ) {
194 ERRORLOG( "Invalid action" );
195 return;
196 }
197
198 for ( const auto& ppAction : m_pcActionVector ) {
199 if ( ppAction != nullptr && ppAction->isEquivalentTo( pAction ) ) {
200 WARNINGLOG( QString( "PC event for Action [%2: Param1: [%3], Param2: [%4], Param3: [%5]] was already registered" )
201 .arg( pAction->getType() )
202 .arg( pAction->getParameter1() )
203 .arg( pAction->getParameter2() )
204 .arg( pAction->getParameter3() ) );
205 return;
206 }
207 }
208
209 m_pcActionVector.push_back( pAction );
210}
211
212std::vector<std::shared_ptr<Action>> MidiMap::getMMCActions( QString sEventString )
213{
214 QMutexLocker mx(&__mutex);
215
216 std::vector<std::shared_ptr<Action>> actions;
217
218 auto range = m_mmcActionMap.equal_range( sEventString );
219
220 for ( auto ii = range.first; ii != range.second; ++ii ) {
221 if ( ii->second != nullptr ) {
222 actions.push_back( ii->second );
223 }
224 }
225
226 return std::move( actions );
227}
228
229std::vector<std::shared_ptr<Action>> MidiMap::getNoteActions( int nNote )
230{
231 QMutexLocker mx(&__mutex);
232
233 std::vector<std::shared_ptr<Action>> actions;
234
235 auto range = m_noteActionMap.equal_range( nNote );
236
237 for ( auto ii = range.first; ii != range.second; ++ii ) {
238 if ( ii->second != nullptr ) {
239 actions.push_back( ii->second );
240 }
241 }
242
243 return std::move( actions );
244}
245
246std::vector<std::shared_ptr<Action>> MidiMap::getCCActions( int nParameter ) {
247 QMutexLocker mx(&__mutex);
248
249 std::vector<std::shared_ptr<Action>> actions;
250
251 auto range = m_ccActionMap.equal_range( nParameter );
252
253 for ( auto ii = range.first; ii != range.second; ++ii ) {
254 if ( ii->second != nullptr ) {
255 actions.push_back( ii->second );
256 }
257 }
258
259 return std::move( actions );
260}
261
262std::vector<int> MidiMap::findCCValuesByActionParam1( QString sActionType, QString sParam1 ) {
263 QMutexLocker mx(&__mutex);
264 std::vector<int> values;
265
266 for ( const auto& [nnParam, ppAction] : m_ccActionMap ) {
267 if ( ppAction != nullptr && ppAction->getType() == sActionType &&
268 ppAction->getParameter1() == sParam1 ){
269 values.push_back( nnParam );
270 }
271 }
272
273 return std::move( values );
274}
275
276std::vector<int> MidiMap::findCCValuesByActionType( QString sActionType ) {
277 QMutexLocker mx(&__mutex);
278 std::vector<int> values;
279
280 for ( const auto& [nnParam, ppAction] : m_ccActionMap ) {
281 if ( ppAction != nullptr && ppAction->getType() == sActionType ){
282 values.push_back( nnParam );
283 }
284 }
285
286 return std::move( values );
287}
288
289std::vector<std::pair<H2Core::MidiMessage::Event,int>> MidiMap::getRegisteredMidiEvents( std::shared_ptr<Action> pAction ) const {
290 std::vector<std::pair<H2Core::MidiMessage::Event,int>> midiEvents;
291
292 if ( pAction != nullptr && ! pAction->isNull() ) {
293 for ( const auto& [nnParam, ppAction] : m_noteActionMap ) {
294 if ( ppAction != nullptr &&
295 ppAction->isEquivalentTo( pAction ) ) {
296 midiEvents.push_back( std::make_pair(
298 }
299 }
300 for ( const auto& [nnParam, ppAction] : m_ccActionMap ) {
301 if ( ppAction != nullptr &&
302 ppAction->isEquivalentTo( pAction ) ) {
303 midiEvents.push_back( std::make_pair(
305 }
306 }
307 for ( const auto& [ssType, ppAction] : m_mmcActionMap ) {
308 if ( ppAction != nullptr &&
309 ppAction->isEquivalentTo( pAction ) ) {
310 const auto event = H2Core::MidiMessage::QStringToEvent( ssType );
311 if ( event == H2Core::MidiMessage::Event::Null ||
315 ERRORLOG( QString( "Unexpected event type [%1] found in mmcActionMap" )
316 .arg( ssType ) );
317 continue;
318 }
319 midiEvents.push_back( std::make_pair( event, 0 ) );
320 }
321 }
322 for ( const auto& ppAction : m_pcActionVector ) {
323 if ( ppAction != nullptr &&
324 ppAction->isEquivalentTo( pAction ) ) {
325 midiEvents.push_back( std::make_pair(
327 }
328 }
329 }
330
331 return std::move( midiEvents );
332}
333
334QString MidiMap::toQString( const QString& sPrefix, bool bShort ) const {
335 QString s = Base::sPrintIndention;
336 QString sOutput;
337 if ( ! bShort ) {
338 sOutput = QString( "%1[MidiMap]\n" ).arg( sPrefix )
339 .append( QString( "%1%2m_noteActionMap:\n" ).arg( sPrefix ).arg( s ) );
340 for ( const auto& [nParam, ppAction] : m_noteActionMap ) {
341 if ( ppAction != nullptr && ! ppAction->isNull() ) {
342 sOutput.append( QString( "%1%2%2%3: %4\n" ).arg( sPrefix ).arg( s )
343 .arg( nParam )
344 .arg( ppAction->toQString( "", true ) ) );
345 }
346 }
347 sOutput.append( QString( "%1%2m_ccActionMap:\n" ).arg( sPrefix ).arg( s ) );
348 for ( const auto& [nParam, ppAction] : m_ccActionMap ) {
349 if ( ppAction != nullptr && ! ppAction->isNull() ) {
350 sOutput.append( QString( "%1%2%2%3: %4\n" ).arg( sPrefix ).arg( s )
351 .arg( nParam )
352 .arg( ppAction->toQString( "", true ) ) );
353 }
354 }
355 sOutput.append( QString( "%1%2m_mmcActionMap:\n" ).arg( sPrefix ).arg( s ) );
356 for ( const auto& [nParam, ppAction] : m_mmcActionMap ) {
357 if ( ppAction != nullptr && ! ppAction->isNull() ) {
358 sOutput.append( QString( "%1%2%2%3: %4\n" ).arg( sPrefix ).arg( s )
359 .arg( nParam )
360 .arg( ppAction->toQString( "", true ) ) );
361 }
362 }
363 sOutput.append( QString( "%1%2m_pcActionVector:\n" ).arg( sPrefix ).arg( s ) );
364 for ( const auto& ppAction : m_pcActionVector ) {
365 if ( ppAction != nullptr && ! ppAction->isNull() ) {
366 sOutput.append( QString( "%1%2%2%3\n" ).arg( sPrefix ).arg( s )
367 .arg( ppAction->toQString( "", true ) ) );
368 }
369 }
370 }
371 else {
372
373 sOutput = QString( "[MidiMap] m_noteActionMap: [" );
374 for ( const auto& [nParam, ppAction] : m_noteActionMap ) {
375 if ( ppAction != nullptr && ! ppAction->isNull() ) {
376 sOutput.append( QString( "%1: %2, " ).arg( nParam )
377 .arg( ppAction->toQString( "", true ) ) );
378 }
379 }
380 sOutput.append( QString( "], m_ccActionMap: [" ) );
381 for ( const auto& [nParam, ppAction] : m_ccActionMap ) {
382 if ( ppAction != nullptr && ! ppAction->isNull() ) {
383 sOutput.append( QString( "%1: %2, " ).arg( nParam )
384 .arg( ppAction->toQString( "", true ) ) );
385 }
386 }
387 sOutput.append( QString( "], m_mmcActionMap: [" ) );
388 for ( const auto& [nParam, ppAction] : m_mmcActionMap ) {
389 if ( ppAction != nullptr && ! ppAction->isNull() ) {
390 sOutput.append( QString( "%1: %2, " ).arg( nParam )
391 .arg( ppAction->toQString( "", true ) ) );
392 }
393 }
394 sOutput.append( QString( ", m_pcActionVector: [" ) );
395 for ( const auto& ppAction : m_pcActionVector ) {
396 if ( ppAction != nullptr && ! ppAction->isNull() ) {
397 sOutput.append( QString( "%1, " ).arg( ppAction->toQString( "", true ) ) );
398 }
399 }
400 sOutput.append( "]" );
401 }
402
403 return sOutput;
404}
#define WARNINGLOG(x)
Definition Object.h:241
#define ERRORLOG(x)
Definition Object.h:242
static QString getNullActionType()
Definition MidiAction.h:34
static Event QStringToEvent(const QString &sEvent)
The MidiMap maps MidiActions to MidiEvents.
Definition MidiMap.h:38
void registerMMCEvent(QString, std::shared_ptr< Action >)
Sets up the relation between a mmc event and an action.
Definition MidiMap.cpp:96
std::vector< std::shared_ptr< Action > > getCCActions(int nParameter)
Returns the cc action which was linked to the given event.
Definition MidiMap.cpp:246
std::multimap< int, std::shared_ptr< Action > > m_noteActionMap
Definition MidiMap.h:113
static MidiMap * __instance
Object holding the current MidiMap singleton.
Definition MidiMap.h:46
void registerNoteEvent(int, std::shared_ptr< Action >)
Sets up the relation between a note event and an action.
Definition MidiMap.cpp:130
void registerPCEvent(std::shared_ptr< Action >)
Sets up the relation between a program change and an action.
Definition MidiMap.cpp:190
static void create_instance()
If __instance equals 0, a new MidiMap singleton will be created and stored in it.
Definition MidiMap.cpp:64
std::vector< int > findCCValuesByActionParam1(QString sActionType, QString sParam1)
Definition MidiMap.cpp:262
QMutex __mutex
Definition MidiMap.h:119
std::vector< std::pair< H2Core::MidiMessage::Event, int > > getRegisteredMidiEvents(std::shared_ptr< Action > pAction) const
Definition MidiMap.cpp:289
std::vector< std::shared_ptr< Action > > getNoteActions(int nNote)
Returns all note actions which are linked to the given event.
Definition MidiMap.cpp:229
std::vector< std::shared_ptr< Action > > getMMCActions(QString sEventString)
Returns all MMC actions which are linked to the given event.
Definition MidiMap.cpp:212
std::vector< std::shared_ptr< Action > > m_pcActionVector
Definition MidiMap.h:116
MidiMap()
Definition MidiMap.cpp:46
std::vector< int > findCCValuesByActionType(QString sActionType)
Definition MidiMap.cpp:276
QString toQString(const QString &sPrefix="", bool bShort=true) const override
Formatted string version for debugging purposes.
Definition MidiMap.cpp:334
std::multimap< QString, std::shared_ptr< Action > > m_mmcActionMap
Definition MidiMap.h:115
std::multimap< int, std::shared_ptr< Action > > m_ccActionMap
Definition MidiMap.h:114
~MidiMap()
Definition MidiMap.cpp:57
void reset()
Reinitializes the object.
Definition MidiMap.cpp:82
static void reset_instance()
Convenience function calling reset() on the current MidiMap __instance.
Definition MidiMap.cpp:71
void registerCCEvent(int, std::shared_ptr< Action >)
Sets up the relation between a cc event and an action.
Definition MidiMap.cpp:161
#define MIDI_OUT_NOTE_MIN
Definition Globals.h:30
#define MIDI_OUT_NOTE_MAX
Definition Globals.h:31