33 radiusScale (owner.getKeyWidth() / 1.5f),
41 float getStrikeRadius()
const {
return 5.0f + getNoteOnVelocity() * radiusScale * 2.0f; }
42 float getPressureRadius()
const {
return 5.0f + getPressure() * radiusScale * 2.0f; }
44 float getNoteOnVelocity()
const {
return noteOnVelocity; }
45 float getPressure()
const {
return pressure; }
52 auto pressSize = getPressureRadius() * 2.0f;
65 float radiusScale = 0.0f, noteOnVelocity = 0.0f, pressure = 0.5f;
67 uint8 initialNote = 0;
68 bool isLatched =
true;
95void MPEKeyboardComponent::drawWhiteKey (
int midiNoteNumber, Graphics& g, Rectangle<float> area)
103 g.setFont (Font (fontHeight).withHorizontalScale (0.8f));
107 case horizontalKeyboard:
108 g.drawText (text, area.withTrimmedLeft (1.0f).withTrimmedBottom (2.0f),
111 case verticalKeyboardFacingLeft:
114 case verticalKeyboardFacingRight:
123void MPEKeyboardComponent::drawBlackKey (
int , Graphics& g, Rectangle<float> area)
132 g.fillRoundedRectangle (area.toFloat().reduced ((area.getWidth() / 2.0f) - (
getBlackNoteWidth() / 12.0f),
133 area.getHeight() / 4.0f), 1.0f);
137 g.fillRoundedRectangle (area.toFloat().reduced (area.getWidth() / 4.0f,
204MPEValue MPEKeyboardComponent::mousePositionToTimbre (Point<float>
mousePos)
210 case horizontalKeyboard:
return mousePos.y;
212 case verticalKeyboardFacingRight:
return mousePos.x;
228 auto channel = channelAssigner->findMidiChannelForNewNote (
newNote);
236 && useMouseSourcePressureForStrike ? e.
pressure
246 if (! note.isValid())
250 noteComponents.end(),
251 [noteID] (
auto& comp) { return comp->sourceID == noteID; });
259 (*noteComponent)->isLatched =
false;
262 auto channel = channelAssigner->findMidiChannelForExistingNote (note.initialNote);
264 if (! (*noteComponent)->isLatched)
265 instrument.
pitchbend (channel, mousePositionToPitchbend (note.initialNote, e.
position));
269 && useMouseSourcePressureForStrike ? e.
pressure
277 if (! note.isValid())
280 instrument.
noteOff (channelAssigner->findMidiChannelForExistingNote (note.initialNote),
282 channelAssigner->noteOff (note.initialNote);
288 for (
auto& comp : noteComponents)
293 instrument.
noteOff (channelAssigner->findMidiChannelForExistingNote (note.initialNote),
299void MPEKeyboardComponent::updateZoneLayout()
306 noteComponents.clear();
317 if (layout.isActive())
319 auto zone = layout.
getLowerZone().isActive() ? layout.getLowerZone()
320 : layout.getUpperZone();
322 channelAssigner = std::make_unique<MPEChannelAssigner> (zone);
323 perNotePitchbendRange = zone.perNotePitchbendRange;
327 channelAssigner.reset();
332void MPEKeyboardComponent::addNewNote (MPENote note)
334 noteComponents.push_back (std::make_unique<MPENoteComponent> (*
this, note.noteID, note.initialNote,
335 note.noteOnVelocity.asUnsignedFloat(),
336 note.pressure.asUnsignedFloat()));
337 auto& comp = noteComponents.back();
348 noteComponents.end(),
349 [note] (
auto& comp) { return comp->sourceID == note.noteID; }))
362 [&comp] (
auto& note) { return comp->sourceID == note.noteID; });
370 if (noteComponents.empty())
374void MPEKeyboardComponent::updateNoteComponentBounds (
const MPENote& note, MPENoteComponent&
noteComponent)
378 const auto currentNote = note.initialNote + (
float) note.totalPitchbendInSemitones;
397 ||
currentOrientation == verticalKeyboardFacingRight ? 1.0f - note.timbre.asUnsignedFloat()
398 : note.timbre.asUnsignedFloat());
408 noteComponent.setBounds (Rectangle<float> (radius * 2.0f, radius * 2.0f)
410 .getSmallestIntegerContainer());
413static bool operator< (
const MPENote&
n1,
const MPENote&
n2)
noexcept {
return n1.noteID <
n2.noteID; }
415void MPEKeyboardComponent::updateNoteComponents()
422 for (
const auto& note : activeNotes)
430 for (
auto& comp : noteComponents)
434 [&comp] (
auto& note) { return note.noteID == comp->sourceID; });
446void MPEKeyboardComponent::timerCallback()
448 updateNoteComponents();
452void MPEKeyboardComponent::noteAdded (MPENote
newNote)
456 activeNotes.push_back ({
newNote,
true });
462void MPEKeyboardComponent::updateNoteData (MPENote&
changedNote)
466 for (
auto& note : activeNotes)
477void MPEKeyboardComponent::notePressureChanged (MPENote
changedNote)
482void MPEKeyboardComponent::notePitchbendChanged (MPENote
changedNote)
487void MPEKeyboardComponent::noteTimbreChanged (MPENote
changedNote)
492void MPEKeyboardComponent::noteReleased (MPENote
finishedNote)
502void MPEKeyboardComponent::zoneLayoutChanged()
The base class for all JUCE user-interface objects.
bool isOpaque() const noexcept
Returns true if no parts of this component are transparent.
int getHeight() const noexcept
Returns the component's height in pixels.
void addAndMakeVisible(Component *child, int zOrder=-1)
Adds a child component to this one, and also makes the child visible if it isn't already.
FocusChangeType
Enumeration used by the focusGained() and focusLost() methods.
void setOpaque(bool shouldBeOpaque)
Indicates whether any parts of the component might be transparent.
Rectangle< int > getBounds() const noexcept
Returns this component's bounding box.
void repaint()
Marks the whole component as needing to be redrawn.
Colour findColour(int colourID, bool inheritFromParent=false) const
Looks for a colour that has been registered with the given colour ID number.
int getWidth() const noexcept
Returns the component's width in pixels.
Rectangle< int > getLocalBounds() const noexcept
Returns the component's bounds, relative to its own origin.
Automatically locks and unlocks a mutex object.
A graphics context, used for drawing a component or image.
void fillRect(Rectangle< int > rectangle) const
Fills a rectangle with the current colour or brush.
void drawEllipse(float x, float y, float width, float height, float lineThickness) const
Draws an elliptical stroke using the current colour or brush.
void setColour(Colour newColour)
Changes the current drawing colour.
void fillEllipse(float x, float y, float width, float height) const
Fills an ellipse with the current colour or brush.
@ centredRight
Indicates that the item should be centred vertically but placed on the right hand side.
@ centredBottom
Indicates that the item should be centred horizontally and placed at the bottom.
@ centredLeft
Indicates that the item should be centred vertically but placed on the left hand side.
A base class for drawing a custom MIDI keyboard component.
bool isHorizontal() const noexcept
Returns true if the keyboard's orientation is horizontal.
float getKeyWidth() const noexcept
Returns the width that was set by setKeyWidth().
float getBlackNoteWidth() const noexcept
Returns the absolute width of the black notes.
void setKeyWidth(float widthInPixels)
Changes the width used to draw the white keys.
int getRangeStart() const noexcept
Returns the first note in the available range.
int getRangeEnd() const noexcept
Returns the last note in the available range.
Rectangle< float > getRectangleForKey(int midiNoteNumber) const
Returns the rectangle for a given key.
int getOctaveForMiddleC() const noexcept
This returns the value set by setOctaveForMiddleC().
NoteAndVelocity getNoteAndVelocityAtPosition(Point< float > position, bool includeChildComponents=false)
Returns the note number and velocity for a given position within the component.
Orientation getOrientation() const noexcept
Returns the keyboard's current direction.
Orientation
The direction of the keyboard.
float getWhiteNoteLength() const noexcept
Returns the absolute length of the white notes.
This class represents an instrument handling MPE.
MPEZoneLayout getZoneLayout() const noexcept
Returns the current zone layout of the instrument.
virtual void pitchbend(int midiChannel, MPEValue pitchbend)
Request a pitchbend on the given channel with the given value (in units of MIDI pitchwheel position).
MPENote getNote(int index) const noexcept
Returns the note at the given index.
bool isLegacyModeEnabled() const noexcept
Returns true if the instrument is in legacy mode, false otherwise.
void addListener(Listener *listenerToAdd)
Adds a listener.
void removeListener(Listener *listenerToRemove)
Removes a listener.
MPENote getNoteWithID(uint16 noteID) const noexcept
Returns the note with a given ID.
int getNumPlayingNotes() const noexcept
Returns the number of MPE notes currently played by the instrument.
virtual void timbre(int midiChannel, MPEValue value)
Request a third dimension (timbre) change on the given channel with the given value.
Range< int > getLegacyModeChannelRange() const noexcept
Returns the range of MIDI channels (1-16) to be used for notes when in legacy mode.
virtual void noteOn(int midiChannel, int midiNoteNumber, MPEValue midiNoteOnVelocity)
Request a note-on on the given channel, with the given initial note number and velocity.
int getLegacyModePitchbendRange() const noexcept
Returns the pitchbend range in semitones (0-96) to be used for notes when in legacy mode.
virtual void noteOff(int midiChannel, int midiNoteNumber, MPEValue midiNoteOffVelocity)
Request a note-off.
virtual void pressure(int midiChannel, MPEValue value)
Request a pressure change on the given channel with the given value.
A component that displays an MPE-compatible keyboard, whose notes can be clicked on.
void mouseUp(const MouseEvent &) override
Called when a mouse button is released.
void mouseDrag(const MouseEvent &) override
Called when the mouse is moved while a button is held down.
MPEKeyboardComponent(MPEInstrument &instrument, Orientation orientation)
Creates an MPEKeyboardComponent.
void focusLost(FocusChangeType) override
Called to indicate that this component has just lost the keyboard focus.
void colourChanged() override
This method is called when a colour is changed by the setColour() method, or when the look-and-feel i...
virtual ~MPEKeyboardComponent() override
Destructor.
void mouseDown(const MouseEvent &) override
Called when a mouse button is pressed.
This class represents a single value for any of the MPE dimensions of control.
static MPEValue centreValue() noexcept
Constructs an MPEValue corresponding to the centre value.
static MPEValue fromUnsignedFloat(float value) noexcept
Constructs an MPEValue from a float between 0.0f and 1.0f.
MPEZone getLowerZone() const noexcept
Returns a struct representing the lower MPE zone.
static bool callAsync(std::function< void()> functionToCall)
Asynchronously invokes a function or C++11 lambda on the message thread.
static String getMidiNoteName(int noteNumber, bool useSharps, bool includeOctaveNumber, int octaveNumForMiddleC)
Returns the name of a midi note number.
Contains position and status information about a mouse event.
MouseInputSource source
The source device that generated this event.
const float pressure
The pressure of the touch or stylus for this event.
const Point< float > position
The position of the mouse when the event occurred.
bool isPressureValid() const noexcept
Returns true if the pressure value for this event is meaningful.
int getDistanceFromDragStartY() const noexcept
Returns the difference between the mouse's current y position and where it was when the button was la...
int getDistanceFromDragStartX() const noexcept
Returns the difference between the mouse's current x position and where it was when the button was la...
A pair of (x, y) coordinates.
Manages a rectangle and allows geometric operations to be performed on it.
Point< ValueType > getCentre() const noexcept
Returns the centre point of the rectangle.
Rectangle< float > toFloat() const noexcept
Casts this rectangle to a Rectangle<float>.
void stopTimer() noexcept
Stops the timer.
void startTimerHz(int timerFrequencyHz) noexcept
Starts the timer with an interval specified in Hertz.
CriticalSection::ScopedLockType ScopedLock
Automatically locks and unlocks a CriticalSection object.
unsigned short uint16
A platform-independent 16-bit unsigned integer type.
constexpr Type jmap(Type value0To1, Type targetRangeMin, Type targetRangeMax)
Remaps a normalised value (between 0 and 1) to a target range.
constexpr Type jmin(Type a, Type b)
Returns the smaller of two values.
constexpr Type jmax(Type a, Type b)
Returns the larger of two values.
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
Constrains a value to keep it within a given range.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
@ press
Represents a "press" action.
unsigned char uint8
A platform-independent 8-bit unsigned integer type.
int roundToInt(const FloatType value) noexcept
Fast floating-point-to-integer conversion.
void paint(Graphics &g) override
Components can override this method to draw their content.
uint16 noteID
A unique ID.