Skip to content

Commit

Permalink
Merge pull request #30 from perkowitz/mike/chords
Browse files Browse the repository at this point in the history
Chords: control midi note output with keyboard input
  • Loading branch information
perkowitz authored Jan 29, 2017
2 parents a473be9 + 46c31ea commit f18060d
Show file tree
Hide file tree
Showing 21 changed files with 1,162 additions and 62 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,14 @@ That basic sequencer runs as one module in Hachi.

Tracking bugs, needed improvements, and all-new features using [issues](https://github.com/perkowitz/issho/issues)
here in Github, but also via [HuBoard](https://huboard.com/perkowitz/issho#/milestones).

# Release notes

## 2017-01-29: v1.0.1

- Add configurable keyboard device
- Added Chords, ChordReceiver, ChordModule to track chords from keyboard device and apply chords to outgoing MIDI notes
- Updated MonoModule and StepModule to extend ChordModule so they follow chords
- Added chord options to keyboard configuration
- Updated doc to cover chords and keyboard configuration
- Added tests for Chords
43 changes: 43 additions & 0 deletions doc/hachi/hachi.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,49 @@ Launchpad's MIDI out port. For example, I have my NanoKontrol programmed to cont
Mutable Shruthi. While using Hachi to sequence the Shruthi, I can plug the NanoKontrol into the Pi and use
it to tweak the Shruthi's sounds.

### Knobby Configuration

A "knobby" device can be added for sending MIDI controllers to downstream MIDI modules. Adding the device to the config will tell
Hachi to route the controller's output to the Launchpad Pro's MIDI out, so that control messages can be sent to any devices
on the MIDI chain. The controller must be set up to send the desired messages; Hachi will not remap the MIDI messages in any way.

```
"devices": {
"knobby": {
"names":[
"nanokontrol"
]
}
}
```

### Keyboard Configuration

A MIDI keyboard device can be added to Hachi. Hachi will route MIDI from the keyboard into a ChordReceiver object, which monitors the currently played
notes to maintain the current chord. This chord is passed on to modules like MonoModule and StepModule, which can remap their MIDI notes to use
only notes in the specified chord. In other words, playing a chord on the connected keyboard will cause Hachi's sequencers to play their sequences
in that key.

The ChordReceiver may have hold enabled or disabled. When disabled, the chord will be maintained only as long as the keys are held down.
When a key is released, it is removed from the chord, and when all keys are released the chord is cleared. With hold enabled, as long as
a note is held down, additional notes played will be added to the current chord. When all notes are released, the current chord will be
held. The chord will be cleared when the ChordReceiver receives a MIDI controller message (any value) for the
holdClearControllerNumber (defaults to 64, hold). Hold can be enabled or disabled in the configuration with "chordHoldEnabled" set to
true or false. The hold clear controller number can also be specified. For example, if your MIDI keyboard has a mod wheel but no hold controller,
set the controller number to 1 to use the mod wheel to clear hold.


```
"devices": {
"keyboard": {
"names":[
"LPK25"
],
"chordHoldEnabled": false,
"holdClearControllerNumber": 64
}
}
```

## Module Configuration

Expand Down
5 changes: 5 additions & 0 deletions doc/hachi/modules/mono.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ set to use a fuchsia or orange color palette.
]
```

## Keyboard and Chords

If Hachi is configured with a MIDI keyboard (see [the main Hachi manual](../hachi.md)), MonoModule will respond to
chords by filtering output MIDI notes to the closest Chord note.

# Color Palette

Mono has two defined palettes: fuchsia and orange. This section describes the color values in the fuchsia palette.
Expand Down
5 changes: 5 additions & 0 deletions doc/hachi/modules/step.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,11 @@ to specify filenames for saving data.
]
```

## Keyboard and Chords

If Hachi is configured with a MIDI keyboard (see [the main Hachi manual](../hachi.md)), StepModule will respond to
chords by filtering output MIDI notes to the closest Chord note.

# Color Palette

Step has only one defined palette.
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>net.perkowitz</groupId>
<artifactId>issho</artifactId>
<version>1.0</version>
<version>1.0.1</version>
<name>Issho</name>


Expand Down
86 changes: 86 additions & 0 deletions src/main/java/net/perkowitz/issho/devices/Keyboard.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package net.perkowitz.issho.devices;

import net.perkowitz.issho.util.MidiUtil;
import javax.sound.midi.*;
import java.util.List;

import static javax.sound.midi.ShortMessage.*;
import static javax.sound.midi.ShortMessage.NOTE_OFF;

/**
* Created by optic on 1/14/17.
*/
public class Keyboard implements Receiver {

private static int MIDI_REALTIME_COMMAND = 0xF0;

private Transmitter inputTransmitter;
private Receiver outputReceiver;

public Keyboard(Transmitter inputTransmitter, Receiver outputReceiver) {
this.inputTransmitter = inputTransmitter;
this.inputTransmitter.setReceiver(this);
this.outputReceiver = outputReceiver;
}



/***** midi receiver implementation **************************************************************/

public void send(MidiMessage message, long timeStamp) {

if (message instanceof ShortMessage) {
ShortMessage shortMessage = (ShortMessage) message;
int command = shortMessage.getCommand();
int status = shortMessage.getStatus();

if (command != MIDI_REALTIME_COMMAND) {
switch (command) {
case NOTE_ON:
// System.out.printf("Keyboard NOTE ON: %d, %d, %d\n", shortMessage.getChannel(), shortMessage.getData1(), shortMessage.getData2());
outputReceiver.send(message, timeStamp);
break;
case NOTE_OFF:
// System.out.printf("Keyboard NOTE OFF: %d, %d, %d\n", shortMessage.getChannel(), shortMessage.getData1(), shortMessage.getData2());
outputReceiver.send(message, timeStamp);
break;
case CONTROL_CHANGE:
// System.out.printf("Keyboard MIDI CC: %d, %d, %d\n", shortMessage.getChannel(), shortMessage.getData1(), shortMessage.getData2());
outputReceiver.send(message, timeStamp);
break;
default:

}
}
}
}

public void close() {

}


/***** static methods **********************************************/

public static Keyboard fromMidiDevice(List<String> names, Receiver targetReceiver) {

MidiDevice midiInput = MidiUtil.findMidiDevice(names.toArray(new String[0]), false, true);
if (midiInput == null) {
System.out.printf("Unable to find keyboard device matching name: %s\n", names);
return null;
}

try {
midiInput.open();
Keyboard keyboard = new Keyboard(midiInput.getTransmitter(), targetReceiver);
return keyboard;
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}

return null;
}


}
Loading

0 comments on commit f18060d

Please sign in to comment.