Mercurial > hg > touchkeys
view Source/TouchKeys/MidiInputController.cpp @ 11:c6f30c1e2bda
Fixes for OSC emulation. Also a debugging tool for generating random TouchKeys data.
author | Andrew McPherson <andrewm@eecs.qmul.ac.uk> |
---|---|
date | Sat, 23 Nov 2013 14:47:02 +0000 |
parents | 3580ffe87dc8 |
children | 88287c1c2c92 |
line wrap: on
line source
/* TouchKeys: multi-touch musical keyboard control software Copyright (c) 2013 Andrew McPherson 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 3 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, see <http://www.gnu.org/licenses/>. ===================================================================== MidiInputController.cpp: handles incoming MIDI data and manages input ports. Detailed processing is broken down by keyboard segment; see MidiKeyboardSegment.h/cpp for more. */ #include "MidiInputController.h" #include "MidiOutputController.h" #include "../Mappings/MappingFactory.h" #undef MIDI_INPUT_CONTROLLER_DEBUG_RAW // Constructor MidiInputController::MidiInputController(PianoKeyboard& keyboard) : keyboard_(keyboard), midiOutputController_(0), segmentUniqueIdentifier_(0) { logFileCreated = false; loggingActive = false; } void MidiInputController::setMidiOutputController(MidiOutputController* ct) { midiOutputController_ = ct; // Propagate the change to the keyboard segments ScopedLock sl(segmentsMutex_); for(int i = 0; i < segments_.size(); i++) { segments_[i]->setMidiOutputController(ct); } } // ------------------------------------------------------ // create a new MIDI log file, ready to have data written to it void MidiInputController::createLogFile(string midiLog_filename, string path) { // indicate that we have created a log file (so we can close it later) logFileCreated = true; if (path.compare("") != 0) { path = path + "/"; } midiLog_filename = path + midiLog_filename; char *fileName = (char*)midiLog_filename.c_str(); // create output file midiLog.open (fileName, ios::out | ios::binary); midiLog.seekp(0); } // ------------------------------------------------------ // close the existing log file void MidiInputController::closeLogFile() { if (logFileCreated) { midiLog.close(); logFileCreated = false; } } // ------------------------------------------------------ // start logging midi data void MidiInputController::startLogging() { loggingActive = true; } // ------------------------------------------------------ // stop logging midi data void MidiInputController::stopLogging() { loggingActive = false; } // Iterate through the available MIDI devices. Return a vector containing // indices and names for each device. The index will later be passed back // to indicate which device to open. vector<pair<int, string> > MidiInputController::availableMidiDevices() { vector<pair<int, string> > deviceList; try { StringArray deviceStrings = MidiInput::getDevices(); for(int i = 0; i < deviceStrings.size(); i++) { pair<int, string> p(i, string(deviceStrings[i].toUTF8())); deviceList.push_back(p); } } catch(...) { deviceList.clear(); } return deviceList; } // Enable a new MIDI port according to its index (returned from availableMidiDevices()) // Returns true on success. bool MidiInputController::enablePort(int portNumber) { if(portNumber < 0) return false; MidiInput *device = MidiInput::openDevice(portNumber, this); if(device == 0) { cout << "Failed to enable MIDI input port " << portNumber << ")\n"; return false; } //cout << "Enabling MIDI input port " << portNumber << " (" << device->getName() << ")\n"; device->start(); // Save the device in the set of ports activePorts_[portNumber] = device; return true; } // Enable all current MIDI ports bool MidiInputController::enableAllPorts() { bool enabledPort = false; vector<pair<int, string> > ports = availableMidiDevices(); vector<pair<int, string> >::iterator it = ports.begin(); while(it != ports.end()) { // Don't enable MIDI input from our own virtual output if(it->second != string(kMidiVirtualOutputName.toUTF8())) enabledPort |= enablePort((it++)->first); else it++; } return enabledPort; } // Remove a specific MIDI input source and free associated memory void MidiInputController::disablePort(int portNumber) { if(activePorts_.count(portNumber) <= 0) return; MidiInput *device = activePorts_[portNumber]; if(device == 0) return; //cout << "Disabling MIDI input port " << portNumber << " (" << device->getName() << ")\n"; device->stop(); delete device; activePorts_.erase(portNumber); } // Remove all MIDI input sources and free associated memory void MidiInputController::disableAllPorts() { map<int, MidiInput*>::iterator it; //cout << "Disabling all MIDI input ports\n"; it = activePorts_.begin(); while(it != activePorts_.end()) { if(it->second == 0) { it++; continue; } it->second->stop(); // disable port delete it->second; // free MidiInputCallback it++; } activePorts_.clear(); } // Return a list of active ports vector<int> MidiInputController::activePorts() { vector<int> ports; map<int, MidiInput*>::iterator it; for(it = activePorts_.begin(); it != activePorts_.end(); ++it) { ports.push_back(it->first); } return ports; } // Add a new keyboard segment. Returns a pointer to the newly created segment MidiKeyboardSegment* MidiInputController::addSegment(int outputPortNumber, int noteMin, int noteMax, int channelMask) { ScopedLock sl(segmentsMutex_); // Create a new segment and populate its values MidiKeyboardSegment *segment = new MidiKeyboardSegment(keyboard_); segment->setMidiOutputController(midiOutputController_); segment->setOutputPort(outputPortNumber); segment->setNoteRange(noteMin, noteMax); segment->setChannelMask(channelMask); // Add the segment to the vector and return the pointer segments_.push_back(segment); segmentUniqueIdentifier_++; return segment; } // Remove a segment by index or by object void MidiInputController::removeSegment(int index) { ScopedLock sl(segmentsMutex_); if(index < 0 || index >= segments_.size()) return; MidiKeyboardSegment* segment = segments_[index]; delete segment; segments_.erase(segments_.begin() + index); segmentUniqueIdentifier_++; } void MidiInputController::removeSegment(MidiKeyboardSegment* segment) { ScopedLock sl(segmentsMutex_); for(int i = 0; i < segments_.size(); i++) { if(segments_[i] == segment) { delete segment; segments_.erase(segments_.begin() + i); break; } } segmentUniqueIdentifier_++; } void MidiInputController::removeAllSegments() { ScopedLock sl(segmentsMutex_); for(int i = 0; i < segments_.size(); i++) delete segments_[i]; segments_.clear(); segmentUniqueIdentifier_++; } // Disable any currently active notes void MidiInputController::allNotesOff() { ScopedLock sl(segmentsMutex_); for(int i = 0; i < segments_.size(); i++) segments_[i]->allNotesOff(); } // This gets called every time MIDI data becomes available on any input controller. source tells // us where the message came from, and may be 0 if being called internally. void MidiInputController::handleIncomingMidiMessage(MidiInput* source, const MidiMessage& message) //void MidiInputController::rtMidiCallback(double deltaTime, vector<unsigned char> *message, int inputNumber) { // Juce will give us one MIDI command per callback, which makes processing easier for us. // Ignore sysex messages for now if(message.isSysEx()) return; // Pull out the raw bytes int dataSize = message.getRawDataSize(); if(dataSize <= 0) return; const unsigned char *messageData = message.getRawData(); // if logging is active if (loggingActive) { //////////////////////////////////////////////////////// //////////////////////////////////////////////////////// //////////////////// BEGIN LOGGING ///////////////////// int midi_channel = (int)(messageData[0]); int midi_number = dataSize > 1 ? (int)(messageData[1]) : 0; int midi_velocity = dataSize > 2 ? (int)(messageData[2]) : 0; timestamp_type timestamp = keyboard_.schedulerCurrentTimestamp(); midiLog.write ((char*)×tamp, sizeof (timestamp_type)); midiLog.write ((char*)&midi_channel, sizeof (int)); midiLog.write ((char*)&midi_number, sizeof (int)); midiLog.write ((char*)&midi_velocity, sizeof (int)); ///////////////////// END LOGGING ////////////////////// //////////////////////////////////////////////////////// //////////////////////////////////////////////////////// } #ifdef MIDI_INPUT_CONTROLLER_DEBUG_RAW if(source == 0) cout << "MIDI Input [internal]: "; else cout << "MIDI Input [" << source->getName() << "]: "; for(int debugPrint = 0; debugPrint < dataSize; debugPrint++) printf("%x ", messageData[debugPrint]); cout << endl; #endif /* MIDI_INPUT_CONTROLLER_DEBUG_RAW */ ScopedLock ksl(keyboard_.performanceDataMutex_); ScopedLock sl(segmentsMutex_); for(int i = 0; i < segments_.size(); i++) { if(segments_[i]->respondsToMessage(message)) segments_[i]->midiHandlerMethod(source, message); } } // Destructor. Free any existing callbacks MidiInputController::~MidiInputController() { if(logFileCreated) { midiLog.close(); } disableAllPorts(); removeAllSegments(); }