andrewm@0: /*
andrewm@0: TouchKeys: multi-touch musical keyboard control software
andrewm@0: Copyright (c) 2013 Andrew McPherson
andrewm@0:
andrewm@0: This program is free software: you can redistribute it and/or modify
andrewm@0: it under the terms of the GNU General Public License as published by
andrewm@0: the Free Software Foundation, either version 3 of the License, or
andrewm@0: (at your option) any later version.
andrewm@0:
andrewm@0: This program is distributed in the hope that it will be useful,
andrewm@0: but WITHOUT ANY WARRANTY; without even the implied warranty of
andrewm@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
andrewm@0: GNU General Public License for more details.
andrewm@0:
andrewm@0: You should have received a copy of the GNU General Public License
andrewm@0: along with this program. If not, see .
andrewm@0:
andrewm@0: =====================================================================
andrewm@0:
andrewm@0: TouchkeyKeyDivisionMappingFactory.cpp: factory for the split-key mapping
andrewm@0: which triggers different actions or pitches depending on where the key
andrewm@0: was struck.
andrewm@0: */
andrewm@0:
andrewm@0: #include "TouchkeyKeyDivisionMappingFactory.h"
andrewm@51: #include "TouchkeyKeyDivisionMappingShortEditor.h"
andrewm@44: #include "../../Display/KeyboardDisplay.h"
andrewm@0:
andrewm@51: /* Yarman-24c microtonal tuning */
andrewm@51: const float TouchkeyKeyDivisionMappingFactory::kTuningsYarman24c[24] = {
andrewm@0: 0, (1124.744 - 1200.0), 83.059, 143.623, 203.9, 191.771, 292.413, 348.343,
andrewm@0: 383.54, 362.503, 498.04, 415.305, 581.382, 634.184, 695.885, 648.682,
andrewm@0: 788.736, 853.063, 905.87, 887.656, 996.1, 1043.623, 1085.49, 1071.942,
andrewm@0: };
andrewm@0:
andrewm@51: const int TouchkeyKeyDivisionMappingFactory::kMaxSegmentsPerKey = 3;
andrewm@0:
andrewm@0: /* As arranged:
andrewm@0: *
andrewm@0: * B| Db/ Dd Eb/ Ed E| Gb/ Gd Ab/ Ad Bb/ Bd
andrewm@0: * C C# D D# E F F# G G# A A# B
andrewm@0: */
andrewm@0:
andrewm@0: TouchkeyKeyDivisionMappingFactory::TouchkeyKeyDivisionMappingFactory(PianoKeyboard &keyboard, MidiKeyboardSegment& segment)
andrewm@0: : TouchkeyBaseMappingFactory(keyboard, segment),
andrewm@51: tuningPreset_(-1), tunings_(0),
andrewm@0: numSegmentsPerKey_(TouchkeyKeyDivisionMapping::kDefaultNumberOfSegments),
andrewm@0: timeout_(TouchkeyKeyDivisionMapping::kDefaultDetectionTimeout),
andrewm@0: detectionParameter_(TouchkeyKeyDivisionMapping::kDefaultDetectionParameter),
andrewm@0: retriggerable_(false),
andrewm@0: retriggerNumFrames_(TouchkeyKeyDivisionMapping::kDefaultRetriggerNumFrames),
andrewm@0: retriggerKeepsVelocity_(true),
andrewm@0: referenceNote_(0), globalOffsetCents_(0)
andrewm@0: {
andrewm@0: //setName("/touchkeys/segmentpitch");
andrewm@0: setBendParameters();
andrewm@51: setTuningPreset(kTuningPreset24TET);
andrewm@44:
andrewm@44: KeyboardDisplay *display = keyboard_.gui();
andrewm@44: if(display != 0) {
andrewm@44: display->addKeyDivision(this, segment.noteRange().first, segment.noteRange().second, numSegmentsPerKey_);
andrewm@44: }
andrewm@44: }
andrewm@44:
andrewm@44: TouchkeyKeyDivisionMappingFactory::~TouchkeyKeyDivisionMappingFactory() {
andrewm@44: // Remove the divisions from the keys, if this mapping has added them
andrewm@44: KeyboardDisplay *display = keyboard_.gui();
andrewm@44: if(display != 0)
andrewm@44: display->removeKeyDivision(this);
andrewm@51: if(tunings_ != 0) {
andrewm@51: delete tunings_;
andrewm@51: tunings_ = 0;
andrewm@51: }
andrewm@0: }
andrewm@0:
andrewm@0: void TouchkeyKeyDivisionMappingFactory::setName(const string& name) {
andrewm@0: TouchkeyBaseMappingFactory::setName(name);
andrewm@0: setBendParameters();
andrewm@0: }
andrewm@0:
andrewm@51: void TouchkeyKeyDivisionMappingFactory::setTuningPreset(int preset) {
andrewm@51: if(preset < 0 || preset >= kTuningPresetMaxValue)
andrewm@51: return;
andrewm@51:
andrewm@51: ScopedLock sl(tuningMutex_);
andrewm@51:
andrewm@51: tuningPreset_ = preset;
andrewm@51: if(tunings_ != 0) {
andrewm@51: delete tunings_;
andrewm@51: tunings_ = 0;
andrewm@51: }
andrewm@51:
andrewm@51: if(tuningPreset_ == kTuningPreset19TET) {
andrewm@51: numSegmentsPerKey_ = 2;
andrewm@51: tunings_ = new float[24];
andrewm@51: for(int i = 0; i < 24; i++) {
andrewm@51: // Start with fraction of an octave, round to the nearest 19th of an octave
andrewm@51: float original = (float)i / 24.0;
andrewm@51: float rounded = floorf(original * 19.0 + 0.5);
andrewm@51:
andrewm@51: // Now convert the 19-tone index back to a fractional number of semitones
andrewm@51: tunings_[i] = rounded * 1200.0 / 19.0;
andrewm@51: }
andrewm@51: }
andrewm@51: else if(tuningPreset_ == kTuningPreset24TET) {
andrewm@51: numSegmentsPerKey_ = 2;
andrewm@51: tunings_ = new float[24];
andrewm@51: for(int i = 0; i < 24; i++) {
andrewm@51: tunings_[i] = (float)i * 50.0;
andrewm@51: }
andrewm@51: }
andrewm@51: else if(tuningPreset_ == kTuningPreset31TET) {
andrewm@51: numSegmentsPerKey_ = 3;
andrewm@51: tunings_ = new float[36];
andrewm@51: for(int i = 0; i < 36; i++) {
andrewm@51: // Start with fraction of an octave, round to the nearest 31st of an octave
andrewm@51: float original = (float)i / 36.0;
andrewm@51: float rounded = floorf(original * 31.0 + 0.5);
andrewm@51:
andrewm@51: // Now convert the 31-tone index back to a fractional number of semitones
andrewm@51: tunings_[i] = rounded * 1200.0 / 31.0;
andrewm@51: }
andrewm@51: }
andrewm@51: else if(tuningPreset_ == kTuningPreset36TET) {
andrewm@51: numSegmentsPerKey_ = 3;
andrewm@51: tunings_ = new float[36];
andrewm@51: for(int i = 0; i < 24; i++) {
andrewm@51: tunings_[i] = (float)i * 100.0 / 3.0;
andrewm@51: }
andrewm@51: }
andrewm@51: else if(tuningPreset_ == kTuningPresetYarman24c) {
andrewm@51: numSegmentsPerKey_ = 2;
andrewm@51: tunings_ = new float[24];
andrewm@51: for(int i = 0; i < 24; i++)
andrewm@51: tunings_[i] = kTuningsYarman24c[i];
andrewm@51: }
andrewm@51:
andrewm@51: KeyboardDisplay *display = keyboard_.gui();
andrewm@51: if(display != 0) {
andrewm@51: display->removeKeyDivision(this);
andrewm@51: display->addKeyDivision(this, keyboardSegment_.noteRange().first, keyboardSegment_.noteRange().second, numSegmentsPerKey_);
andrewm@51: }
andrewm@51: }
andrewm@51:
andrewm@51: #ifndef TOUCHKEYS_NO_GUI
andrewm@51: // ***** GUI Support *****
andrewm@51: MappingEditorComponent* TouchkeyKeyDivisionMappingFactory::createBasicEditor() {
andrewm@51: return new TouchkeyKeyDivisionMappingShortEditor(*this);
andrewm@51: }
andrewm@51: #endif
andrewm@36:
andrewm@36: // ****** Preset Save/Load ******
andrewm@36: XmlElement* TouchkeyKeyDivisionMappingFactory::getPreset() {
andrewm@36: PropertySet properties;
andrewm@36:
andrewm@36: storeCommonProperties(properties);
andrewm@36:
andrewm@36: // No properties for now
andrewm@36:
andrewm@36: XmlElement* preset = properties.createXml("MappingFactory");
andrewm@36: preset->setAttribute("type", "KeyDivision");
andrewm@36:
andrewm@36: return preset;
andrewm@36: }
andrewm@36:
andrewm@36: bool TouchkeyKeyDivisionMappingFactory::loadPreset(XmlElement const* preset) {
andrewm@36: if(preset == 0)
andrewm@36: return false;
andrewm@36:
andrewm@36: PropertySet properties;
andrewm@36: properties.restoreFromXml(*preset);
andrewm@36:
andrewm@36: if(!loadCommonProperties(properties))
andrewm@36: return false;
andrewm@36:
andrewm@36: // Nothing specific to do for now
andrewm@36:
andrewm@36: return true;
andrewm@36: }
andrewm@36:
andrewm@36: // ***** Private Methods *****
andrewm@36:
andrewm@0: // Set the initial parameters for a new mapping
andrewm@0: void TouchkeyKeyDivisionMappingFactory::initializeMappingParameters(int noteNumber, TouchkeyKeyDivisionMapping *mapping) {
andrewm@51: ScopedLock sl(tuningMutex_);
andrewm@51:
andrewm@51: // Convert absolute tunings into pitch bends in semitones
andrewm@51: float tunings[kMaxSegmentsPerKey];
andrewm@51:
andrewm@0: int index = (noteNumber + 12 - referenceNote_) % 12;
andrewm@0: float standardTuning = (float)index * 100.0;
andrewm@0:
andrewm@51: for(int i = 0; i < numSegmentsPerKey_; i++) {
andrewm@51: tunings[i] = (tunings_[index*numSegmentsPerKey_ + i] - standardTuning + globalOffsetCents_) * .01;
andrewm@51: }
andrewm@51: mapping->setSegmentPitchBends(tunings, numSegmentsPerKey_);
andrewm@0: mapping->setNumberOfSegments(numSegmentsPerKey_);
andrewm@0: mapping->setTimeout(timeout_);
andrewm@0: mapping->setDetectionParameter(detectionParameter_);
andrewm@0: mapping->setRetriggerable(retriggerable_, retriggerNumFrames_, retriggerKeepsVelocity_);
andrewm@0: }
andrewm@0:
andrewm@0: void TouchkeyKeyDivisionMappingFactory::setBendParameters() {
andrewm@7: // Range of 0 indicates special case of using global pitch wheel range
andrewm@7: setMidiParameters(MidiKeyboardSegment::kControlPitchWheel, 0.0, 0.0, 0.0);
andrewm@0:
andrewm@0: if(midiConverter_ != 0) {
andrewm@0: midiConverter_->listenToIncomingControl(MidiKeyboardSegment::kControlPitchWheel);
andrewm@0: }
andrewm@0: }