| 1 | /* |
|---|
| 2 | * Hydrogen |
|---|
| 3 | * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net] |
|---|
| 4 | * |
|---|
| 5 | * http://www.hydrogen-music.org |
|---|
| 6 | * |
|---|
| 7 | * This program is free software; you can redistribute it and/or modify |
|---|
| 8 | * it under the terms of the GNU General Public License as published by |
|---|
| 9 | * the Free Software Foundation; either version 2 of the License, or |
|---|
| 10 | * (at your option) any later version. |
|---|
| 11 | * |
|---|
| 12 | * This program is distributed in the hope that it will be useful, |
|---|
| 13 | * but WITHOUT ANY WARRANTY, without even the implied warranty of |
|---|
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 15 | * GNU General Public License for more details. |
|---|
| 16 | * |
|---|
| 17 | * You should have received a copy of the GNU General Public License |
|---|
| 18 | * along with this program; if not, write to the Free Software |
|---|
| 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|---|
| 20 | * |
|---|
| 21 | */ |
|---|
| 22 | |
|---|
| 23 | #include "PortMidiDriver.h" |
|---|
| 24 | |
|---|
| 25 | #include <hydrogen/Preferences.h> |
|---|
| 26 | |
|---|
| 27 | #ifdef WIN32 |
|---|
| 28 | #include <windows.h> |
|---|
| 29 | #endif |
|---|
| 30 | |
|---|
| 31 | #ifdef PORTMIDI_SUPPORT |
|---|
| 32 | |
|---|
| 33 | #include <porttime.h> |
|---|
| 34 | #define TIME_PROC ((long (*)(void *)) Pt_Time) |
|---|
| 35 | #define TIME_START Pt_Start(1, 0, 0) /* timer started w/millisecond accuracy */ |
|---|
| 36 | |
|---|
| 37 | #include <pthread.h> |
|---|
| 38 | |
|---|
| 39 | namespace H2Core |
|---|
| 40 | { |
|---|
| 41 | |
|---|
| 42 | pthread_t PortMidiDriverThread; |
|---|
| 43 | |
|---|
| 44 | void* PortMidiDriver_thread( void* param ) |
|---|
| 45 | { |
|---|
| 46 | PortMidiDriver *instance = ( PortMidiDriver* )param; |
|---|
| 47 | _INFOLOG( "PortMidiDriver_thread starting" ); |
|---|
| 48 | |
|---|
| 49 | PmError status; |
|---|
| 50 | PmError length; |
|---|
| 51 | PmEvent buffer[1]; |
|---|
| 52 | while ( instance->m_bRunning ) { |
|---|
| 53 | status = Pm_Poll( instance->m_pMidiIn ); |
|---|
| 54 | if ( status == TRUE ) { |
|---|
| 55 | length = Pm_Read( instance->m_pMidiIn, buffer, 1 ); |
|---|
| 56 | if ( length > 0 ) { |
|---|
| 57 | MidiMessage msg; |
|---|
| 58 | |
|---|
| 59 | int nEventType = Pm_MessageStatus( buffer[0].message ); |
|---|
| 60 | if ( ( nEventType >= 128 ) && ( nEventType < 144 ) ) { // note off |
|---|
| 61 | msg.m_nChannel = nEventType - 128; |
|---|
| 62 | msg.m_type = MidiMessage::NOTE_OFF; |
|---|
| 63 | } else if ( ( nEventType >= 144 ) && ( nEventType < 160 ) ) { // note on |
|---|
| 64 | msg.m_nChannel = nEventType - 144; |
|---|
| 65 | msg.m_type = MidiMessage::NOTE_ON; |
|---|
| 66 | } else if ( ( nEventType >= 160 ) && ( nEventType < 176 ) ) { // Polyphonic Key Pressure (After-touch) |
|---|
| 67 | msg.m_nChannel = nEventType - 160; |
|---|
| 68 | msg.m_type = MidiMessage::POLYPHONIC_KEY_PRESSURE; |
|---|
| 69 | } else if ( ( nEventType >= 176 ) && ( nEventType < 192 ) ) { // Control Change |
|---|
| 70 | msg.m_nChannel = nEventType - 176; |
|---|
| 71 | msg.m_type = MidiMessage::CONTROL_CHANGE; |
|---|
| 72 | } else if ( ( nEventType >= 192 ) && ( nEventType < 208 ) ) { // Program Change |
|---|
| 73 | msg.m_nChannel = nEventType - 192; |
|---|
| 74 | msg.m_type = MidiMessage::PROGRAM_CHANGE; |
|---|
| 75 | } else if ( ( nEventType >= 208 ) && ( nEventType < 224 ) ) { // Channel Pressure (After-touch) |
|---|
| 76 | msg.m_nChannel = nEventType - 208; |
|---|
| 77 | msg.m_type = MidiMessage::CHANNEL_PRESSURE; |
|---|
| 78 | } else if ( ( nEventType >= 224 ) && ( nEventType < 240 ) ) { // Pitch Wheel Change |
|---|
| 79 | msg.m_nChannel = nEventType - 224; |
|---|
| 80 | msg.m_type = MidiMessage::PITCH_WHEEL; |
|---|
| 81 | } else if ( ( nEventType >= 240 ) && ( nEventType < 256 ) ) { // System Exclusive |
|---|
| 82 | msg.m_nChannel = nEventType - 240; |
|---|
| 83 | msg.m_type = MidiMessage::SYSTEM_EXCLUSIVE; |
|---|
| 84 | } else { |
|---|
| 85 | _ERRORLOG( "Unhandled midi message type: " + QString::number( nEventType ) ); |
|---|
| 86 | _INFOLOG( "MIDI msg: " ); |
|---|
| 87 | _INFOLOG( QString::number( buffer[0].timestamp ) ); |
|---|
| 88 | _INFOLOG( QString::number( Pm_MessageStatus( buffer[0].message ) ) ); |
|---|
| 89 | _INFOLOG( QString::number( Pm_MessageData1( buffer[0].message ) ) ); |
|---|
| 90 | _INFOLOG( QString::number( Pm_MessageData2( buffer[0].message ) ) ); |
|---|
| 91 | } |
|---|
| 92 | |
|---|
| 93 | msg.m_nData1 = Pm_MessageData1( buffer[0].message ); |
|---|
| 94 | msg.m_nData2 = Pm_MessageData2( buffer[0].message ); |
|---|
| 95 | |
|---|
| 96 | instance->handleMidiMessage( msg ); |
|---|
| 97 | } |
|---|
| 98 | } else { |
|---|
| 99 | #ifdef WIN32 |
|---|
| 100 | Sleep( 1 ); |
|---|
| 101 | #else |
|---|
| 102 | usleep( 100 ); |
|---|
| 103 | #endif |
|---|
| 104 | } |
|---|
| 105 | } |
|---|
| 106 | |
|---|
| 107 | |
|---|
| 108 | |
|---|
| 109 | _INFOLOG( "MIDI Thread DESTROY" ); |
|---|
| 110 | pthread_exit( NULL ); |
|---|
| 111 | return NULL; |
|---|
| 112 | } |
|---|
| 113 | |
|---|
| 114 | |
|---|
| 115 | PortMidiDriver::PortMidiDriver() |
|---|
| 116 | : MidiInput( "PortMidiDriver" ) |
|---|
| 117 | , m_bRunning( false ) |
|---|
| 118 | { |
|---|
| 119 | Pm_Initialize(); |
|---|
| 120 | } |
|---|
| 121 | |
|---|
| 122 | |
|---|
| 123 | PortMidiDriver::~PortMidiDriver() |
|---|
| 124 | { |
|---|
| 125 | Pm_Terminate(); |
|---|
| 126 | } |
|---|
| 127 | |
|---|
| 128 | |
|---|
| 129 | |
|---|
| 130 | |
|---|
| 131 | void PortMidiDriver::open() |
|---|
| 132 | { |
|---|
| 133 | INFOLOG( "[open]" ); |
|---|
| 134 | |
|---|
| 135 | int nInputBufferSize = 100; |
|---|
| 136 | |
|---|
| 137 | int nDeviceId = -1; |
|---|
| 138 | QString sMidiPortName = Preferences::getInstance()->m_sMidiPortName; |
|---|
| 139 | int nDevices = Pm_CountDevices(); |
|---|
| 140 | for ( int i = 0; i < nDevices; i++ ) { |
|---|
| 141 | const PmDeviceInfo *info = Pm_GetDeviceInfo( i ); |
|---|
| 142 | if ( info == NULL ) { |
|---|
| 143 | ERRORLOG( "Could not open input device" ); |
|---|
| 144 | } |
|---|
| 145 | |
|---|
| 146 | if ( info->input == TRUE ) { |
|---|
| 147 | if ( info->name == sMidiPortName.toStdString() ) { |
|---|
| 148 | nDeviceId = i; |
|---|
| 149 | } |
|---|
| 150 | } |
|---|
| 151 | } |
|---|
| 152 | |
|---|
| 153 | if ( nDeviceId == -1 ) { |
|---|
| 154 | INFOLOG( "Midi input device not found." ); |
|---|
| 155 | return; |
|---|
| 156 | } |
|---|
| 157 | |
|---|
| 158 | const PmDeviceInfo *info = Pm_GetDeviceInfo( nDeviceId ); |
|---|
| 159 | if ( info == NULL ) { |
|---|
| 160 | ERRORLOG( "Error opening midi input device" ); |
|---|
| 161 | } |
|---|
| 162 | |
|---|
| 163 | //INFOLOG( string( "Device: " ).append( info->interf ).append( " " ).append( info->name ) ); |
|---|
| 164 | TIME_START; |
|---|
| 165 | |
|---|
| 166 | PmError err = Pm_OpenInput( |
|---|
| 167 | &m_pMidiIn, |
|---|
| 168 | nDeviceId, |
|---|
| 169 | NULL, |
|---|
| 170 | nInputBufferSize, |
|---|
| 171 | TIME_PROC, |
|---|
| 172 | NULL |
|---|
| 173 | ); |
|---|
| 174 | |
|---|
| 175 | if ( err != pmNoError ) { |
|---|
| 176 | ERRORLOG( "Error in Pm_OpenInput" ); |
|---|
| 177 | } |
|---|
| 178 | |
|---|
| 179 | m_bRunning = true; |
|---|
| 180 | |
|---|
| 181 | pthread_attr_t attr; |
|---|
| 182 | pthread_attr_init( &attr ); |
|---|
| 183 | pthread_create( &PortMidiDriverThread, &attr, PortMidiDriver_thread, ( void* )this ); |
|---|
| 184 | } |
|---|
| 185 | |
|---|
| 186 | |
|---|
| 187 | void PortMidiDriver::close() |
|---|
| 188 | { |
|---|
| 189 | INFOLOG( "[close]" ); |
|---|
| 190 | if ( m_bRunning ) { |
|---|
| 191 | m_bRunning = false; |
|---|
| 192 | pthread_join( PortMidiDriverThread, NULL ); |
|---|
| 193 | PmError err = Pm_Close( m_pMidiIn ); |
|---|
| 194 | if ( err != pmNoError ) { |
|---|
| 195 | ERRORLOG( "Error in Pm_OpenInput" ); |
|---|
| 196 | } |
|---|
| 197 | } |
|---|
| 198 | } |
|---|
| 199 | |
|---|
| 200 | |
|---|
| 201 | |
|---|
| 202 | std::vector<QString> PortMidiDriver::getOutputPortList() |
|---|
| 203 | { |
|---|
| 204 | std::vector<QString> portList; |
|---|
| 205 | |
|---|
| 206 | int nDevices = Pm_CountDevices(); |
|---|
| 207 | for ( int i = 0; i < nDevices; i++ ) { |
|---|
| 208 | const PmDeviceInfo *info = Pm_GetDeviceInfo( i ); |
|---|
| 209 | if ( info == NULL ) { |
|---|
| 210 | ERRORLOG( "Could not open input device" ); |
|---|
| 211 | } |
|---|
| 212 | |
|---|
| 213 | if ( info->input == TRUE ) { |
|---|
| 214 | INFOLOG( info->name ); |
|---|
| 215 | portList.push_back( info->name ); |
|---|
| 216 | } |
|---|
| 217 | } |
|---|
| 218 | |
|---|
| 219 | return portList; |
|---|
| 220 | } |
|---|
| 221 | |
|---|
| 222 | }; |
|---|
| 223 | |
|---|
| 224 | #endif // PORTMIDI_SUPPORT |
|---|