From b8e9418dc930b81f6b427bf62758281546f54067 Mon Sep 17 00:00:00 2001 From: Aurelien Leblond Date: Mon, 21 Apr 2014 00:14:44 +0100 Subject: [PATCH 01/11] Midi Foot Pedal (Midi CC 04) basic management --- data/xsd/drumkit.xsd | 3 ++ src/core/include/hydrogen/IO/MidiInput.h | 2 + src/core/include/hydrogen/basics/instrument.h | 42 +++++++++++++++++++ src/core/src/IO/midi_input.cpp | 29 +++++++++++++ src/core/src/basics/instrument.cpp | 16 +++++++ src/core/src/helpers/legacy.cpp | 3 ++ 6 files changed, 95 insertions(+) diff --git a/data/xsd/drumkit.xsd b/data/xsd/drumkit.xsd index f62038bfd4..94786ac9f5 100644 --- a/data/xsd/drumkit.xsd +++ b/data/xsd/drumkit.xsd @@ -61,6 +61,9 @@ + + + diff --git a/src/core/include/hydrogen/IO/MidiInput.h b/src/core/include/hydrogen/IO/MidiInput.h index 0f33ba064c..8582e322c8 100644 --- a/src/core/include/hydrogen/IO/MidiInput.h +++ b/src/core/include/hydrogen/IO/MidiInput.h @@ -64,6 +64,8 @@ class MidiInput : public virtual Object unsigned long __noteOffTick; unsigned long computeDeltaNoteOnOfftime(); + int __hihat_cc_openess; + }; diff --git a/src/core/include/hydrogen/basics/instrument.h b/src/core/include/hydrogen/basics/instrument.h index 5a6b5150c3..f09e7b8919 100644 --- a/src/core/include/hydrogen/basics/instrument.h +++ b/src/core/include/hydrogen/basics/instrument.h @@ -237,6 +237,15 @@ class Instrument : public H2Core::Object /** get the stop notes of the instrument */ bool is_stop_notes() const; + void set_hihat( bool ishihat ); + bool is_hihat() const; + + void set_lower_cc( int message ); + int get_lower_cc() const; + + void set_higher_cc( int message ); + int get_higher_cc() const; + ///< set the name of the related drumkit void set_drumkit_name( const QString& name ); ///< get the name of the related drumkits @@ -268,6 +277,9 @@ class Instrument : public H2Core::Object int __queued; ///< count the number of notes queued within Sampler::__playing_notes_queue or std::priority_queue m_songNoteQueue float __fx_level[MAX_FX]; ///< Ladspa FX level array InstrumentLayer* __layers[MAX_LAYERS]; ///< InstrumentLayer array + bool __hihat; ///< the instrument is a hihat + int __lower_cc; ///< lower cc level + int __higher_cc; ///< higher cc level }; // DEFINITIONS @@ -506,6 +518,36 @@ inline bool Instrument::is_stop_notes() const return __stop_notes; } +inline void Instrument::set_hihat( bool ishihat ) +{ + __hihat = ishihat; +} + +inline bool Instrument::is_hihat() const +{ + return __hihat; +} + +inline void Instrument::set_lower_cc( int message ) +{ + __lower_cc = message; +} + +inline int Instrument::get_lower_cc() const +{ + return __lower_cc; +} + +inline void Instrument::set_higher_cc( int message ) +{ + __higher_cc = message; +} + +inline int Instrument::get_higher_cc() const +{ + return __higher_cc; +} + inline InstrumentLayer* Instrument::operator[]( int idx ) { assert( idx>=0 && idx handleAction( pAction ); + if(msg.m_nData1 == 04) + __hihat_cc_openess = msg.m_nData2; + pEngine->lastMidiEvent = "CC"; pEngine->lastMidiEventParameter = msg.m_nData1; } @@ -248,6 +252,31 @@ void MidiInput::handleNoteOnMessage( const MidiMessage& msg ) nInstrument = MAX_INSTRUMENTS - 1; } + InstrumentList *instrList = pEngine->getSong()->get_instrument_list(); + Instrument *instr = instrList->get( nInstrument ); + /* + Only look to change instrument if the + current note is actually of hihat and + hihat openess is outside the instrument selected + */ + if ( instr != NULL && + instr->is_hihat() && + ( __hihat_cc_openess < instr->get_lower_cc() || __hihat_cc_openess > instr->get_higher_cc() ) ) + { + for(int i=0 ; i<=instrList->size() ; i++) + { + Instrument *instr_contestant = instrList->get( i ); + if( instr_contestant != NULL && + instr_contestant->is_hihat() && + __hihat_cc_openess >= instr_contestant->get_lower_cc() && + __hihat_cc_openess <= instr_contestant->get_higher_cc() ) + { + nInstrument = i; + break; + } + } + } + pEngine->addRealtimeNote( nInstrument, fVelocity, fPan_L, fPan_R, 0.0, false, true, nNote ); } diff --git a/src/core/src/basics/instrument.cpp b/src/core/src/basics/instrument.cpp index acbed8c7e8..a0aee7d4f5 100644 --- a/src/core/src/basics/instrument.cpp +++ b/src/core/src/basics/instrument.cpp @@ -63,6 +63,9 @@ Instrument::Instrument( const int id, const QString& name, ADSR* adsr ) , __muted( false ) , __mute_group( -1 ) , __queued( 0 ) + , __hihat( false ) + , __lower_cc( 0 ) + , __higher_cc( 127 ) { if ( __adsr==0 ) __adsr = new ADSR(); for ( int i=0; iis_muted() ) , __mute_group( other->get_mute_group() ) , __queued( other->is_queued() ) + , __hihat( other->is_hihat() ) + , __lower_cc( other->get_lower_cc() ) + , __higher_cc( other->get_higher_cc() ) { for ( int i=0; iget_fx_level( i ); @@ -173,6 +179,9 @@ void Instrument::load_from( Drumkit* drumkit, Instrument* instrument, bool is_li this->set_midi_out_channel( instrument->get_midi_out_channel() ); this->set_midi_out_note( instrument->get_midi_out_note() ); this->set_stop_notes( instrument->is_stop_notes() ); + this->set_hihat( instrument->is_hihat() ); + this->set_lower_cc( instrument->get_lower_cc() ); + this->set_higher_cc( instrument->get_higher_cc() ); if ( is_live ) AudioEngine::get_instance()->unlock(); } @@ -214,6 +223,10 @@ Instrument* Instrument::load_from( XMLNode* node, const QString& dk_path, const instrument->set_midi_out_channel( node->read_int( "midiOutChannel", -1, true, false ) ); instrument->set_midi_out_note( node->read_int( "midiOutNote", MIDI_MIDDLE_C, true, false ) ); instrument->set_stop_notes( node->read_bool( "isStopNote", true ,false ) ); + instrument->set_hihat( node->read_bool( "isHihat", false, true ) ); + instrument->set_lower_cc( node->read_int( "lower_cc", 0, true ) ); + instrument->set_higher_cc( node->read_int( "higher_cc", 127, true ) ); + for ( int i=0; iset_fx_level( node->read_float( QString( "FX%1Level" ).arg( i+1 ), 0.0 ), i ); } @@ -269,6 +282,9 @@ void Instrument::save_to( XMLNode* node ) instrument_node.write_int( "midiOutChannel", __midi_out_channel ); instrument_node.write_int( "midiOutNote", __midi_out_note ); instrument_node.write_bool( "isStopNote", __stop_notes ); + instrument_node.write_bool( "isHihat", __hihat ); + instrument_node.write_int( "lower_cc", __lower_cc ); + instrument_node.write_int( "higher_cc", __higher_cc ); for ( int i=0; iset_midi_out_channel( instrument_node.read_int( "midiOutChannel", -1, true, false ) ); instrument->set_midi_out_note( instrument_node.read_int( "midiOutNote", MIDI_MIDDLE_C, true, false ) ); instrument->set_stop_notes( instrument_node.read_bool( "isStopNote", true ,false ) ); + instrument->set_hihat( instrument_node.read_bool( "isHihat", false, true ) ); + instrument->set_lower_cc( instrument_node.read_int( "lower_cc", 0, true ) ); + instrument->set_higher_cc( instrument_node.read_int( "higher_cc", 127, true ) ); for ( int i=0; iset_fx_level( instrument_node.read_float( QString( "FX%1Level" ).arg( i+1 ), 0.0 ), i ); } From 2f00c252227ca4855c3a74e74778751654a0a5e6 Mon Sep 17 00:00:00 2001 From: Aurelien Leblond Date: Tue, 22 Apr 2014 16:05:13 +0100 Subject: [PATCH 02/11] Instrument Editor tab - makes space for hihat controls --- .../instrumentEditor/instrumentTab_new.png | Bin 0 -> 403641 bytes .../src/InstrumentEditor/InstrumentEditor.cpp | 82 +++++++++--------- 2 files changed, 41 insertions(+), 41 deletions(-) create mode 100644 data/img/gray/instrumentEditor/instrumentTab_new.png diff --git a/data/img/gray/instrumentEditor/instrumentTab_new.png b/data/img/gray/instrumentEditor/instrumentTab_new.png new file mode 100644 index 0000000000000000000000000000000000000000..7976000f40784d172b90dd3b4cd4fc8cdf869a13 GIT binary patch literal 403641 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 VMe~Pmove(0, 31); - m_pInstrumentProp->setPixmap( "/instrumentEditor/instrumentTab.png" ); + m_pInstrumentProp->setPixmap( "/instrumentEditor/instrumentTab_new.png" ); m_pNameLbl = new ClickableLabel( m_pInstrumentProp ); m_pNameLbl->setGeometry( 8, 5, 275, 28 ); - + ///////////// //Midi Out - + m_pMidiOutChannelLCD = new LCDDisplay( m_pInstrumentProp, LCDDigit::SMALL_BLUE, 4 ); - m_pMidiOutChannelLCD->move( 67, 243 ); + m_pMidiOutChannelLCD->move( 67, 261 ); m_pAddMidiOutChannelBtn = new Button( m_pInstrumentProp, @@ -114,7 +114,7 @@ InstrumentEditor::InstrumentEditor( QWidget* pParent ) QSize( 16, 8 ) ); - m_pAddMidiOutChannelBtn->move( 109, 243 ); + m_pAddMidiOutChannelBtn->move( 109, 260 ); connect( m_pAddMidiOutChannelBtn, SIGNAL( clicked(Button*) ), this, SLOT( midiOutChannelBtnClicked(Button*) ) ); @@ -125,12 +125,12 @@ InstrumentEditor::InstrumentEditor( QWidget* pParent ) "/lcd/LCDSpinBox_down_over.png", QSize(16,8) ); - m_pDelMidiOutChannelBtn->move( 109, 251 ); + m_pDelMidiOutChannelBtn->move( 109, 269 ); connect( m_pDelMidiOutChannelBtn, SIGNAL( clicked(Button*) ), this, SLOT( midiOutChannelBtnClicked(Button*) ) ); - + /// m_pMidiOutNoteLCD = new LCDDisplay( m_pInstrumentProp, LCDDigit::SMALL_BLUE, 4 ); - m_pMidiOutNoteLCD->move( 160, 243 ); + m_pMidiOutNoteLCD->move( 160, 261 ); m_pAddMidiOutNoteBtn = new Button( m_pInstrumentProp, @@ -140,7 +140,7 @@ InstrumentEditor::InstrumentEditor( QWidget* pParent ) QSize( 16, 8 ) ); - m_pAddMidiOutNoteBtn->move( 202, 243 ); + m_pAddMidiOutNoteBtn->move( 202, 260 ); connect( m_pAddMidiOutNoteBtn, SIGNAL( clicked(Button*) ), this, SLOT( midiOutNoteBtnClicked(Button*) ) ); @@ -151,9 +151,9 @@ InstrumentEditor::InstrumentEditor( QWidget* pParent ) "/lcd/LCDSpinBox_down_over.png", QSize(16,8) ); - m_pDelMidiOutNoteBtn->move( 202, 251 ); + m_pDelMidiOutNoteBtn->move( 202, 269 ); connect( m_pDelMidiOutNoteBtn, SIGNAL( clicked(Button*) ), this, SLOT( midiOutNoteBtnClicked(Button*) ) ); - + ///////////// QFont boldFont; @@ -162,7 +162,7 @@ InstrumentEditor::InstrumentEditor( QWidget* pParent ) connect( m_pNameLbl, SIGNAL( labelClicked(ClickableLabel*) ), this, SLOT( labelClicked(ClickableLabel*) ) ); m_pRandomPitchRotary = new Rotary( m_pInstrumentProp, Rotary::TYPE_NORMAL, trUtf8( "Random pitch factor" ), false, true ); - m_pRandomPitchRotary->move( 117, 192 ); + m_pRandomPitchRotary->move( 117, 210 ); connect( m_pRandomPitchRotary, SIGNAL( valueChanged(Rotary*) ), this, SLOT( rotaryChanged(Rotary*) ) ); // Filter @@ -181,9 +181,9 @@ InstrumentEditor::InstrumentEditor( QWidget* pParent ) m_pResonanceRotary = new Rotary( m_pInstrumentProp, Rotary::TYPE_NORMAL, trUtf8( "Filter resonance" ), false, true ); connect( m_pResonanceRotary, SIGNAL( valueChanged(Rotary*) ), this, SLOT( rotaryChanged(Rotary*) ) ); - m_pFilterBypassBtn->move( 70, 152 ); - m_pCutoffRotary->move( 117, 146 ); - m_pResonanceRotary->move( 170, 146 ); + m_pFilterBypassBtn->move( 70, 170 ); + m_pCutoffRotary->move( 117, 164 ); + m_pResonanceRotary->move( 170, 164 ); //~ Filter // ADSR @@ -234,8 +234,8 @@ InstrumentEditor::InstrumentEditor( QWidget* pParent ) m_pDelMuteGroupBtn->move( 202, 113 ); connect( m_pDelMuteGroupBtn, SIGNAL( clicked(Button*) ), this, SLOT( muteGroupBtnClicked(Button*) ) ); - m_pIsStopNoteCheckBox = new QCheckBox ( trUtf8( "Auto-Stop-Note" ), m_pInstrumentProp ); - m_pIsStopNoteCheckBox->move( 15, 300 ); + m_pIsStopNoteCheckBox = new QCheckBox ( trUtf8( "" ), m_pInstrumentProp ); + m_pIsStopNoteCheckBox->move( 63, 138 ); m_pIsStopNoteCheckBox->setToolTip( trUtf8( "Stop the current playing instrument-note before trigger the next note sample." ) ); connect( m_pIsStopNoteCheckBox, SIGNAL( toggled( bool ) ), this, SLOT( onIsStopNoteCheckBoxClicked( bool ) ) ); @@ -255,7 +255,7 @@ InstrumentEditor::InstrumentEditor( QWidget* pParent ) // Layer preview m_pLayerPreview = new LayerPreview( NULL ); - + m_pLayerScrollArea = new QScrollArea( m_pLayerProp); m_pLayerScrollArea->setFrameShape( QFrame::NoFrame ); m_pLayerScrollArea->move( 6, 4 ); @@ -421,22 +421,22 @@ void InstrumentEditor::selectedInstrumentChangedEvent() if (m_pInstrument->get_mute_group() == -1 ) { sMuteGroup = "Off"; } - m_pMuteGroupLCD->setText( sMuteGroup ); - + m_pMuteGroupLCD->setText( sMuteGroup ); + // midi out QString sMidiOutChannel = QString("%1").arg( m_pInstrument->get_midi_out_channel()+1); if (m_pInstrument->get_midi_out_channel() == -1 ) { sMidiOutChannel = "Off"; } m_pMidiOutChannelLCD->setText( sMidiOutChannel ); - + //Convert note id into notation { int note = m_pInstrument->get_midi_out_note(); int octave = (note / 12) - 2; const char *noteStrs[12] = { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" }; QString sMidiOutNote = QString(noteStrs[note % 12]) + QString::number(octave); - m_pMidiOutNoteLCD->setText( sMidiOutNote ); + m_pMidiOutNoteLCD->setText( sMidiOutNote ); } // select the last valid layer @@ -593,7 +593,7 @@ void InstrumentEditor::buttonClicked( Button* pButton ) HydrogenApp::get_instance()->showSampleEditor( name, m_nSelectedLayer ); } } - + } else { ERRORLOG( "[buttonClicked] unhandled button" ); @@ -620,7 +620,7 @@ void InstrumentEditor::loadLayer() if ( filename[2].isEmpty() ) return; - bool fnc = false; + bool fnc = false; if ( filename[0] == "true" ){ fnc = true; } @@ -630,36 +630,36 @@ void InstrumentEditor::loadLayer() int selectedLayer = m_nSelectedLayer; int firstSelection = selectedLayer; - - + + if (filename.size() > 2) { - - for(int i=2;i < filename.size();++i) + + for(int i=2;i < filename.size();++i) { selectedLayer = m_nSelectedLayer + i - 2; if( ( i-2 >= MAX_LAYERS ) || ( selectedLayer + 1 > MAX_LAYERS ) ) break; Sample *newSample = Sample::load( filename[i] ); - + H2Core::Instrument *pInstr = NULL; - + AudioEngine::get_instance()->lock( RIGHT_HERE ); Song *song = engine->getSong(); InstrumentList *instrList = song->get_instrument_list(); pInstr = instrList->get( engine->getSelectedInstrumentNumber() ); - - /* - if we're using multiple layers, we start inserting the first layer + + /* + if we're using multiple layers, we start inserting the first layer at m_nSelectedLayer and the next layer at m_nSelectedLayer+1 */ - + H2Core::InstrumentLayer *pLayer = pInstr->get_layer( selectedLayer ); if (pLayer != NULL) { // delete old sample Sample *oldSample = pLayer->get_sample(); delete oldSample; - + // insert new sample from newInstrument pLayer->set_sample( newSample ); } @@ -667,20 +667,20 @@ void InstrumentEditor::loadLayer() pLayer = new H2Core::InstrumentLayer(newSample); pInstr->set_layer( pLayer, selectedLayer ); } - + if ( fnc ){ QString newFilename = filename[i].section( '/', -1 ); newFilename.replace( "." + newFilename.section( '.', -1 ), ""); m_pInstrument->set_name( newFilename ); } - + //set automatic velocity if ( filename[1] == "true" ){ setAutoVelocity(); } - + //pInstr->set_drumkit_name( "" ); // external sample, no drumkit info - + AudioEngine::get_instance()->unlock(); } @@ -877,13 +877,13 @@ void InstrumentEditor::midiOutNoteBtnClicked(Button *pRef) ); if( !newSample ){ continue; - } + } delete pSample; // insert new sample from newInstrument AudioEngine::get_instance()->lock( RIGHT_HERE ); pLayer->set_sample( newSample ); AudioEngine::get_instance()->unlock(); - + } } } From 2b86bb8bbad955a2acd3fa330be04ff9dcde812c Mon Sep 17 00:00:00 2001 From: Aurelien Leblond Date: Tue, 22 Apr 2014 19:32:00 +0100 Subject: [PATCH 03/11] GUI for hihat instrument management --- .../instrumentEditor/instrumentTab_new.png | Bin 403641 -> 424568 bytes .../src/InstrumentEditor/InstrumentEditor.cpp | 98 ++++++++++++++++++ .../src/InstrumentEditor/InstrumentEditor.h | 24 ++++- 3 files changed, 118 insertions(+), 4 deletions(-) diff --git a/data/img/gray/instrumentEditor/instrumentTab_new.png b/data/img/gray/instrumentEditor/instrumentTab_new.png index 7976000f40784d172b90dd3b4cd4fc8cdf869a13..b7edbad710381917a3ea18f5d7a6b1a890d3edde 100644 GIT binary patch delta 4369 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 delta 734 zcmYLHO-L0{6rOYE&N%ZjvF8haUW<_|9)x9xCR8L^poyaEF@1|xw#Y}i$jWEN18d{G zOnF`OGi{n)Mc-eLp9oemYsA z%C0tjm+3F2hNwtN((JJWh>Ct6K6h+K^R`-XvtY+|ptk;gzgP{^a1Ro^_ zIWy#!e0ml<)&T!XDnKILal+Yc-;KLKKb&wbbDMIfZ%tPHVB zR4ml%G2ivNsMy0b!x>6vJ;qhXjUNlhpNER6h#vyWV`X7x1!Ad~T2_lVTL`sqmcK@u zgVTC-YW|&*PBuz>t^v@6B`{hQt#u?KXK%qp>Q9v4hOAh`8`C<<( diff --git a/src/gui/src/InstrumentEditor/InstrumentEditor.cpp b/src/gui/src/InstrumentEditor/InstrumentEditor.cpp index cdfd158fc1..e32e421dc8 100644 --- a/src/gui/src/InstrumentEditor/InstrumentEditor.cpp +++ b/src/gui/src/InstrumentEditor/InstrumentEditor.cpp @@ -239,6 +239,65 @@ InstrumentEditor::InstrumentEditor( QWidget* pParent ) m_pIsStopNoteCheckBox->setToolTip( trUtf8( "Stop the current playing instrument-note before trigger the next note sample." ) ); connect( m_pIsStopNoteCheckBox, SIGNAL( toggled( bool ) ), this, SLOT( onIsStopNoteCheckBoxClicked( bool ) ) ); + ////////////////////////// + // HiHat setup + + m_pIsHihat = new QCheckBox ( trUtf8( "" ), m_pInstrumentProp ); + m_pIsHihat->move( 63, 304 ); + m_pIsHihat->setToolTip( trUtf8( "Set the instrument as part of a hihat set." ) ); + connect( m_pIsHihat, SIGNAL( toggled( bool ) ), this, SLOT( pIsHihatCheckBoxClicked( bool ) ) ); + + + m_pHihatMinRangeLCD = new LCDDisplay( m_pInstrumentProp, LCDDigit::SMALL_BLUE, 4 ); + m_pHihatMinRangeLCD->move( 67, 320 ); + + m_pAddHihatMinRangeBtn = new Button( + m_pInstrumentProp, + "/lcd/LCDSpinBox_up_on.png", + "/lcd/LCDSpinBox_up_off.png", + "/lcd/LCDSpinBox_up_over.png", + QSize( 16, 8 ) + ); + m_pAddHihatMinRangeBtn->move( 109, 319 ); + connect( m_pAddHihatMinRangeBtn, SIGNAL( clicked(Button*) ), this, SLOT( hihatMinRangeBtnClicked(Button*) ) ); + + m_pDelHihatMinRangeBtn = new Button( + m_pInstrumentProp, + "/lcd/LCDSpinBox_down_on.png", + "/lcd/LCDSpinBox_down_off.png", + "/lcd/LCDSpinBox_down_over.png", + QSize(16,8) + ); + m_pDelHihatMinRangeBtn->move( 109, 328 ); + connect( m_pDelHihatMinRangeBtn, SIGNAL( clicked(Button*) ), this, SLOT( hihatMinRangeBtnClicked(Button*) ) ); + + + m_pHihatMaxRangeLCD = new LCDDisplay( m_pInstrumentProp, LCDDigit::SMALL_BLUE, 4 ); + m_pHihatMaxRangeLCD->move( 160, 320 ); + + m_pAddHihatMaxRangeBtn = new Button( + m_pInstrumentProp, + "/lcd/LCDSpinBox_up_on.png", + "/lcd/LCDSpinBox_up_off.png", + "/lcd/LCDSpinBox_up_over.png", + QSize( 16, 8 ) + ); + m_pAddHihatMaxRangeBtn->move( 202, 319 ); + connect( m_pAddHihatMaxRangeBtn, SIGNAL( clicked(Button*) ), this, SLOT( hihatMaxRangeBtnClicked(Button*) ) ); + + m_pDelHihatMaxRangeBtn = new Button( + m_pInstrumentProp, + "/lcd/LCDSpinBox_down_on.png", + "/lcd/LCDSpinBox_down_off.png", + "/lcd/LCDSpinBox_down_over.png", + QSize(16,8) + ); + m_pDelHihatMaxRangeBtn->move( 202, 328 ); + connect( m_pDelHihatMaxRangeBtn, SIGNAL( clicked(Button*) ), this, SLOT( hihatMaxRangeBtnClicked(Button*) ) ); + + // + + //~ Instrument properties @@ -430,6 +489,13 @@ void InstrumentEditor::selectedInstrumentChangedEvent() } m_pMidiOutChannelLCD->setText( sMidiOutChannel ); + // hihat + m_pIsHihat->setChecked( m_pInstrument->is_hihat() ); + QString sHiHatMinRange = QString("%1").arg( m_pInstrument->get_lower_cc() ); + m_pHihatMinRangeLCD->setText( sHiHatMinRange ); + QString sHiHatMaxRange = QString("%1").arg( m_pInstrument->get_higher_cc() ); + m_pHihatMaxRangeLCD->setText( sHiHatMaxRange ); + //Convert note id into notation { int note = m_pInstrument->get_midi_out_note(); @@ -893,3 +959,35 @@ void InstrumentEditor::midiOutNoteBtnClicked(Button *pRef) } } + +void InstrumentEditor::pIsHihatCheckBoxClicked( bool on ) +{ + assert( m_pInstrument ); + + m_pInstrument->set_hihat( on ); + selectedInstrumentChangedEvent(); // force an update +} + +void InstrumentEditor::hihatMinRangeBtnClicked(Button *pRef) +{ + assert( m_pInstrument ); + + if ( pRef == m_pAddHihatMinRangeBtn && m_pInstrument->get_lower_cc() < 127 ) + m_pInstrument->set_lower_cc( m_pInstrument->get_lower_cc() + 1 ); + else if ( pRef == m_pDelHihatMinRangeBtn && m_pInstrument->get_lower_cc() > 0 ) + m_pInstrument->set_lower_cc( m_pInstrument->get_lower_cc() - 1 ); + + selectedInstrumentChangedEvent(); // force an update +} + +void InstrumentEditor::hihatMaxRangeBtnClicked(Button *pRef) +{ + assert( m_pInstrument ); + + if ( pRef == m_pAddHihatMaxRangeBtn && m_pInstrument->get_higher_cc() < 127 ) + m_pInstrument->set_higher_cc( m_pInstrument->get_higher_cc() + 1); + else if ( pRef == m_pDelHihatMaxRangeBtn && m_pInstrument->get_higher_cc() > 0 ) + m_pInstrument->set_higher_cc( m_pInstrument->get_higher_cc() - 1); + + selectedInstrumentChangedEvent(); // force an update +} diff --git a/src/gui/src/InstrumentEditor/InstrumentEditor.h b/src/gui/src/InstrumentEditor/InstrumentEditor.h index 3f0f31a55b..50edef746c 100644 --- a/src/gui/src/InstrumentEditor/InstrumentEditor.h +++ b/src/gui/src/InstrumentEditor/InstrumentEditor.h @@ -73,6 +73,10 @@ class InstrumentEditor : public QWidget, public H2Core::Object, public EventList void midiOutChannelBtnClicked(Button *pRef); void midiOutNoteBtnClicked(Button *pRef); + void pIsHihatCheckBoxClicked( bool on); + void hihatMinRangeBtnClicked(Button *pRef); + void hihatMaxRangeBtnClicked(Button *pRef); + private: H2Core::Instrument *m_pInstrument; int m_nSelectedLayer; @@ -107,15 +111,27 @@ class InstrumentEditor : public QWidget, public H2Core::Object, public EventList LCDDisplay *m_pMuteGroupLCD; Button *m_pAddMuteGroupBtn; Button *m_pDelMuteGroupBtn; - + // Instrument midi out LCDDisplay *m_pMidiOutChannelLCD; Button *m_pAddMidiOutChannelBtn; Button *m_pDelMidiOutChannelBtn; - + LCDDisplay *m_pMidiOutNoteLCD; Button *m_pAddMidiOutNoteBtn; - Button *m_pDelMidiOutNoteBtn; + Button *m_pDelMidiOutNoteBtn; + + // Instrument hihat + + QCheckBox *m_pIsHihat; + + LCDDisplay *m_pHihatMinRangeLCD; + Button *m_pAddHihatMinRangeBtn; + Button *m_pDelHihatMinRangeBtn; + + LCDDisplay *m_pHihatMaxRangeLCD; + Button *m_pAddHihatMaxRangeBtn; + Button *m_pDelHihatMaxRangeBtn; //~ Instrument properties @@ -123,7 +139,7 @@ class InstrumentEditor : public QWidget, public H2Core::Object, public EventList // Layer properties LayerPreview *m_pLayerPreview; QScrollArea *m_pLayerScrollArea; - + PixmapWidget *m_pLayerProp; Rotary *m_pLayerGainRotary; From 5095411d769dba0527671d9bb8a4ce553187e234 Mon Sep 17 00:00:00 2001 From: Aurelien Leblond Date: Tue, 22 Apr 2014 20:49:43 +0100 Subject: [PATCH 04/11] Implement a press&hold for the button. Handy for hihat range control instead of having to click 127 times! --- src/gui/src/widgets/Button.cpp | 21 ++++++++++++++++++++- src/gui/src/widgets/Button.h | 6 ++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/gui/src/widgets/Button.cpp b/src/gui/src/widgets/Button.cpp index eb65446ae0..c20611d61b 100644 --- a/src/gui/src/widgets/Button.cpp +++ b/src/gui/src/widgets/Button.cpp @@ -66,6 +66,10 @@ Button::Button( QWidget * pParent, const QString& sOnImage, const QString& sOffI } this->setStyleSheet("font-size: 9px; font-weight: bold;"); + + m_timerTimeout = 0; + m_timer = new QTimer(this); + connect(m_timer, SIGNAL(timeout()), this, SLOT(buttonPressed_timer_timeout())); } @@ -126,7 +130,14 @@ void Button::mousePressEvent(QMouseEvent*ev) { m_bPressed = true; update(); + emit mousePress(this); + + if ( ev->button() == Qt::LeftButton ) + { + m_timerTimeout = 2000; + buttonPressed_timer_timeout(); + } } @@ -136,12 +147,20 @@ void Button::mouseReleaseEvent(QMouseEvent* ev) setPressed( false ); if (ev->button() == Qt::LeftButton) { - emit clicked(this); + m_timer->stop(); } else if (ev->button() == Qt::RightButton) { emit rightClicked(this); } +} + +void Button::buttonPressed_timer_timeout() +{ + emit clicked(this); + if(m_timerTimeout > 50) + m_timerTimeout = m_timerTimeout / 2; + m_timer->start(m_timerTimeout); } void Button::setFontSize(int size) diff --git a/src/gui/src/widgets/Button.h b/src/gui/src/widgets/Button.h index 1452fbd8cf..4f28804527 100644 --- a/src/gui/src/widgets/Button.h +++ b/src/gui/src/widgets/Button.h @@ -64,6 +64,9 @@ class Button : public QWidget, public H2Core::Object, public MidiLearnable void rightClicked(Button *pBtn); void mousePress(Button *pBtn); + protected slots: + void buttonPressed_timer_timeout(); + protected: bool m_bPressed; @@ -84,6 +87,9 @@ class Button : public QWidget, public H2Core::Object, public MidiLearnable void leaveEvent(QEvent *ev); void paintEvent( QPaintEvent* ev); + QTimer *m_timer; + int m_timerTimeout; + bool loadImage( const QString& sFilename, QPixmap& pixmap ); }; From e387ed9096147e9da57dfff30cb7cc988a1367af Mon Sep 17 00:00:00 2001 From: Aurelien Leblond Date: Sun, 27 Apr 2014 19:33:21 +0100 Subject: [PATCH 05/11] Cymbal Choke --- src/core/include/hydrogen/IO/MidiInput.h | 3 ++- src/core/src/IO/midi_input.cpp | 24 ++++++++++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/core/include/hydrogen/IO/MidiInput.h b/src/core/include/hydrogen/IO/MidiInput.h index 8582e322c8..edfc5635bb 100644 --- a/src/core/include/hydrogen/IO/MidiInput.h +++ b/src/core/include/hydrogen/IO/MidiInput.h @@ -51,12 +51,13 @@ class MidiInput : public virtual Object void handleSysexMessage( const MidiMessage& msg ); void handleControlChangeMessage( const MidiMessage& msg ); void handleProgramChangeMessage( const MidiMessage& msg ); + void handlePolyphonicKeyPressureMessage( const MidiMessage& msg ); protected: bool m_bActive; void handleNoteOnMessage( const MidiMessage& msg ); - void handleNoteOffMessage( const MidiMessage& msg ); + void handleNoteOffMessage( const MidiMessage& msg, bool CymbalChoke ); private: diff --git a/src/core/src/IO/midi_input.cpp b/src/core/src/IO/midi_input.cpp index 6e2e9005b5..b4045fae34 100644 --- a/src/core/src/IO/midi_input.cpp +++ b/src/core/src/IO/midi_input.cpp @@ -93,11 +93,14 @@ void MidiInput::handleMidiMessage( const MidiMessage& msg ) break; case MidiMessage::NOTE_OFF: - handleNoteOffMessage( msg ); + handleNoteOffMessage( msg, false ); break; case MidiMessage::POLYPHONIC_KEY_PRESSURE: - ERRORLOG( "POLYPHONIC_KEY_PRESSURE event not handled yet" ); + //ERRORLOG( "POLYPHONIC_KEY_PRESSURE event not handled yet" ); + INFOLOG( QString( "[handleMidiMessage] POLYPHONIC_KEY_PRESSURE Parameter: %1, Value: %2") + .arg( msg.m_nData1 ).arg( msg.m_nData2 ) ); + handlePolyphonicKeyPressureMessage( msg ); break; case MidiMessage::CONTROL_CHANGE: @@ -206,7 +209,7 @@ void MidiInput::handleNoteOnMessage( const MidiMessage& msg ) float fVelocity = msg.m_nData2 / 127.0; if ( fVelocity == 0 ) { - handleNoteOffMessage( msg ); + handleNoteOffMessage( msg, false ); return; } @@ -283,12 +286,21 @@ void MidiInput::handleNoteOnMessage( const MidiMessage& msg ) __noteOnTick = pEngine->__getMidiRealtimeNoteTickPosition(); } +/* + EDrums (at least Roland TD-6V) uses PolyphonicKeyPressure + for cymbal choke. + If the message is 127 (choked) we send a NoteOff +*/ +void MidiInput::handlePolyphonicKeyPressureMessage( const MidiMessage& msg ) +{ + if( msg.m_nData2 == 127 ) + handleNoteOffMessage( msg, true ); +} - -void MidiInput::handleNoteOffMessage( const MidiMessage& msg ) +void MidiInput::handleNoteOffMessage( const MidiMessage& msg, bool CymbalChoke ) { // INFOLOG( "handleNoteOffMessage" ); - if ( Preferences::get_instance()->m_bMidiNoteOffIgnore ) { + if ( !CymbalChoke && Preferences::get_instance()->m_bMidiNoteOffIgnore ) { return; } From 2a497a601a0786e6ab531166a07e2e701478e572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Leblond?= Date: Tue, 29 Apr 2014 18:35:08 +0100 Subject: [PATCH 06/11] Alsa driver and Midi Aftertouch --- src/core/src/IO/alsa_midi_driver.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/core/src/IO/alsa_midi_driver.cpp b/src/core/src/IO/alsa_midi_driver.cpp index c4853ad22e..698736b1a5 100644 --- a/src/core/src/IO/alsa_midi_driver.cpp +++ b/src/core/src/IO/alsa_midi_driver.cpp @@ -250,6 +250,13 @@ void AlsaMidiDriver::midi_action( snd_seq_t *seq_handle ) msg.m_nChannel = ev->data.control.channel; break; + case SND_SEQ_EVENT_KEYPRESS: + msg.m_type = MidiMessage::POLYPHONIC_KEY_PRESSURE; + msg.m_nData1 = ev->data.note.note; + msg.m_nData2 = ev->data.note.velocity; + msg.m_nChannel = ev->data.control.channel; + break; + case SND_SEQ_EVENT_SYSEX: { msg.m_type = MidiMessage::SYSEX; snd_midi_event_t *seq_midi_parser; From 8eda90336a25c2a35a0bc0b811e965fe453f43ff Mon Sep 17 00:00:00 2001 From: Aurelien Leblond Date: Tue, 29 Apr 2014 19:20:24 +0100 Subject: [PATCH 07/11] Jack driver and Midi Aftertouch --- src/core/src/IO/jack_midi_driver.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/core/src/IO/jack_midi_driver.cpp b/src/core/src/IO/jack_midi_driver.cpp index 364a366faf..4cb5af7013 100644 --- a/src/core/src/IO/jack_midi_driver.cpp +++ b/src/core/src/IO/jack_midi_driver.cpp @@ -117,6 +117,13 @@ JackMidiDriver::JackMidiWrite(jack_nframes_t nframes) msg.m_nChannel = buffer[0] & 0xF; handleMidiMessage(msg); break; + case 0xA: /* aftertouch */ + msg.m_type = MidiMessage::POLYPHONIC_KEY_PRESSURE; + msg.m_nData1 = buffer[1]; + msg.m_nData2 = buffer[2]; + msg.m_nChannel = buffer[0] & 0xF; + handleMidiMessage(msg); + break; case 0xB: /* control change */ msg.m_type = MidiMessage::CONTROL_CHANGE; msg.m_nData1 = buffer[1]; From 4890ff7377325b3ac4ae1a37e01044e898f5a9b4 Mon Sep 17 00:00:00 2001 From: Aurelien Leblond Date: Sun, 27 Apr 2014 19:33:21 +0100 Subject: [PATCH 08/11] Cymbal Choke --- src/core/include/hydrogen/IO/MidiInput.h | 3 ++- src/core/src/IO/midi_input.cpp | 24 ++++++++++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/core/include/hydrogen/IO/MidiInput.h b/src/core/include/hydrogen/IO/MidiInput.h index 0f33ba064c..48ce18f3db 100644 --- a/src/core/include/hydrogen/IO/MidiInput.h +++ b/src/core/include/hydrogen/IO/MidiInput.h @@ -51,12 +51,13 @@ class MidiInput : public virtual Object void handleSysexMessage( const MidiMessage& msg ); void handleControlChangeMessage( const MidiMessage& msg ); void handleProgramChangeMessage( const MidiMessage& msg ); + void handlePolyphonicKeyPressureMessage( const MidiMessage& msg ); protected: bool m_bActive; void handleNoteOnMessage( const MidiMessage& msg ); - void handleNoteOffMessage( const MidiMessage& msg ); + void handleNoteOffMessage( const MidiMessage& msg, bool CymbalChoke ); private: diff --git a/src/core/src/IO/midi_input.cpp b/src/core/src/IO/midi_input.cpp index 55fb64e27b..74b18e22a8 100644 --- a/src/core/src/IO/midi_input.cpp +++ b/src/core/src/IO/midi_input.cpp @@ -92,11 +92,14 @@ void MidiInput::handleMidiMessage( const MidiMessage& msg ) break; case MidiMessage::NOTE_OFF: - handleNoteOffMessage( msg ); + handleNoteOffMessage( msg, false ); break; case MidiMessage::POLYPHONIC_KEY_PRESSURE: - ERRORLOG( "POLYPHONIC_KEY_PRESSURE event not handled yet" ); + //ERRORLOG( "POLYPHONIC_KEY_PRESSURE event not handled yet" ); + INFOLOG( QString( "[handleMidiMessage] POLYPHONIC_KEY_PRESSURE Parameter: %1, Value: %2") + .arg( msg.m_nData1 ).arg( msg.m_nData2 ) ); + handlePolyphonicKeyPressureMessage( msg ); break; case MidiMessage::CONTROL_CHANGE: @@ -202,7 +205,7 @@ void MidiInput::handleNoteOnMessage( const MidiMessage& msg ) float fVelocity = msg.m_nData2 / 127.0; if ( fVelocity == 0 ) { - handleNoteOffMessage( msg ); + handleNoteOffMessage( msg, false ); return; } @@ -254,12 +257,21 @@ void MidiInput::handleNoteOnMessage( const MidiMessage& msg ) __noteOnTick = pEngine->__getMidiRealtimeNoteTickPosition(); } +/* + EDrums (at least Roland TD-6V) uses PolyphonicKeyPressure + for cymbal choke. + If the message is 127 (choked) we send a NoteOff +*/ +void MidiInput::handlePolyphonicKeyPressureMessage( const MidiMessage& msg ) +{ + if( msg.m_nData2 == 127 ) + handleNoteOffMessage( msg, true ); +} - -void MidiInput::handleNoteOffMessage( const MidiMessage& msg ) +void MidiInput::handleNoteOffMessage( const MidiMessage& msg, bool CymbalChoke ) { // INFOLOG( "handleNoteOffMessage" ); - if ( Preferences::get_instance()->m_bMidiNoteOffIgnore ) { + if ( !CymbalChoke && Preferences::get_instance()->m_bMidiNoteOffIgnore ) { return; } From 86583c5cb5cae4679fa1a7a47da52378eea0a143 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Leblond?= Date: Tue, 29 Apr 2014 18:35:08 +0100 Subject: [PATCH 09/11] Alsa driver and Midi Aftertouch --- src/core/src/IO/alsa_midi_driver.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/core/src/IO/alsa_midi_driver.cpp b/src/core/src/IO/alsa_midi_driver.cpp index c4853ad22e..698736b1a5 100644 --- a/src/core/src/IO/alsa_midi_driver.cpp +++ b/src/core/src/IO/alsa_midi_driver.cpp @@ -250,6 +250,13 @@ void AlsaMidiDriver::midi_action( snd_seq_t *seq_handle ) msg.m_nChannel = ev->data.control.channel; break; + case SND_SEQ_EVENT_KEYPRESS: + msg.m_type = MidiMessage::POLYPHONIC_KEY_PRESSURE; + msg.m_nData1 = ev->data.note.note; + msg.m_nData2 = ev->data.note.velocity; + msg.m_nChannel = ev->data.control.channel; + break; + case SND_SEQ_EVENT_SYSEX: { msg.m_type = MidiMessage::SYSEX; snd_midi_event_t *seq_midi_parser; From b0b33dd18d5be6a0b2ae338e91648fb62e9007c2 Mon Sep 17 00:00:00 2001 From: Aurelien Leblond Date: Tue, 29 Apr 2014 19:20:24 +0100 Subject: [PATCH 10/11] Jack driver and Midi Aftertouch --- src/core/src/IO/jack_midi_driver.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/core/src/IO/jack_midi_driver.cpp b/src/core/src/IO/jack_midi_driver.cpp index 364a366faf..4cb5af7013 100644 --- a/src/core/src/IO/jack_midi_driver.cpp +++ b/src/core/src/IO/jack_midi_driver.cpp @@ -117,6 +117,13 @@ JackMidiDriver::JackMidiWrite(jack_nframes_t nframes) msg.m_nChannel = buffer[0] & 0xF; handleMidiMessage(msg); break; + case 0xA: /* aftertouch */ + msg.m_type = MidiMessage::POLYPHONIC_KEY_PRESSURE; + msg.m_nData1 = buffer[1]; + msg.m_nData2 = buffer[2]; + msg.m_nChannel = buffer[0] & 0xF; + handleMidiMessage(msg); + break; case 0xB: /* control change */ msg.m_type = MidiMessage::CONTROL_CHANGE; msg.m_nData1 = buffer[1]; From 34f641efa84383757e10787e9ffaa1888a75c242 Mon Sep 17 00:00:00 2001 From: Aurelien Leblond Date: Sat, 5 Jul 2014 17:58:04 +0100 Subject: [PATCH 11/11] git ignore for code::blocks --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 3cf3ecc3f6..8e66b1f076 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ moc_*.cc /libs/hydrogen/include/hydrogen/config.h /cmake_opts /hydrogen +*.cbp +*.layout