/midi_data.cpp
C++ | 382 lines | 240 code | 80 blank | 62 comment | 18 complexity | 2bca88ddee194e5e959dd84fec06ec5d MD5 | raw file
1/*************************************************************************** 2 * Copyright (C) 2008 by Steve Merrony * 3 * * 4 * * 5 * This program is free software; you can redistribute it and/or modify * 6 * it under the terms of the GNU General Public License as published by * 7 * the Free Software Foundation; either version 3 of the License, or * 8 * (at your option) any later version. * 9 * * 10 * This program is distributed in the hope that it will be useful, * 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 * GNU General Public License for more details. * 14 * * 15 * You should have received a copy of the GNU General Public License * 16 * along with this program; if not, write to the * 17 * Free Software Foundation, Inc., * 18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 19 ***************************************************************************/ 20#include <cstdlib> 21#include <iostream> 22using namespace std; 23 24#include <QDataStream> 25#include <QFile> 26#include <QSettings> 27#include <QString> 28 29#include "midi_data.h" 30 31// Platform-dependent sleep routines. 32#if defined(__WINDOWS_MM__) 33 #include <windows.h> 34 #define SLEEP( milliseconds ) Sleep( (DWORD) milliseconds ) 35#else // Unix variants 36 #include <unistd.h> 37 #define SLEEP( milliseconds ) usleep( (unsigned long) (milliseconds * 1000.0) ) 38#endif 39 40midi_data::midi_data() { 41 verboseMode = false; 42 createOurMIDIports(); 43 last_patch_loaded = -1; 44 connectedInPort = -1; 45 connectedOutPort = -1; 46} 47 48 49midi_data::~midi_data() { 50 51 delete midiIn; 52 delete midiOut; 53} 54 55void midi_data::createOurMIDIports() { 56 57 midiIn = 0; 58 midiOut = 0; 59 60 try { 61 midiIn = new RtMidiIn(); 62 } 63 catch ( RtError &error ) { 64 error.printMessage(); 65 exit( EXIT_FAILURE ); 66 } 67 68 // Do(n't) ignore sysex, timing, or active sensing messages. 69 midiIn->ignoreTypes( false, true, true ); // FIXME - should just be line 330? 70 71 72 try { 73 midiOut = new RtMidiOut(); 74 } 75 catch ( RtError &error ) { 76 error.printMessage(); 77 exit( EXIT_FAILURE ); 78 } 79 80} 81 82void midi_data::sendPanic() { 83 84 for (int mc = 0; mc < 16; mc++) { 85 86 //snd_seq_ev_set_controller (&ev, mc, MIDI_CTL_ALL_NOTES_OFF,0); 87 88 } 89} 90 91/** 92 * Requests a single patch from the EWI and waits for one to be returned. 93 * @param p 94 * @return 95 */ 96bool midi_data::requestPatch ( unsigned char p) { 97 98 std::vector<unsigned char> message; 99 100 if (p >= EWI_NUM_PATCHES) { 101 cerr << "Illegal request for patch - aborting\n"; 102 exit(1); 103 } 104 105 //char sysex_fetch_patch[] = { 0xf0, 0x47, 0x64, 0x00, 0x40, 0x00, 0xf7 }; // 6th byte is patch # 106 message.push_back( MIDI_SYSEX_HEADER ); 107 message.push_back( MIDI_SYSEX_AKAI_ID ); 108 message.push_back( MIDI_SYSEX_AKAI_EWI4K ); 109 message.push_back( MIDI_SYSEX_CHANNEL ); 110 message.push_back( MIDI_PRESET_DUMP_REQ ); 111 message.push_back( p ); // 6th byte is patch # 112 message.push_back( MIDI_SYSEX_TRAILER ); 113 114 try { 115 midiOut->sendMessage( &message ); 116 } 117 catch ( RtError &error ) { 118 error.printMessage(); 119 } 120 121 mymutex.lock(); // wait for a SysEx to be returned (MIDIListener looks after that) 122 if (!sysexDone.wait( &mymutex, MIDI_TIMEOUT_MS )) { 123 // we timed out 124 cout << "Timeout waiting for response from EWI\n"; 125 mymutex.unlock(); 126 return false; 127 } 128 mymutex.unlock(); 129 130 // it seems the EWI can get out of sync - so retry if we didn't ge the patch back we asked for 131 if (last_patch_loaded != (int) p) requestPatch( p ); 132 133 return true; 134} 135 136/** 137 * Requests the QuickPCs from the EWI and waits for them to be returned 138 * @param 139 * @return 140 */ 141bool midi_data::requestQuickPCs () { 142 143 std::vector<unsigned char> message; 144 145 //char sysex_fetch_patch[] = { 0xf0, 0x47, 0x64, 0x00, 0x40, 0x00, 0xf7 }; // 6th byte is patch # 146 message.push_back( MIDI_SYSEX_HEADER ); 147 message.push_back( MIDI_SYSEX_AKAI_ID ); 148 message.push_back( MIDI_SYSEX_AKAI_EWI4K ); 149 message.push_back( MIDI_SYSEX_ALLCHANNELS ); 150 message.push_back( MIDI_QUICKPC_DUMP_REQ ); 151 message.push_back( 0x00 ); // 6th byte is patch # 152 message.push_back( MIDI_SYSEX_TRAILER ); 153 154 try { 155 midiOut->sendMessage( &message ); 156 } 157 catch ( RtError &error ) { 158 error.printMessage(); 159 } 160 161 mymutex.lock(); // wait for a SysEx to be returned 162 if (!sysexDone.wait( &mymutex, MIDI_TIMEOUT_MS )) { 163 // we timed out 164 cout << "Timeout waiting for response from EWI\n"; 165 mymutex.unlock(); 166 return false; 167 } 168 mymutex.unlock(); 169 170 return true; 171} 172 173/** 174 * Sends the QuickPCs to the EWI 175 * @param 176 * @return 177 */ 178bool midi_data::sendQuickPCs () { 179 180 std::vector<unsigned char> message; 181 182 //char sysex_fetch_patch[] = { 0xf0, 0x47, 0x64, 0x00, 0x40, 0x00, 0xf7 }; // 6th byte is patch # 183 message.push_back( MIDI_SYSEX_HEADER ); 184 message.push_back( MIDI_SYSEX_AKAI_ID ); 185 message.push_back( MIDI_SYSEX_AKAI_EWI4K ); 186 message.push_back( MIDI_SYSEX_ALLCHANNELS ); 187 message.push_back( MIDI_QUICKPC_DUMP ); 188 message.push_back( 0x00 ); // 6th byte is patch # 189 for (int i = 0; i < EWI_NUM_QUICKPCS; i++) { 190 message.push_back(quickPCs[i]); 191 } 192 message.push_back( MIDI_SYSEX_TRAILER ); 193 194 try { 195 midiOut->sendMessage( &message ); 196 } 197 catch ( RtError &error ) { 198 error.printMessage(); 199 } 200 201 SLEEP( 250 ); 202 203 return true; 204} 205 206void midi_data::sendLiveControl (int lsb, int msb, int cvalue) { 207 sendCC (MIDI_CC_NRPN_LSB, lsb); 208 sendCC (MIDI_CC_NRPN_MSB, msb); 209 sendCC (MIDI_CC_DATA_ENTRY, cvalue); 210 sendCC (MIDI_CC_NRPN_LSB, 127); 211 sendCC (MIDI_CC_NRPN_MSB, 127); 212} 213 214void midi_data::sendCC (int cc, int val, int ch) { 215 216 std::vector<unsigned char> message; 217 218 message.push_back( 176+ch ); 219 message.push_back( cc ); 220 message.push_back( val ); 221 222 try { 223 midiOut->sendMessage( &message ); 224 } 225 catch ( RtError &error ) { 226 error.printMessage(); 227 } 228} 229 230 231/** 232 * @param sysex 233 * @param len 234 */ 235void midi_data::sendSysEx (char *sysex, int len) { 236 237 vector<unsigned char> message; 238 239 for (int i = 0; i<len; i++) { 240 // int h = sysex[i]; 241 message.push_back( sysex[i] ); 242 // cout <<dec<< i << " : "; cout << hex << h << endl; 243 } 244 245 try { 246 midiOut->sendMessage( &message ); 247 } 248 catch ( RtError &error ) { 249 error.printMessage(); 250 } 251 252 SLEEP( 250 ); 253} 254 255void midi_data::sendSysExFile( QString fileName ) { 256 257 // N.B. It seems RtMidi assumes only 1 sysex per message. We should check and break up files 258 // containing multiple sysexes here... FIXME - maybe fixed in RtMidi 1.0.9? 259 int nbytes; 260 char sysex[MAX_SYSEX_LENGTH]; 261 QFile sysex_file( fileName ); 262 sysex_file.open( QIODevice::ReadOnly ); 263 QDataStream sysex_data( &sysex_file ); 264 265 nbytes = sysex_data.readRawData( &sysex[0], MAX_SYSEX_LENGTH ); 266 267 sendSysEx( &sysex[0], nbytes ); 268 269} 270 271void midi_data::sendPatch (patch_t p, unsigned char mode) { 272 273 p.parameters.mode = mode; 274 275 sendSysEx( &p.whole_patch[0], EWI_PATCH_LENGTH ); 276 277 if (mode == EWI_EDIT) { 278 // if we're going to edit we need to send it again as patch 0 with the 279 // edit flag set... 280 p.parameters.patch_num = 0x00; 281 sendSysEx( &p.whole_patch[0], EWI_PATCH_LENGTH ); 282 } 283} 284 285void midi_data::scanPorts() { 286 287 QString port_name; 288 289 inPortList.clear(); 290 inPortPorts.clear(); 291 outPortList.clear(); 292 outPortPorts.clear(); 293 294 // Check inputs. 295 unsigned int nPorts = midiIn->getPortCount(); 296 std::cout << "\nThere are " << nPorts << " MIDI input sources available.\n"; 297 std::string portName; 298 for ( unsigned int i=0; i<nPorts; i++ ) { 299 try { 300 portName = midiIn->getPortName(i); 301 } 302 catch ( RtError &error ) { 303 error.printMessage(); 304 //goto cleanup; 305 } 306 //std::cout << " Input Port #" << i+1 << ": " << portName << '\n'; 307 inPortPorts.append( i ); 308 inPortList.append( QString::number( i ) + " : " + QString::fromStdString( portName ) ); 309 } 310 311 // Check outputs. 312 nPorts = midiOut->getPortCount(); 313 std::cout << "\nThere are " << nPorts << " MIDI output ports available.\n"; 314 for ( unsigned int i=0; i<nPorts; i++ ) { 315 try { 316 portName = midiOut->getPortName(i); 317 } 318 catch (RtError &error) { 319 error.printMessage(); 320 //goto cleanup; 321 } 322 //std::cout << " Output Port #" << i+1 << ": " << portName << '\n'; 323 outPortPorts.append( i ); 324 outPortList.append( QString::number( i ) + " : " + QString::fromStdString( portName ) ); 325 } 326 327} 328 329void midi_data::connectOutput( int o_port ) { 330 331 if (connectedOutPort != -1 ) disconnectOutput(); 332 try { 333 midiOut->openPort( o_port ); 334 } 335 catch (RtError &error) { 336 error.printMessage(); 337 //goto cleanup; 338 } 339 connectedOutPort = o_port; 340 if (verboseMode) cout << "Connected to MIDI output: " << o_port << endl; 341 QSettings settings( "EWItool", "EWItool" ); 342 settings.setValue( "MIDI/OutPort", o_port ); 343} 344 345void midi_data::connectInput( int i_port ) { 346 347 if (connectedInPort != -1) disconnectInput(); 348 try { 349 midiIn->openPort( i_port ); 350 } 351 catch (RtError &error) { 352 error.printMessage(); 353 //goto cleanup; 354 } 355 356 // don't ignore SysExes, do ignore timing and active sense messages 357 midiIn->ignoreTypes( false, true, true ); 358 359 connectedInPort = i_port; 360 if (verboseMode) cout << "Connected to MIDI input: " << i_port << endl; 361 QSettings settings( "EWItool", "EWItool" ); 362 settings.setValue( "MIDI/InPort", i_port ); 363 last_patch_loaded = -1; 364} 365 366void midi_data::disconnectInput() { 367 368 midiIn->closePort(); 369 370 connectedInPort = -1; 371} 372 373void midi_data::disconnectOutput() { 374 375 midiOut->closePort(); 376 377 connectedOutPort = -1; 378} 379 380 381 382