hydrogen 1.2.3
MidiTable.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-2024 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 "../Skin.h"
24#include "LCDCombo.h"
25#include "LCDSpinBox.h"
26#include "MidiSenseWidget.h"
27#include "MidiTable.h"
28
29#include <core/MidiMap.h>
30#include <core/IO/MidiCommon.h>
32#include <core/Globals.h>
33#include <core/Hydrogen.h>
35
36#include <QHeaderView>
37
38MidiTable::MidiTable( QWidget *pParent )
39 : QTableWidget( pParent )
40 , m_nRowHeight( 29 )
41 , m_nColumn0Width( 25 )
42 , m_nColumn1Width( 146 )
43 , m_nColumn2Width( 85 )
44 , m_nColumn3Width( 175 )
45 , m_nColumn4Width( 56 )
46 , m_nColumn5Width( 56 )
47 , m_nColumn6Width( 56 )
48 {
49 m_nRowCount = 0;
51
52 m_pUpdateTimer = new QTimer( this );
54}
55
56
58{
59 for( int myRow = 0; myRow <= m_nRowCount ; myRow++ ) {
60 delete cellWidget( myRow, 0 );
61 delete cellWidget( myRow, 1 );
62 delete cellWidget( myRow, 2 );
63 delete cellWidget( myRow, 3 );
64 delete cellWidget( myRow, 4 );
65 delete cellWidget( myRow, 5 );
66 delete cellWidget( myRow, 6 );
67 }
68}
69
71
73 MidiSenseWidget midiSenseWidget( this );
74 midiSenseWidget.exec();
75
76 LCDCombo * eventCombo = dynamic_cast <LCDCombo *> ( cellWidget( row, 1 ) );
77 LCDSpinBox * eventSpinner = dynamic_cast <LCDSpinBox *> ( cellWidget( row, 2 ) );
78
79
80 eventCombo->setCurrentIndex( eventCombo->findText(
82 midiSenseWidget.getLastMidiEvent() ) ) );
83 eventSpinner->setValue( midiSenseWidget.getLastMidiEventParameter() );
84
85 m_pUpdateTimer->start( 100 );
86
87 emit changed();
88}
89
90// Reimplementing this one is quite expensive. But the visibility of
91// the spinBoxes is reset after the end of updateTable(). In addition,
92// the function is only called frequently when interacting the the
93// table via mouse. This won't happen too often.
94void MidiTable::paintEvent( QPaintEvent* ev ) {
95 QTableWidget::paintEvent( ev );
97}
98
100 if( m_nRowCount > 0 ) {
101 // Ensure that the last row is empty
102 LCDCombo* pEventCombo = dynamic_cast<LCDCombo*>( cellWidget( m_nRowCount - 1, 1 ) );
103 LCDCombo* pActionCombo = dynamic_cast<LCDCombo*>( cellWidget( m_nRowCount - 1, 3 ) );
104
105 if ( pEventCombo == nullptr || pActionCombo == nullptr ) {
106 return;
107 }
108
109 if( ! pActionCombo->currentText().isEmpty() && ! pEventCombo->currentText().isEmpty() ) {
110 std::shared_ptr<Action> pAction = std::make_shared<Action>();
111 insertNewRow( pAction, "", 0 );
112 }
113
114 // Ensure that all other empty rows are removed and that the
115 // parameter spinboxes are only shown when required for that
116 // particular parameter.
117 for ( int ii = 0; ii < m_nRowCount; ii++ ) {
118 updateRow( ii );
119 }
120 }
121}
122
124 emit changed();
125}
126
127void MidiTable::insertNewRow(std::shared_ptr<Action> pAction, QString eventString, int eventParameter)
128{
130
131 insertRow( m_nRowCount );
132
133 int oldRowCount = m_nRowCount;
134
135 ++m_nRowCount;
136
137 QPushButton *midiSenseButton = new QPushButton(this);
138 midiSenseButton->setObjectName( "MidiSenseButton" );
139 midiSenseButton->setIcon(QIcon(Skin::getSvgImagePath() + "/icons/record.svg"));
140 midiSenseButton->setIconSize( QSize( 13, 13 ) );
141 midiSenseButton->setToolTip( tr("press button to record midi event") );
142
143 QSignalMapper *signalMapper = new QSignalMapper(this);
144
145 connect(midiSenseButton, SIGNAL( clicked()), signalMapper, SLOT( map() ));
146 signalMapper->setMapping( midiSenseButton, oldRowCount );
147 connect( signalMapper, SIGNAL(mapped( int ) ), this, SLOT( midiSensePressed(int) ) );
148 setCellWidget( oldRowCount, 0, midiSenseButton );
149
150
151
152 LCDCombo *eventBox = new LCDCombo(this);
153 eventBox->setSize( QSize( m_nColumn1Width, m_nRowHeight ) );
154 eventBox->insertItems( oldRowCount , H2Core::MidiMessage::getEventList() );
155 eventBox->setCurrentIndex( eventBox->findText(eventString) );
156 connect( eventBox , SIGNAL( currentIndexChanged( int ) ) , this , SLOT( updateTable() ) );
157 connect( eventBox , SIGNAL( currentIndexChanged( int ) ),
158 this, SLOT( sendChanged() ) );
159 setCellWidget( oldRowCount, 1, eventBox );
160
161
162 LCDSpinBox *eventParameterSpinner = new LCDSpinBox(this);
163 eventParameterSpinner->setSize( QSize( m_nColumn2Width, m_nRowHeight ) );
164 setCellWidget( oldRowCount , 2, eventParameterSpinner );
165 eventParameterSpinner->setMaximum( 999 );
166 eventParameterSpinner->setValue( eventParameter );
167 connect( eventParameterSpinner, SIGNAL( valueChanged( double ) ),
168 this, SLOT( sendChanged() ) );
169
170
171 LCDCombo *actionBox = new LCDCombo(this);
172 actionBox->setSize( QSize( m_nColumn3Width, m_nRowHeight ) );
173 actionBox->insertItems( oldRowCount, pActionHandler->getActionList());
174 actionBox->setCurrentIndex ( actionBox->findText( pAction->getType() ) );
175 connect( actionBox , SIGNAL( currentIndexChanged( int ) ) , this , SLOT( updateTable() ) );
176 connect( actionBox , SIGNAL( currentIndexChanged( int ) ),
177 this, SLOT( sendChanged() ) );
178 setCellWidget( oldRowCount , 3, actionBox );
179
180 bool ok;
181 LCDSpinBox *actionParameterSpinner1 = new LCDSpinBox(this);
182 actionParameterSpinner1->setSize( QSize( m_nColumn4Width, m_nRowHeight ) );
183 setCellWidget( oldRowCount , 4, actionParameterSpinner1 );
184 actionParameterSpinner1->setMaximum( 999 );
185 actionParameterSpinner1->setValue( pAction->getParameter1().toInt(&ok,10) );
186 actionParameterSpinner1->hide();
187 connect( actionParameterSpinner1, SIGNAL( valueChanged( double ) ),
188 this, SLOT( sendChanged() ) );
189
190 LCDSpinBox *actionParameterSpinner2 = new LCDSpinBox(this);
191 actionParameterSpinner2->setSize( QSize( m_nColumn5Width, m_nRowHeight ) );
192 setCellWidget( oldRowCount , 5, actionParameterSpinner2 );
193 actionParameterSpinner2->setMaximum( std::max(MAX_FX, MAX_COMPONENTS) );
194 actionParameterSpinner2->setValue( pAction->getParameter2().toInt(&ok,10) );
195 actionParameterSpinner2->hide();
196 connect( actionParameterSpinner2, SIGNAL( valueChanged( double ) ),
197 this, SLOT( sendChanged() ) );
198
199 LCDSpinBox *actionParameterSpinner3 = new LCDSpinBox(this);
200 actionParameterSpinner3->setSize( QSize( m_nColumn6Width, m_nRowHeight ) );
201 setCellWidget( oldRowCount , 6, actionParameterSpinner3 );
202 actionParameterSpinner3->setMaximum( H2Core::InstrumentComponent::getMaxLayers() );
203 actionParameterSpinner3->setValue( pAction->getParameter3().toInt(&ok,10) );
204 actionParameterSpinner3->hide();
205 connect( actionParameterSpinner3, SIGNAL( valueChanged( double ) ),
206 this, SLOT( sendChanged() ) );
207}
208
210{
211 MidiMap *pMidiMap = MidiMap::get_instance();
212
213 QStringList items;
214 items << "" << tr("Incoming Event") << tr("Event Para.")
215 << tr("Action") << tr("Para. 1") << tr("Para. 2") << tr("Para. 3");
216
217 setRowCount( 0 );
218 setColumnCount( 7 );
219
220 verticalHeader()->hide();
221
222 setHorizontalHeaderLabels( items );
223 horizontalHeader()->setStretchLastSection(true);
224
225 setColumnWidth( 0 , m_nColumn0Width );
226 setColumnWidth( 1 , m_nColumn1Width );
227 setColumnWidth( 2, m_nColumn2Width );
228 setColumnWidth( 3, m_nColumn3Width );
229 setColumnWidth( 4 , m_nColumn4Width );
230 setColumnWidth( 5 , m_nColumn5Width );
231 setColumnWidth( 6 , m_nColumn6Width );
232
233 for ( const auto& [ssMmcType, ppAction] : pMidiMap->getMMCActionMap() ) {
234 if ( ppAction != nullptr && ! ppAction->isNull() ) {
235 insertNewRow( ppAction, ssMmcType, 0 );
236 }
237 }
238
239 for ( const auto& [nnPitch, ppAction] : pMidiMap->getNoteActionMap() ) {
240 if ( ppAction != nullptr && ! ppAction->isNull() ) {
241 insertNewRow( ppAction,
244 nnPitch );
245 }
246 }
247
248 for ( const auto& [nnParam, ppAction] : pMidiMap->getCCActionMap() ) {
249 if ( ppAction != nullptr && ! ppAction->isNull() ) {
250 insertNewRow( ppAction,
253 nnParam );
254 }
255 }
256
257 for ( const auto& ppAction : pMidiMap->getPCActions() ) {
258 if ( ppAction != nullptr && ! ppAction->isNull() ) {
259 insertNewRow( ppAction,
262 }
263 }
264
265 std::shared_ptr<Action> pAction = std::make_shared<Action>();
266 insertNewRow( pAction, "", 0 );
267}
268
269
271{
273
274 for ( int row = 0; row < m_nRowCount; row++ ) {
275
276 LCDCombo * eventCombo = dynamic_cast <LCDCombo *> ( cellWidget( row, 1 ) );
277 LCDSpinBox * eventSpinner = dynamic_cast <LCDSpinBox *> ( cellWidget( row, 2 ) );
278 LCDCombo * actionCombo = dynamic_cast <LCDCombo *> ( cellWidget( row, 3 ) );
279 LCDSpinBox * actionSpinner1 = dynamic_cast <LCDSpinBox *> ( cellWidget( row, 4 ) );
280 LCDSpinBox * actionSpinner2 = dynamic_cast <LCDSpinBox *> ( cellWidget( row, 5 ) );
281 LCDSpinBox * actionSpinner3 = dynamic_cast <LCDSpinBox *> ( cellWidget( row, 6 ) );
282
283 if( !eventCombo->currentText().isEmpty() && !actionCombo->currentText().isEmpty() ){
284 const QString sEventString = eventCombo->currentText();
285 const auto event = H2Core::MidiMessage::QStringToEvent( sEventString );
286
287 const QString actionString = actionCombo->currentText();
288
289 std::shared_ptr<Action> pAction = std::make_shared<Action>( actionString );
290
291 if( actionSpinner1->cleanText() != ""){
292 pAction->setParameter1( actionSpinner1->cleanText() );
293 }
294 if( actionSpinner2->cleanText() != ""){
295 pAction->setParameter2( actionSpinner2->cleanText() );
296 }
297 if( actionSpinner3->cleanText() != ""){
298 pAction->setParameter3( actionSpinner3->cleanText() );
299 }
300
301 switch ( event ) {
303 mM->registerCCEvent( eventSpinner->cleanText().toInt() , pAction );
304 break;
305
307 mM->registerNoteEvent( eventSpinner->cleanText().toInt() , pAction );
308 break;
309
311 mM->registerPCEvent( pAction );
312 break;
313
315 // Event not recognized
316 continue;
317
318 default:
319 // All remaining events should be different trades of
320 // MMC events. If not, registerMMCEvent will handle it.
321 mM->registerMMCEvent( sEventString , pAction );
322 }
323 }
324 }
325}
326
327void MidiTable::updateRow( int nRow ) {
328 LCDCombo* pEventCombo = dynamic_cast<LCDCombo*>( cellWidget( nRow, 1 ) );
329 LCDCombo* pActionCombo = dynamic_cast<LCDCombo*>( cellWidget( nRow, 3 ) );
330
331 if ( pEventCombo == nullptr || pActionCombo == nullptr ) {
332 return;
333 }
334
335 if( pActionCombo->currentText().isEmpty() &&
336 pEventCombo->currentText().isEmpty() && nRow != m_nRowCount - 1 ) {
337
338 removeRow( nRow );
339 m_nRowCount--;
340 return;
341 }
342
343 // Adjust the event parameter spin box to fit the need of the
344 // particular event.
345 LCDSpinBox* pEventParameterSpinner = dynamic_cast<LCDSpinBox*>( cellWidget( nRow, 2 ) );
346 const QString sEventString = pEventCombo->currentText();
347 const auto event = H2Core::MidiMessage::QStringToEvent( sEventString );
348
349 switch ( event ) {
351 pEventParameterSpinner->show();
352 pEventParameterSpinner->setMinimum( 0 );
353 pEventParameterSpinner->setMaximum( 127 );
354 break;
355
357 pEventParameterSpinner->show();
358 pEventParameterSpinner->setMinimum( MIDI_OUT_NOTE_MIN );
359 pEventParameterSpinner->setMaximum( MIDI_OUT_NOTE_MAX );
360 break;
361
364 default:
365 // Includes all MMC events
366 pEventParameterSpinner->hide();
367 }
368
369 QString sActionType = pActionCombo->currentText();
370 LCDSpinBox* pActionSpinner1 = dynamic_cast<LCDSpinBox*>( cellWidget( nRow, 4 ) );
371 LCDSpinBox* pActionSpinner2 = dynamic_cast<LCDSpinBox*>( cellWidget( nRow, 5 ) );
372 LCDSpinBox* pActionSpinner3 = dynamic_cast<LCDSpinBox*>( cellWidget( nRow, 6 ) );
373 if ( sActionType == Action::getNullActionType() || sActionType.isEmpty() ) {
374 pActionSpinner1->hide();
375 pActionSpinner2->hide();
376 pActionSpinner3->hide();
377
378 } else {
379 int nParameterNumber = MidiActionManager::get_instance()->getParameterNumber( sActionType );
380 if ( nParameterNumber != -1 ) {
381 if ( nParameterNumber < 3 ) {
382 pActionSpinner3->hide();
383 } else {
384 pActionSpinner3->show();
385 }
386 if ( nParameterNumber < 2 ) {
387 pActionSpinner2->hide();
388 } else {
389 pActionSpinner2->show();
390 }
391 if ( nParameterNumber < 1 ) {
392 pActionSpinner1->hide();
393 } else {
394 pActionSpinner1->show();
395 }
396 } else {
397 ERRORLOG( QString( "Unable to find MIDI action [%1]" ).arg( sActionType ) );
398 }
399
400 // Relative changes should allow for both increasing and
401 // decreasing the pattern number.
402 if ( sActionType == "SELECT_NEXT_PATTERN_RELATIVE" ) {
403 pActionSpinner1->setMinimum( -1 * pActionSpinner1->maximum() );
404 }
405 }
406}
#define ERRORLOG(x)
Definition Object.h:239
static QString getNullActionType()
Definition MidiAction.h:34
static QStringList getEventList()
Retrieve the string representation for all available Event.
static QString EventToQString(Event event)
static Event QStringToEvent(const QString &sEvent)
void setSize(QSize size)
Definition LCDCombo.cpp:174
Custom spin box.
Definition LCDSpinBox.h:50
void setValue(double fValue)
void setSize(QSize size)
The MidiActionManager cares for the execution of MidiActions.
Definition MidiAction.h:142
static MidiActionManager * get_instance()
Returns a pointer to the current MidiActionManager singleton stored in __instance.
Definition MidiAction.h:252
QStringList getActionList()
Definition MidiAction.h:254
int getParameterNumber(const QString &sActionType) const
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
void registerNoteEvent(int, std::shared_ptr< Action >)
Sets up the relation between a note event and an action.
Definition MidiMap.cpp:130
std::multimap< int, std::shared_ptr< Action > > getCCActionMap() const
Definition MidiMap.h:128
void registerPCEvent(std::shared_ptr< Action >)
Sets up the relation between a program change and an action.
Definition MidiMap.cpp:190
std::vector< std::shared_ptr< Action > > getPCActions() const
Returns the pc action which was linked to the given event.
Definition MidiMap.h:131
std::multimap< QString, std::shared_ptr< Action > > getMMCActionMap() const
Definition MidiMap.h:122
std::multimap< int, std::shared_ptr< Action > > getNoteActionMap() const
Definition MidiMap.h:125
static MidiMap * get_instance()
Returns a pointer to the current MidiMap singleton stored in __instance.
Definition MidiMap.h:65
void registerCCEvent(int, std::shared_ptr< Action >)
Sets up the relation between a cc event and an action.
Definition MidiMap.cpp:161
int getLastMidiEventParameter() const
H2Core::MidiMessage::Event getLastMidiEvent() const
void saveMidiTable()
int m_nColumn3Width
Definition MidiTable.h:67
int m_nColumn5Width
Definition MidiTable.h:69
int m_nColumn1Width
Definition MidiTable.h:65
QTimer * m_pUpdateTimer
Definition MidiTable.h:62
int m_nRowHeight
Definition MidiTable.h:63
void setupMidiTable()
void changed()
Identicates a user action changing the content of the table.
int m_nColumn6Width
Definition MidiTable.h:70
void insertNewRow(std::shared_ptr< Action > pAction, QString eventString, int eventParameter)
int m_nColumn2Width
Definition MidiTable.h:66
MidiTable(QWidget *pParent)
Definition MidiTable.cpp:38
void updateRow(int nRow)
int m_nColumn4Width
Definition MidiTable.h:68
void midiSensePressed(int)
Definition MidiTable.cpp:70
int m_nRowCount
Definition MidiTable.h:60
int m_nCurrentMidiAutosenseRow
Definition MidiTable.h:61
void sendChanged()
void updateTable()
Definition MidiTable.cpp:99
virtual void paintEvent(QPaintEvent *ev) override
Definition MidiTable.cpp:94
int m_nColumn0Width
Definition MidiTable.h:64
static QString getSvgImagePath()
Definition Skin.h:40
#define MAX_COMPONENTS
Maximum number of components each Instrument is allowed to have.
Definition config.dox:75
#define MAX_FX
Maximum number of effects.
Definition config.dox:83
#define MIDI_OUT_NOTE_MIN
Definition Globals.h:30
#define MIDI_OUT_NOTE_MAX
Definition Globals.h:31