public void NoteOn(NoteOnMessage msg) { // Find the per-device/channel mappings for the message's Device/Channel. If there's none, nothing to do. String deviceKey = Mapping.PerDeviceChannelMapping.createKey(msg.Device.Name, (int)msg.Channel); if (!m_perDeviceChannelMappings.ContainsKey(deviceKey)) { return; } Mapping.PerDeviceChannelMapping perDeviceChannelMapping = m_perDeviceChannelMappings[deviceKey]; // Iterate over the NoteMappings for this device/channel foreach (NoteMapping mapping in perDeviceChannelMapping.noteMappings) { // See if the note received is in range for the NoteMapping currently under consideration if (msg.Pitch >= (Pitch)mapping.lowestNote && msg.Pitch <= (Pitch)mapping.highestNote) { // It is. // Create mapped note record MappedNote mappedNoteRecord = new MappedNote(); mappedNoteRecord.sourceDeviceName = msg.Device.Name; mappedNoteRecord.sourceChannel = msg.Channel; mappedNoteRecord.origNote = msg.Pitch; SoundGenerator soundGenerator = mapping.soundGenerator; mappedNoteRecord.mappedDevice = soundGenerator.device; mappedNoteRecord.mappedChannel = (Channel)mapping.soundGeneratorPhysicalChannel; mappedNoteRecord.mappedNote = msg.Pitch + mapping.pitchOffset + masterTranspose; if (mappedNoteRecord.mappedNote < 0 || mappedNoteRecord.mappedNote > (Pitch)127) { continue; } // See if this note is already sounding. Look it up based on it's unmapped device, channel and note#. MappedNote matchingMappedNoteAlreadySounding = FindMappedNote(mappedNoteRecord); if (matchingMappedNoteAlreadySounding != null) { matchingMappedNoteAlreadySounding.mappedDevice.SendNoteOff(matchingMappedNoteAlreadySounding.mappedChannel, matchingMappedNoteAlreadySounding.mappedNote, 127); m_mappedNotesList.Remove(matchingMappedNoteAlreadySounding); } // Now, play the new mapping of the source note. mappedNoteRecord.mappedDevice.SendNoteOn(mappedNoteRecord.mappedChannel, mappedNoteRecord.mappedNote, msg.Velocity); // And add it to the dictionary of sounding notes. m_mappedNotesList.Add(mappedNoteRecord); } } }
public void PitchBend(PitchBendMessage msg) { // Find the per-device/channel mappings for the message's Device/Channel. If there's none, nothing to do. String deviceKey = Mapping.PerDeviceChannelMapping.createKey(msg.Device.Name, (int)msg.Channel); if (!m_perDeviceChannelMappings.ContainsKey(deviceKey)) { return; } Mapping.PerDeviceChannelMapping perDeviceChannelMapping = m_perDeviceChannelMappings[deviceKey]; // Iterate over the pitch-bend mappings for this device/channel and send the pitch bend, scaled per the mapping, to the target device foreach (PitchBendMapping pitchBendMapping in perDeviceChannelMapping.pitchBendMappings) { if (pitchBendMapping.scale != 0.0) { pitchBendMapping.soundGenerator.device.SendPitchBend((Channel)pitchBendMapping.soundGeneratorPhysicalChannel, (int)(msg.Value * pitchBendMapping.scale)); } } }
public void createTrialConfiguration() { dirty = true; try { Mapping.PerDeviceChannelMapping globalPerInputDeviceChannelMapping = new Mapping.PerDeviceChannelMapping(); globalPerInputDeviceChannelMapping.logicalInputDeviceName = "Input Device 1"; globalPerInputDeviceChannelMapping.inputDeviceChannel = 0; ControlMapping globalControlMapping = new ControlMapping(); globalControlMapping.soundGeneratorName = "Reaper"; globalControlMapping.soundGeneratorRelativeChannel = 0; globalControlMapping.sourceControlNumber = 75; // Top Left Rotary Knob on Oxygen globalControlMapping.mappedControlNumber = 9; // I've got Reaper Master Volume mapped to this. globalControlMapping.min = 30; // This provides a nice workable vol range globalControlMapping.max = 91; globalPerInputDeviceChannelMapping.controlMappings.Add(globalControlMapping); globalControlMappings.Add(globalPerInputDeviceChannelMapping); primaryInputDeviceName = "Input Device 1"; LogicalInputDevice.createTrialConfiguration(logicalInputDeviceDict); LogicalOutputDevice.createTrialConfiguration(logicalOutputDeviceDict); SoundGenerator.createTrialConfiguration(soundGenerators); Mapping.createTrialConfiguration(mappings); MidiProgram.createTrialConfiguration(midiPrograms); Setlist.createTrialConfiguration(songDict, setlists); } catch (Exception e) { MessageBox.Show("Exception creating trial configurations: " + e); } }
public void ControlChange(ControlChangeMessage msg) { // First we see if this is a "global" control, ie, one registered for JoeMidi as a whole, not for particular mappings. // This might be used to create a master-volume control that's always in effect. bool globalControlChangeSent = false; foreach (Mapping.PerDeviceChannelMapping globalPerDeviceChannelMappings in configuration.globalControlMappings) { if (globalPerDeviceChannelMappings.inputDevice != null && globalPerDeviceChannelMappings.inputDevice.Equals(msg.Device) && globalPerDeviceChannelMappings.inputDeviceChannel == (int)msg.Channel) { foreach (ControlMapping controlMapping in globalPerDeviceChannelMappings.controlMappings) { if (controlMapping.sourceControlNumber == (int)msg.Control) { int scaledValue = (int)(msg.Value * controlMapping.scale) + controlMapping.min; controlMapping.soundGenerator.device.SendControlChange((Channel)controlMapping.soundGeneratorPhysicalChannel, (Midi.Control)controlMapping.mappedControlNumber, scaledValue); globalControlChangeSent = true; } } } } if (globalControlChangeSent == true) { // When a control mapping is defined globally any per-mapping definitions of it are ignored. Debatable one day... return; } // Find the per-device/channel mappings for the message's Device/Channel. If there's none, nothing else to do. String deviceKey = Mapping.PerDeviceChannelMapping.createKey(msg.Device.Name, (int)msg.Channel); if (!m_perDeviceChannelMappings.ContainsKey(deviceKey)) { return; } Mapping.PerDeviceChannelMapping perDeviceChannelMapping = m_perDeviceChannelMappings[deviceKey]; // Iterate over the control mappings ... foreach (ControlMapping controlMapping in perDeviceChannelMapping.controlMappings) { // If we've received a control change message that the current mapping remaps process it. if (controlMapping.sourceControlNumber == (int)msg.Control && controlMapping.mappedControlNumber >= 0) { // Two control mapping modes: toggle and regular. if (controlMapping.bToggle == false) { // Regular remap of incoming control message to a different outgoing control#, with value scaled from the incoming range of // 0-127 to an outgoing range of min-max. int scaledValue = (int)(msg.Value * controlMapping.scale) + controlMapping.min; // Special processing for sustain pedal if (controlMapping.mappedControlNumber == (int)Midi.Control.SustainPedal) { if (msg.Value > 0) { // Down: Record what physical device/channels are receiving it from the source device. recordSustainPedalDown(msg.Device, controlMapping.soundGenerator.device, (Channel)controlMapping.soundGeneratorPhysicalChannel); controlMapping.soundGenerator.device.SendControlChange((Channel)controlMapping.soundGeneratorPhysicalChannel, (Midi.Control)controlMapping.mappedControlNumber, scaledValue); } else { // Up: un-sustain any soundGen that was previously sustained by a message originating from the source input device sendSustainPedalUpToAllDeviceChannelsWithSustainPedalDown(msg.Device); } } // Special Processing for cc7: Scaling again, on a per-sound-generator basis, to accomodate diff sound generator cc7 ranges. else if (controlMapping.mappedControlNumber == (int)Midi.Control.Volume) { scaledValue = (int)(scaledValue * controlMapping.soundGenerator.cc7Scale) + controlMapping.soundGenerator.cc7Min; controlMapping.soundGenerator.device.SendControlChange((Channel)controlMapping.soundGeneratorPhysicalChannel, (Midi.Control)controlMapping.mappedControlNumber, scaledValue); } else { controlMapping.soundGenerator.device.SendControlChange((Channel)controlMapping.soundGeneratorPhysicalChannel, (Midi.Control)controlMapping.mappedControlNumber, scaledValue); } } else { // Toggle mode alternately sends min and max remapped values to the remap control# on receipt of remappable control messages with values > 0x40. // This is typically used with momentary switch controls which send 7F on press and 0 on release. // I created this to allow my sustain pedal to toggle Rotary Speaker speed between high and low on each press. if (msg.Value >= 0x40) { if (controlMapping.currentToggleState == true) { controlMapping.currentToggleState = false; controlMapping.soundGenerator.device.SendControlChange((Channel)controlMapping.soundGeneratorPhysicalChannel, (Midi.Control)controlMapping.mappedControlNumber, controlMapping.max); } else { controlMapping.currentToggleState = true; controlMapping.soundGenerator.device.SendControlChange((Channel)controlMapping.soundGeneratorPhysicalChannel, (Midi.Control)controlMapping.mappedControlNumber, controlMapping.min); } } } } } }
virtual public bool bind(Dictionary <String, LogicalInputDevice> logicalInputDeviceDict, Dictionary <String, SoundGenerator> soundGenerators, Dictionary <String, Mapping> mappings, LogicalInputDevice primaryInputDevice) { // Find or create a Mapping for this MidiProgram that it will point to if (bSingle == false) // If it's not a single, then it's a mapping, so just find the one named. { if (mappings.ContainsKey(this.MappingName)) { mapping = mappings[this.MappingName]; return(true); } else { MessageBox.Show("MidiProgram Mapping " + this.MappingName + " not defined in this configuration."); return(false); } } else { // If it's a single, then we create a mapping on the fly for it. mapping = new Mapping(); String candidateName = "(S) " + this.SinglePatchName; if (mappings.ContainsKey(candidateName)) { // Patch Name alone isn't a unique mapping name. Fully qualify it with the SG name too. candidateName = "(S) " + this.SingleSoundGeneratorName + " - " + this.SinglePatchName; } mapping.name = candidateName; // Create a single mapping with some settings values on the fly Mapping.PerDeviceChannelMapping perDeviceChannelMapping = new Mapping.PerDeviceChannelMapping(); perDeviceChannelMapping.logicalInputDeviceName = primaryInputDevice.logicalDeviceName; perDeviceChannelMapping.inputDeviceChannel = 0; NoteMapping noteMapping = new NoteMapping(); noteMapping.soundGeneratorName = this.SingleSoundGeneratorName; noteMapping.soundGeneratorRelativeChannel = 0; noteMapping.lowestNote = 0; noteMapping.highestNote = 127; noteMapping.pitchOffset = 0; perDeviceChannelMapping.noteMappings.Add(noteMapping); MappingPatch mappingPatch = new MappingPatch(); mappingPatch.soundGeneratorName = this.SingleSoundGeneratorName; mappingPatch.soundGeneratorRelativeChannel = 0; mappingPatch.patchName = this.SinglePatchName; perDeviceChannelMapping.mappingPatches.Add(mappingPatch); PitchBendMapping pitchBendMapping = new PitchBendMapping(); pitchBendMapping.soundGeneratorName = this.SingleSoundGeneratorName; pitchBendMapping.soundGeneratorRelativeChannel = 0; pitchBendMapping.scale = 1.0; perDeviceChannelMapping.pitchBendMappings.Add(pitchBendMapping); ControlMapping controlMapping = new ControlMapping(); controlMapping.soundGeneratorName = this.SingleSoundGeneratorName; controlMapping.soundGeneratorRelativeChannel = 0; controlMapping.sourceControlNumber = 1; // Mod Wheel controlMapping.mappedControlNumber = 1; controlMapping.min = 0; controlMapping.max = 127; controlMapping.initialValue = -1; perDeviceChannelMapping.controlMappings.Add(controlMapping); controlMapping = new ControlMapping(); controlMapping.soundGeneratorName = this.SingleSoundGeneratorName; controlMapping.soundGeneratorRelativeChannel = 0; controlMapping.sourceControlNumber = -1; controlMapping.mappedControlNumber = 7; // Vol controlMapping.min = 0; controlMapping.max = 127; controlMapping.initialValue = 127; perDeviceChannelMapping.controlMappings.Add(controlMapping); controlMapping = new ControlMapping(); controlMapping.soundGeneratorName = this.SingleSoundGeneratorName; controlMapping.soundGeneratorRelativeChannel = 0; controlMapping.sourceControlNumber = 64; // Damper controlMapping.mappedControlNumber = 64; controlMapping.min = 0; controlMapping.max = 127; controlMapping.initialValue = -1; perDeviceChannelMapping.controlMappings.Add(controlMapping); // Kurzweil Artis Variation Button... controlMapping = new ControlMapping(); controlMapping.soundGeneratorName = this.SingleSoundGeneratorName; controlMapping.soundGeneratorRelativeChannel = 0; controlMapping.sourceControlNumber = 0x1D; controlMapping.mappedControlNumber = 0x1D; controlMapping.min = 0; controlMapping.max = 127; controlMapping.initialValue = -1; perDeviceChannelMapping.controlMappings.Add(controlMapping); mapping.perDeviceChannelMappings.Add(perDeviceChannelMapping.key, perDeviceChannelMapping); return(mapping.bind(logicalInputDeviceDict, soundGenerators)); } }
override public bool bind(Dictionary <String, LogicalInputDevice> logicalInputDeviceDict, Dictionary <String, SoundGenerator> soundGenerators) { // Builds out an actual Mapping based on the SimpleMapping. Dictionary <String, int> soundGeneratorChannelAssignments = new Dictionary <string, int>(); perDeviceChannelMappings.Clear(); foreach (PerDeviceSimpleMapping perDeviceSimpleMapping in perDeviceSimpleMappings) { Mapping.PerDeviceChannelMapping perDeviceChannelMapping = new Mapping.PerDeviceChannelMapping(perDeviceSimpleMapping.inputDeviceName, perDeviceSimpleMapping.inputDeviceChannel); foreach (SimpleMappingDefinition simpleMappingDefinition in perDeviceSimpleMapping.simpleMappingDefinitions) { NoteMapping noteMapping = new NoteMapping(); String soundGeneratorName = simpleMappingDefinition.soundGeneratorName; noteMapping.soundGeneratorName = soundGeneratorName; int soundGeneratorRelativeChannel = 0; if (soundGeneratorChannelAssignments.ContainsKey(soundGeneratorName)) { soundGeneratorRelativeChannel = soundGeneratorChannelAssignments[soundGeneratorName] + 1; soundGeneratorChannelAssignments[soundGeneratorName] = soundGeneratorRelativeChannel; } else { soundGeneratorRelativeChannel = 0; soundGeneratorChannelAssignments.Add(soundGeneratorName, 0); } noteMapping.soundGeneratorRelativeChannel = soundGeneratorRelativeChannel; if (perDeviceSimpleMapping.splitPoint == -1) { noteMapping.highestNote = 127; noteMapping.lowestNote = 0; } else if (simpleMappingDefinition.bLower == true) { noteMapping.highestNote = perDeviceSimpleMapping.splitPoint - 1; noteMapping.lowestNote = 0; } else { noteMapping.highestNote = 127; noteMapping.lowestNote = perDeviceSimpleMapping.splitPoint; } noteMapping.pitchOffset = simpleMappingDefinition.transpose; perDeviceChannelMapping.noteMappings.Add(noteMapping); MappingPatch mappingPatch = new MappingPatch(); mappingPatch.patchName = simpleMappingDefinition.programName; mappingPatch.soundGeneratorName = soundGeneratorName; mappingPatch.soundGeneratorRelativeChannel = soundGeneratorRelativeChannel; perDeviceChannelMapping.mappingPatches.Add(mappingPatch); if (simpleMappingDefinition.pbScale != 0) { PitchBendMapping pitchBendMapping = new PitchBendMapping(); pitchBendMapping.soundGeneratorName = soundGeneratorName; pitchBendMapping.soundGeneratorRelativeChannel = soundGeneratorRelativeChannel; pitchBendMapping.scale = simpleMappingDefinition.pbScale; perDeviceChannelMapping.pitchBendMappings.Add(pitchBendMapping); } if (simpleMappingDefinition.bEnaVolControl == true || simpleMappingDefinition.initialVolume >= 0) { // This may be a mapping only, it may be an initial value setting only, or it may be both ControlMapping ctlMapping = new ControlMapping(); ctlMapping.soundGeneratorName = soundGeneratorName; ctlMapping.soundGeneratorRelativeChannel = soundGeneratorRelativeChannel; ctlMapping.sourceControlNumber = (simpleMappingDefinition.bEnaVolControl == true) ? 7 : -1; ctlMapping.mappedControlNumber = 7; ctlMapping.initialValue = (simpleMappingDefinition.initialVolume >= 0) ? simpleMappingDefinition.initialVolume : 127; perDeviceChannelMapping.controlMappings.Add(ctlMapping); } if (simpleMappingDefinition.bEnaModControl == true) { ControlMapping ctlMapping = new ControlMapping(); ctlMapping.soundGeneratorName = soundGeneratorName; ctlMapping.soundGeneratorRelativeChannel = soundGeneratorRelativeChannel; ctlMapping.sourceControlNumber = 1; ctlMapping.mappedControlNumber = 1; ctlMapping.initialValue = 0; perDeviceChannelMapping.controlMappings.Add(ctlMapping); } if (simpleMappingDefinition.bEnaDamperControl == true) { ControlMapping ctlMapping = new ControlMapping(); ctlMapping.soundGeneratorName = soundGeneratorName; ctlMapping.soundGeneratorRelativeChannel = soundGeneratorRelativeChannel; ctlMapping.sourceControlNumber = 64; ctlMapping.mappedControlNumber = simpleMappingDefinition.damperRemap; ctlMapping.bToggle = simpleMappingDefinition.bDamplerToggle; // Special option for damper: toggle mode: ie for Leslie ctl. ctlMapping.initialValue = 0; perDeviceChannelMapping.controlMappings.Add(ctlMapping); } { // Kurzweil Artis Variation Button ControlMapping ctlMapping = new ControlMapping(); ctlMapping.soundGeneratorName = soundGeneratorName; ctlMapping.soundGeneratorRelativeChannel = soundGeneratorRelativeChannel; ctlMapping.sourceControlNumber = 0x1D; ctlMapping.mappedControlNumber = 0x1D; ctlMapping.initialValue = 0; perDeviceChannelMapping.controlMappings.Add(ctlMapping); } } perDeviceChannelMappings.Add(perDeviceChannelMapping.key, perDeviceChannelMapping); } return(base.bind(logicalInputDeviceDict, soundGenerators)); }