Diatonic Keyboard
Implements a diatonic music keyboard. It reacts to touch (or click) events and fires note on/off callbacks, as well as note expression callbacks per pointer drag events and pressure events if it is configured as such.
Using DiatonicKeyboard
You are supposed to manage your note states of each keys as noteOnStates
, and pass onNoteOn
and onNoteOff
event handlers. They take note number and "details" as the argument. Note "details" is UNUSED in the current version, but will contain (1)velocity for MIDI 1.0, and (2)velocity + note attributes for MIDI 2.0. They can be originated from touch pressure etc.
It can also send "note expressions" alike, if DiatonicKeyboardMoveAction.NoteExpression
is specified at moveAction
parameter. Then onExpression
event will be raised for dragging operation and touch pressure changes. It is done per pointer, so it works like an MPE (or MIDI 2.0) keyboard. Dragging events are sent for both X and Y axes.
Customizing keyboard
The musical key range is calculated from octaveZeroBased
argument (4
by default) and up to numWhiteKeys
(14
= 2 octaves by default). ("from zero" means, it does not start from 1 which are used by several DAWs). The start note is fixed to its C key so far.
The rendered keys are defined by numWhiteKeys
above, and the rendered sizes are determined by whiteKeyWidth
(30.dp
by default), blackKeyHeight
(35.dp
by default), totalWidth
(whiteKeyWidth * numWhiteKeys
by default) and totalHeight
(60.dp by default). You can expand the actual Composable size by modifier
, but it will be rendered based on these arguments, and pointer inputs are calculated based on those arguments too.
You can also customize whiteNoteOnColor
(Color.Cyan
by default),blackNoteOnColor
(defaults to whiteNoteOnColor
argument), as well as whiteKeyColor
and blackKeyColor
(for note-off state).
UI implementation details
This keyboard control is designed for touch UI, and at the same time, for small screen sizes. Our default white key size is 30.dp
. It is smaller than 48.dp
, but on touch events it does not strictly require touching insets. Instead, the target note is calculated based on the nearest to the center of the keys. On the other hand, if the input type is mouse or stylus, it expects exact insets.
Regarding drag events for note expressions, the valid motion range is -80dp..80dp from the initial point, by default. They are then mapped to -1f..1f value range when onExpression
callback is invoked.
Regarding touch events for note expressions, the value semantics depends on whatever device sends.
Usage example
Here is an example (complicated) use of DiatonicKeyboard():
val noteOnStates = remember { List(128) { 0 }.toMutableStateList() }
DiatonicKeyboard(noteOnStates.toList(),
// you will also insert actual musical operations within these lambdas
onNoteOn = { note, _ -> noteOnStates[note] = 1 },
onNoteOff = { note, _ -> noteOnStates[note] = 0 },
// use below only if you need MIDI 2.0 / MPE compat keyboard
moveAction = DiatonicKeyboardMoveAction.NoteExpression,
onExpression = { origin, note, data ->
when (origin) {
DiatonicKeyboardNoteExpressionOrigin.Horizontal) ->
perNotePitchBend(note, data / 2f + 0.5f) }
DiatonicKeyboardNoteExpressionOrigin.Vertical) ->
polyphonicPressure(note, data / 2f + 0.5f) }
else -> {}
}
}
)
Parameters
a List of Long that holds note states. It must contain 128 elements. Currently it only expects that note on state holds non-zero value.
a Modifier that applies to its top level Column.
an event handler that is called when a key for a note is pressed
an event handler that is called when a key for a note is released
an event handler that is called when note expression events occur, by dragging (if indicated so by moveAction
). The value range for data
sent to the handler depends on the target origin
. For HorizontalDragging
and VerticalDragging
they are -1.0f..1.0f
. For Pressure
it is up to device.
indicates that how dragging works. See the documentation on DiatonicKeyboardMoveAction
enumeration type. NoteChange
by default.
the octave (in zero-based counting i.e. 0 to 9 or 10 (up to numWhiteKeys
). 4
by default.
the number of white keys to be rendered. 14
by default (which means 2 octaves)
a sensitivity parameter over note expression dragging. The value is treated as a Dp
value that corresponds to the width for "half" of the motion size towards max or min value. 80
(Dp) by default.
The display size for one white key width. 30.dp
by default.
The display size for black key height. 35.dp
by default.
The display size for the whole keyboard control. It is automatically calculated as whiteKeyWidth * numWhiteKeys
but you can change it. Alternatively, you can explicitly specify null
then it will take use Modifier.fillMaxSize()
(but note that the number of the rendered key is governed by numWhiteKeys
anyways).
The display size for the whole keyboard control. It also means white key height. 60.dp
by default.
A Color
value for the note on indication on the white keys. Color.Cyan
by default.
A Color
value for the note on indication on the black keys. whiteNoteOnColor
by default.
A Color
value for the white keys (when not at note-on state). Color.White
by default.
A Color
value for the black keys (when not at note-on state). Color.Black
by default.