This patch adds a simple MIDI out feature to Hydrogen. Features:
- Each instrument has a configurable channel / note, accessible in the instrument editor - MIDI messages are sent when a sample is about to be played in the sequencer, meaning no change apart from setting the channel/note is necessary. Known Issues: - The UI is less than ideal - Note off messages are only sent right before a note on, and on stopping of playback - Samples are still associated with instruments, so there's no "pure" MIDI out. However there's nothing stopping anyone from using empty/silent samples for each instrument. - ALSA is the only driver supported for now. Once this patch is in an acceptable state adding support for other platforms / drivers should be easy. The added files are: libs/hydrogen/include/hydrogen/IO/MidiCommon.h libs/hydrogen/include/hydrogen/IO/MidiOutput.h libs/hydrogen/src/IO/midi_output.cpp I've modified the following binaries: data/img/gray/instrumentEditor/instrumentTab.png This is a first draft of this feature, so I expect there to be issues with it. Please let me know what changes are needed to make it more in line with the overall architecture, and any problems found with it. I've only tried it with a Roland SonicCell, so I don't know if it works with any other hardware, though I fully expect it to.
Index: gui/src/InstrumentEditor/InstrumentEditor.cpp =================================================================== --- gui/src/InstrumentEditor/InstrumentEditor.cpp (revision 728) +++ gui/src/InstrumentEditor/InstrumentEditor.cpp (working copy) @@ -20,7 +20,6 @@ * */ - #include <QtGui> #include <math.h> @@ -96,7 +95,63 @@ 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_pAddMidiOutChannelBtn = new Button( + m_pInstrumentProp, + "/lcd/LCDSpinBox_up_on.png", + "/lcd/LCDSpinBox_up_off.png", + "/lcd/LCDSpinBox_up_over.png", + QSize( 16, 8 ) + ); + + m_pAddMidiOutChannelBtn->move( 109, 243 ); + connect( m_pAddMidiOutChannelBtn, SIGNAL( clicked(Button*) ), this, SLOT( midiOutChannelBtnClicked(Button*) ) ); + + + m_pDelMidiOutChannelBtn = new Button( + m_pInstrumentProp, + "/lcd/LCDSpinBox_down_on.png", + "/lcd/LCDSpinBox_down_off.png", + "/lcd/LCDSpinBox_down_over.png", + QSize(16,8) + ); + m_pDelMidiOutChannelBtn->move( 109, 251 ); + 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_pAddMidiOutNoteBtn = new Button( + m_pInstrumentProp, + "/lcd/LCDSpinBox_up_on.png", + "/lcd/LCDSpinBox_up_off.png", + "/lcd/LCDSpinBox_up_over.png", + QSize( 16, 8 ) + ); + + m_pAddMidiOutNoteBtn->move( 202, 243 ); + connect( m_pAddMidiOutNoteBtn, SIGNAL( clicked(Button*) ), this, SLOT( midiOutNoteBtnClicked(Button*) ) ); + + + m_pDelMidiOutNoteBtn = new Button( + m_pInstrumentProp, + "/lcd/LCDSpinBox_down_on.png", + "/lcd/LCDSpinBox_down_off.png", + "/lcd/LCDSpinBox_down_over.png", + QSize(16,8) + ); + m_pDelMidiOutNoteBtn->move( 202, 251 ); + connect( m_pDelMidiOutNoteBtn, SIGNAL( clicked(Button*) ), this, SLOT( midiOutNoteBtnClicked(Button*) ) ); + + ///////////// + QFont boldFont; boldFont.setBold(true); m_pNameLbl->setFont( boldFont ); @@ -328,7 +383,23 @@ if (m_pInstrument->get_mute_group() == -1 ) { sMuteGroup = "Off"; } - m_pMuteGroupLCD->setText( sMuteGroup ); + m_pMuteGroupLCD->setText( sMuteGroup ); + + // midi out + QString sMidiOutChannel = to_string( 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 ); + } // select the last valid layer for (int i = MAX_LAYERS - 1; i >= 0; i-- ) { @@ -681,3 +752,32 @@ selectedInstrumentChangedEvent(); // force an update } +void InstrumentEditor::midiOutChannelBtnClicked(Button *pRef) +{ + assert( m_pInstrument ); + + if (pRef == m_pAddMidiOutChannelBtn ) { + m_pInstrument->set_midi_out_channel( m_pInstrument->get_midi_out_channel() + 1); + } + else if (pRef == m_pDelMidiOutChannelBtn ) { + m_pInstrument->set_midi_out_channel( m_pInstrument->get_midi_out_channel() - 1); + } + + selectedInstrumentChangedEvent(); // force an update +} + +void InstrumentEditor::midiOutNoteBtnClicked(Button *pRef) +{ + assert( m_pInstrument ); + + if (pRef == m_pAddMidiOutNoteBtn ) { + m_pInstrument->set_midi_out_note( m_pInstrument->get_midi_out_note() + 1); + } + else if (pRef == m_pDelMidiOutNoteBtn ) { + m_pInstrument->set_midi_out_note( m_pInstrument->get_midi_out_note() - 1); + } + + selectedInstrumentChangedEvent(); // force an update +} + + Index: gui/src/InstrumentEditor/InstrumentEditor.h =================================================================== --- gui/src/InstrumentEditor/InstrumentEditor.h (revision 728) +++ gui/src/InstrumentEditor/InstrumentEditor.h (working copy) @@ -64,6 +64,8 @@ void labelClicked( ClickableLabel* pRef ); void muteGroupBtnClicked(Button *pRef); + void midiOutChannelBtnClicked(Button *pRef); + void midiOutNoteBtnClicked(Button *pRef); private: H2Core::Instrument *m_pInstrument; @@ -99,6 +101,15 @@ 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; //~ Instrument properties Index: gui/src/widgets/LCD.cpp =================================================================== --- gui/src/widgets/LCD.cpp (revision 728) +++ gui/src/widgets/LCD.cpp (working copy) @@ -161,9 +161,9 @@ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' ', ' ', - '-', ':', '/', '\\', ',', ';', '.' + '-', ':', '/', '\\', ',', ';', '.', ' ', ' ', ' ', '#' }; - for ( int n = 0; n < 73; n++ ) { + for ( int n = 0; n < 77; n++ ) { //73 if ( keymap[ n ] == ch ) { m_nCol = n % MAXCOL; m_nRow = n / MAXCOL; Index: libs/hydrogen/include/hydrogen/instrument.h =================================================================== --- libs/hydrogen/include/hydrogen/instrument.h (revision 728) +++ libs/hydrogen/include/hydrogen/instrument.h (working copy) @@ -157,7 +157,22 @@ inline int get_mute_group() { return __mute_group; } - + void set_midi_out_channel( int channel ) { + if ((channel > -2) && (channel < 16)) { + __midi_out_channel = channel; + } + } + inline int get_midi_out_channel() { + return __midi_out_channel; + } + void set_midi_out_note( int note ) { + if ((note >= 0) && (note < 128)) { + __midi_out_note = note; + } + } + inline int get_midi_out_note() { + return __midi_out_note; + } void set_muted( bool muted ) { __muted = muted; } @@ -302,6 +317,8 @@ QString __drumkit_name; ///< Drumkit name bool __filter_active; ///< Is filter active? int __mute_group; ///< Mute group + int __midi_out_channel; ///< Midi out channel + int __midi_out_note; ///< Midi out note bool __active; ///< is the instrument active? bool __soloed; Index: libs/hydrogen/include/hydrogen/hydrogen.h =================================================================== --- libs/hydrogen/include/hydrogen/hydrogen.h (revision 728) +++ libs/hydrogen/include/hydrogen/hydrogen.h (working copy) @@ -28,6 +28,7 @@ #include <hydrogen/Object.h> #include <hydrogen/IO/AudioOutput.h> #include <hydrogen/IO/MidiInput.h> +#include <hydrogen/IO/MidiOutput.h> #include <hydrogen/SoundLibrary.h> @@ -117,6 +118,7 @@ AudioOutput* getAudioOutput(); MidiInput* getMidiInput(); + MidiOutput* getMidiOutput(); int getState(); Index: libs/hydrogen/include/hydrogen/IO/MidiInput.h =================================================================== --- libs/hydrogen/include/hydrogen/IO/MidiInput.h (revision 728) +++ libs/hydrogen/include/hydrogen/IO/MidiInput.h (working copy) @@ -26,59 +26,15 @@ #include <hydrogen/Object.h> #include <string> #include <vector> +#include "MidiCommon.h" namespace H2Core { -class MidiMessage -{ -public: - enum MidiMessageType { - UNKNOWN, - SYSEX, - NOTE_ON, - NOTE_OFF, - POLYPHONIC_KEY_PRESSURE, - CONTROL_CHANGE, - PROGRAM_CHANGE, - CHANNEL_PRESSURE, - PITCH_WHEEL, - SYSTEM_EXCLUSIVE, - START, - CONTINUE, - STOP, - SONG_POS, - QUARTER_FRAME - }; - - MidiMessageType m_type; - int m_nData1; - int m_nData2; - int m_nChannel; - std::vector<unsigned char> m_sysexData; - - MidiMessage() - : m_type( UNKNOWN ) - , m_nData1( -1 ) - , m_nData2( -1 ) - , m_nChannel( -1 ) {} -}; - - - -class MidiPortInfo -{ -public: - QString m_sName; - int m_nClient; - int m_nPort; -}; - - /** * MIDI input base class */ -class MidiInput : public Object +class MidiInput : public virtual Object { public: MidiInput( const QString class_name ); Index: libs/hydrogen/src/local_file_mgr.cpp =================================================================== --- libs/hydrogen/src/local_file_mgr.cpp (revision 728) +++ libs/hydrogen/src/local_file_mgr.cpp (working copy) @@ -685,8 +685,12 @@ float fSustain = LocalFileMng::readXmlFloat( instrumentNode, "Sustain", 1.0, false, false ); // Sustain float fRelease = LocalFileMng::readXmlFloat( instrumentNode, "Release", 1000, false, false ); // Release float fGain = readXmlFloat( instrumentNode, "gain", 1.0f, false, false ); - QString sMuteGroup = readXmlString( instrumentNode, "muteGroup", "-1", false, false ); + QString sMuteGroup = readXmlString( instrumentNode, "muteGroup", "-1", false, false ); + QString sMidiOutChannel = readXmlString( instrumentNode, "midiOutChannel", "-1", false, false ); + QString sMidiOutNote = readXmlString( instrumentNode, "midiOutNote", "60", false, false ); int nMuteGroup = sMuteGroup.toInt(); + int nMidiOutChannel = sMidiOutChannel.toInt(); + int nMidiOutNote = sMidiOutNote.toInt(); // some sanity checks if ( id == "" ) { @@ -743,6 +747,8 @@ pInstrument->set_drumkit_name( drumkitInfo->getName() ); pInstrument->set_gain( fGain ); pInstrument->set_mute_group( nMuteGroup ); + pInstrument->set_midi_out_channel( nMidiOutChannel ); + pInstrument->set_midi_out_note( nMidiOutNote ); pInstrument->set_adsr( new ADSR( fAttack, fDecay, fSustain, fRelease ) ); instrumentList->add( pInstrument ); @@ -833,6 +839,9 @@ LocalFileMng::writeXmlString( &instrumentNode, "Release", to_string( instr->get_adsr()->__release ) ); LocalFileMng::writeXmlString( &instrumentNode, "muteGroup", to_string( instr->get_mute_group() ) ); + + LocalFileMng::writeXmlString( &instrumentNode, "midiOutChannel", to_string( instr->get_midi_out_channel() ) ); + LocalFileMng::writeXmlString( &instrumentNode, "midiOutNote", to_string( instr->get_midi_out_note() ) ); for ( unsigned nLayer = 0; nLayer < MAX_LAYERS; nLayer++ ) { InstrumentLayer *pLayer = instr->get_layer( nLayer ); @@ -1159,6 +1168,9 @@ LocalFileMng::writeXmlString( &instrumentNode, "randomPitchFactor", to_string( instr->get_random_pitch_factor() ) ); LocalFileMng::writeXmlString( &instrumentNode, "muteGroup", to_string( instr->get_mute_group() ) ); + + LocalFileMng::writeXmlString( &instrumentNode, "midiOutChannel", to_string( instr->get_midi_out_channel() ) ); + LocalFileMng::writeXmlString( &instrumentNode, "midiOutNote", to_string( instr->get_midi_out_note() ) ); for ( unsigned nLayer = 0; nLayer < MAX_LAYERS; nLayer++ ) { InstrumentLayer *pLayer = instr->get_layer( nLayer ); Index: libs/hydrogen/src/song.cpp =================================================================== --- libs/hydrogen/src/song.cpp (revision 728) +++ libs/hydrogen/src/song.cpp (working copy) @@ -293,9 +293,12 @@ float fFilterCutoff = LocalFileMng::readXmlFloat( instrumentNode, "filterCutoff", 1.0f, false ); float fFilterResonance = LocalFileMng::readXmlFloat( instrumentNode, "filterResonance", 0.0f, false ); QString sMuteGroup = LocalFileMng::readXmlString( instrumentNode, "muteGroup", "-1", false ); + QString sMidiOutChannel = LocalFileMng::readXmlString( instrumentNode, "midiOutChannel", "-1", false, false ); + QString sMidiOutNote = LocalFileMng::readXmlString( instrumentNode, "midiOutNote", "60", false, false ); int nMuteGroup = sMuteGroup.toInt(); + int nMidiOutChannel = sMidiOutChannel.toInt(); + int nMidiOutNote = sMidiOutNote.toInt(); - if ( sId == "" ) { ERRORLOG( "Empty ID for instrument '" + sName + "'. skipping." ); continue; @@ -319,6 +322,8 @@ pInstrument->set_filter_resonance( fFilterResonance ); pInstrument->set_gain( fGain ); pInstrument->set_mute_group( nMuteGroup ); + pInstrument->set_midi_out_channel( nMidiOutChannel ); + pInstrument->set_midi_out_note( nMidiOutNote ); QString drumkitPath = ""; if ( ( sDrumkit != "" ) && ( sDrumkit != "-" ) ) { Index: libs/hydrogen/src/instrument.cpp =================================================================== --- libs/hydrogen/src/instrument.cpp (revision 728) +++ libs/hydrogen/src/instrument.cpp (working copy) @@ -55,6 +55,8 @@ , __gain( 1.0 ) , __drumkit_name( "" ) , __mute_group( -1 ) + , __midi_out_channel( -1 ) + , __midi_out_note( 60 ) , __stop_notes( false ) { for ( unsigned nFX = 0; nFX < MAX_FX; ++nFX ) { Index: libs/hydrogen/src/hydrogen.cpp =================================================================== --- libs/hydrogen/src/hydrogen.cpp (revision 728) +++ libs/hydrogen/src/hydrogen.cpp (working copy) @@ -55,6 +55,7 @@ #include <hydrogen/IO/JackOutput.h> #include <hydrogen/IO/NullDriver.h> #include <hydrogen/IO/MidiInput.h> +#include <hydrogen/IO/MidiOutput.h> #include <hydrogen/IO/CoreMidiDriver.h> #include <hydrogen/IO/TransportInfo.h> #include <hydrogen/Preferences.h> @@ -109,6 +110,7 @@ AudioOutput *m_pAudioDriver = NULL; ///< Audio output MidiInput *m_pMidiDriver = NULL; ///< MIDI input +MidiOutput *m_pMidiDriverOut = NULL; ///< MIDI output // overload the the > operator of Note objects for priority_queue bool operator> (const Note& pNote1, const Note &pNote2) { @@ -630,7 +632,6 @@ case TransportInfo::STOPPED: - if ( m_audioEngineState == STATE_PLAYING ) { audioEngine_stop( false ); // no engine lock } @@ -1141,6 +1142,7 @@ &m_nPatternStartTick ); } else { _INFOLOG( "End of Song" ); + Hydrogen::get_instance()->getMidiOutput()->handleQueueAllNoteOff(); return -1; } } @@ -1563,7 +1565,9 @@ if ( preferencesMng->m_sMidiDriver == "ALSA" ) { #ifdef ALSA_SUPPORT // Create MIDI driver - m_pMidiDriver = new AlsaMidiDriver(); + AlsaMidiDriver *alsaMidiDriver = new AlsaMidiDriver(); + m_pMidiDriverOut = alsaMidiDriver; + m_pMidiDriver = alsaMidiDriver; m_pMidiDriver->open(); m_pMidiDriver->setActive( true ); #endif @@ -1762,6 +1766,7 @@ /// Stop the internal sequencer void Hydrogen::sequencer_stop() { + Hydrogen::get_instance()->getMidiOutput()->handleQueueAllNoteOff(); m_pAudioDriver->stop(); } @@ -2157,8 +2162,13 @@ return m_pMidiDriver; } +MidiOutput* Hydrogen::getMidiOutput() +{ + return m_pMidiDriverOut; +} + void Hydrogen::setMasterPeak_L( float value ) { m_fMasterPeak_L = value; Index: libs/hydrogen/src/sampler/sampler.cpp =================================================================== --- libs/hydrogen/src/sampler/sampler.cpp (revision 728) +++ libs/hydrogen/src/sampler/sampler.cpp (working copy) @@ -163,6 +163,8 @@ pInstr->enqueue(); __playing_notes_queue.push_back( note ); + + Hydrogen::get_instance()->getMidiOutput()->handleQueueNote(note); } Index: libs/hydrogen/src/IO/AlsaMidiDriver.h =================================================================== --- libs/hydrogen/src/IO/AlsaMidiDriver.h (revision 728) +++ libs/hydrogen/src/IO/AlsaMidiDriver.h (working copy) @@ -26,6 +26,7 @@ #ifdef ALSA_SUPPORT #include <hydrogen/IO/MidiInput.h> +#include <hydrogen/IO/MidiOutput.h> #include <alsa/asoundlib.h> #include <string> @@ -38,11 +39,11 @@ /// Alsa Midi Driver /// Based on Matthias Nagorni alsa sequencer example /// -class AlsaMidiDriver : public MidiInput +class AlsaMidiDriver : public virtual MidiInput, public virtual MidiOutput { public: AlsaMidiDriver(); - ~AlsaMidiDriver(); + virtual ~AlsaMidiDriver(); virtual void open(); virtual void close(); @@ -50,8 +51,9 @@ void midi_action( snd_seq_t *seq_handle ); void getPortInfo( const QString& sPortName, int& nClient, int& nPort ); + virtual void handleQueueNote(Note* pNote); + virtual void handleQueueAllNoteOff(); - private: }; Index: libs/hydrogen/src/IO/alsa_midi_driver.cpp =================================================================== --- libs/hydrogen/src/IO/alsa_midi_driver.cpp (revision 728) +++ libs/hydrogen/src/IO/alsa_midi_driver.cpp (working copy) @@ -31,6 +31,8 @@ #include <hydrogen/event_queue.h> #include <pthread.h> +#include <hydrogen/note.h> +#include <hydrogen/instrument.h> #ifdef LASH_SUPPORT #include <hydrogen/LashClient.h> @@ -50,6 +52,7 @@ struct pollfd *pfd; int portId; int clientId; +int outPortId; void* alsaMidiDriver_thread( void* param ) @@ -80,6 +83,18 @@ _ERRORLOG( "Error creating sequencer port." ); pthread_exit( NULL ); } + + if ( ( outPortId = snd_seq_create_simple_port( seq_handle, + "Hydrogen Midi-Out", + SND_SEQ_PORT_CAP_READ | + SND_SEQ_PORT_CAP_SUBS_READ, + SND_SEQ_PORT_TYPE_APPLICATION + ) + ) < 0 ) { + _ERRORLOG( "Error creating sequencer port." ); + pthread_exit( NULL ); + } + clientId = snd_seq_client_id( seq_handle ); #ifdef LASH_SUPPORT @@ -150,7 +165,7 @@ AlsaMidiDriver::AlsaMidiDriver() - : MidiInput( "AlsaMidiDriver" ) + : MidiInput( "AlsaMidiDriver" ), MidiOutput( "AlsaMidiDriver" ), Object( "AlsaMidiDriver" ) { // infoLog("INIT"); } @@ -166,9 +181,6 @@ // infoLog("DESTROY"); } - - - void AlsaMidiDriver::open() { // start main thread @@ -413,6 +425,79 @@ ERRORLOG( "Midi port " + sPortName + " not found" ); } +void AlsaMidiDriver::handleQueueNote(Note* pNote) +{ + if ( seq_handle == NULL ) { + ERRORLOG( "seq_handle = NULL " ); + return; + } + + int channel = pNote->get_instrument()->get_midi_out_channel(); + if (channel < 0) { + return; + } + + int key = pNote->get_instrument()->get_midi_out_note(); + int velocity = pNote->get_velocity() * 127; + + snd_seq_event_t ev; + + //Note off + snd_seq_ev_clear(&ev); + snd_seq_ev_set_source(&ev, outPortId); + snd_seq_ev_set_subs(&ev); + snd_seq_ev_set_direct(&ev); + snd_seq_ev_set_noteoff(&ev, channel, key, velocity); + snd_seq_event_output(seq_handle, &ev); + snd_seq_drain_output(seq_handle); + + //Note on + //snd_seq_event_input(seq_handle, &ev); + snd_seq_ev_clear(&ev); + snd_seq_ev_set_source(&ev, outPortId); + snd_seq_ev_set_subs(&ev); + snd_seq_ev_set_direct(&ev); + //snd_seq_event_output_direct( seq_handle, ev ); + + snd_seq_ev_set_noteon(&ev, channel, key, velocity); + snd_seq_event_output(seq_handle, &ev); + + //snd_seq_free_event(ev); + snd_seq_drain_output(seq_handle); +} + +void AlsaMidiDriver::handleQueueAllNoteOff() +{ + if ( seq_handle == NULL ) { + ERRORLOG( "seq_handle = NULL " ); + return; + } + + InstrumentList *instList = Hydrogen::get_instance()->getSong()->get_instrument_list(); + + unsigned int numInstruments = instList->get_size(); + for (int index = 0; index < numInstruments; ++index) { + Instrument *curInst = instList->get(index); + + int channel = curInst->get_midi_out_channel(); + if (channel < 0) { + continue; + } + int key = curInst->get_midi_out_note(); + + snd_seq_event_t ev; + + //Note off + snd_seq_ev_clear(&ev); + snd_seq_ev_set_source(&ev, outPortId); + snd_seq_ev_set_subs(&ev); + snd_seq_ev_set_direct(&ev); + snd_seq_ev_set_noteoff(&ev, channel, key, 0); + snd_seq_event_output(seq_handle, &ev); + snd_seq_drain_output(seq_handle); + } +} + }; #endif // ALSA_SUPPORT
<<attachment: instrumentTab.png>>
/* * Hydrogen * Copyright(c) 2002-2008 by Alex >Comix< Cominu [co...@users.sourceforge.net] * * http://www.hydrogen-music.org * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY, without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef H2_MIDI_COMMON_H #define H2_MIDI_COMMON_H #include <hydrogen/Object.h> #include <string> #include <vector> namespace H2Core { class MidiMessage { public: enum MidiMessageType { UNKNOWN, SYSEX, NOTE_ON, NOTE_OFF, POLYPHONIC_KEY_PRESSURE, CONTROL_CHANGE, PROGRAM_CHANGE, CHANNEL_PRESSURE, PITCH_WHEEL, SYSTEM_EXCLUSIVE, START, CONTINUE, STOP, SONG_POS, QUARTER_FRAME }; MidiMessageType m_type; int m_nData1; int m_nData2; int m_nChannel; std::vector<unsigned char> m_sysexData; MidiMessage() : m_type( UNKNOWN ) , m_nData1( -1 ) , m_nData2( -1 ) , m_nChannel( -1 ) {} }; class MidiPortInfo { public: QString m_sName; int m_nClient; int m_nPort; }; }; #endif
/* * Hydrogen * Copyright(c) 2002-2008 by Alex >Comix< Cominu [co...@users.sourceforge.net] * * http://www.hydrogen-music.org * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY, without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef H2_MIDI_OUTPUT_H #define H2_MIDI_OUTPUT_H #include <hydrogen/Object.h> #include <string> #include <vector> #include "MidiCommon.h" namespace H2Core { class Note; /** * MIDI input base class */ class MidiOutput : public virtual Object { public: MidiOutput( const QString class_name ); virtual ~MidiOutput(); virtual void handleQueueNote(Note* pNote) = 0; virtual void handleQueueAllNoteOff() = 0; //protected: // std::vector<MidiMessage> m_pendingMessages; }; }; #endif
/* * Hydrogen * Copyright(c) 2002-2008 by Alex >Comix< Cominu [co...@users.sourceforge.net] * * http://www.hydrogen-music.org * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY, without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include <hydrogen/IO/MidiOutput.h> #include <hydrogen/event_queue.h> #include <hydrogen/Preferences.h> #include <hydrogen/hydrogen.h> #include <hydrogen/instrument.h> #include <hydrogen/note.h> #include <hydrogen/action.h> #include <hydrogen/midiMap.h> namespace H2Core { MidiOutput::MidiOutput( const QString class_name ) : Object( class_name ) { //INFOLOG( "INIT" ); } MidiOutput::~MidiOutput() { //INFOLOG( "DESTROY" ); } /* void MidiOutput::queueMidiMessage( const MidiMessage& msg ) { m_pendingMessages.push_back(msg); } bool MidiOutput::hasQueuedMessages() { return !m_pendingMessages.empty(); } void MidiOutput::processQueuedMessages() { /// Do something... } */ };
------------------------------------------------------------------------------ This SF.net email is sponsored by: SourcForge Community SourceForge wants to tell your story. http://p.sf.net/sfu/sf-spreadtheword
_______________________________________________ Hydrogen-devel mailing list Hydrogen-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/hydrogen-devel