29constexpr uint8 whiteNotes[] = { 0, 2, 4, 5, 7, 9, 11 };
30constexpr uint8 blackNotes[] = { 1, 3, 6, 8, 10 };
36 :
Button ({}), owner (c), delta (d)
44 note = delta < 0 ? (note - 1) / 12 : note / 12 + 1;
51 void paintButton (
Graphics& g,
bool shouldDrawButtonAsHighlighted,
bool shouldDrawButtonAsDown)
override
54 shouldDrawButtonAsHighlighted, shouldDrawButtonAsDown,
84 keyWidth = widthInPixels;
93 if (scrollButtonWidth != widthInPixels)
95 scrollButtonWidth = widthInPixels;
102 if (orientation != newOrientation)
104 orientation = newOrientation;
111 jassert (lowestNote >= 0 && lowestNote <= 127);
112 jassert (highestNote >= 0 && highestNote <= 127);
113 jassert (lowestNote <= highestNote);
115 if (rangeStart != lowestNote || rangeEnd != highestNote)
117 rangeStart =
jlimit (0, 127, lowestNote);
118 rangeEnd =
jlimit (0, 127, highestNote);
119 firstKey =
jlimit ((
float) rangeStart, (
float) rangeEnd, firstKey);
126 setLowestVisibleKeyFloat ((
float) noteNumber);
129void KeyboardComponentBase::setLowestVisibleKeyFloat (
float noteNumber)
131 noteNumber =
jlimit ((
float) rangeStart, (
float) rangeEnd, noteNumber);
135 bool hasMoved = (((
int) firstKey) != (
int) noteNumber);
136 firstKey = noteNumber;
152 jassert (ratio >= 0.0f && ratio <= 1.0f);
156 blackNoteLengthRatio = ratio;
163 auto whiteNoteLength = orientation == horizontalKeyboard ?
getHeight() :
getWidth();
164 return (
float) whiteNoteLength * blackNoteLengthRatio;
169 jassert (ratio >= 0.0f && ratio <= 1.0f);
173 blackNoteWidthRatio = ratio;
180 if (canScroll != newCanScroll)
182 canScroll = newCanScroll;
188Range<float> KeyboardComponentBase::getKeyPos (
int midiNoteNumber)
const
197 return getKeyPos (midiNoteNumber).
getStart();
202 return getKeyPos (rangeEnd).
getEnd();
212 if (orientation != horizontalKeyboard)
216 if (orientation == verticalKeyboardFacingLeft)
222 return remappedXYToNote (p +
Point<float> (xOffset, 0));
229 if (pos.
getY() < blackNoteLength)
231 for (
int octaveStart = 12 * (rangeStart / 12); octaveStart <= rangeEnd; octaveStart += 12)
233 for (
int i = 0; i < 5; ++i)
235 auto note = octaveStart + blackNotes[i];
237 if (rangeStart <= note && note <= rangeEnd)
239 if (getKeyPos (note).
contains (pos.
x - xOffset))
241 return { note,
jmax (0.0f, pos.
y / blackNoteLength) };
248 for (
int octaveStart = 12 * (rangeStart / 12); octaveStart <= rangeEnd; octaveStart += 12)
250 for (
int i = 0; i < 7; ++i)
252 auto note = octaveStart + whiteNotes[i];
254 if (note >= rangeStart && note <= rangeEnd)
256 if (getKeyPos (note).
contains (pos.
x - xOffset))
258 auto whiteNoteLength = (orientation == horizontalKeyboard) ?
getHeight() :
getWidth();
259 return { note,
jmax (0.0f, pos.
y / (
float) whiteNoteLength) };
270 jassert (note >= rangeStart && note <= rangeEnd);
272 auto pos = getKeyPos (note);
273 auto x = pos.getStart();
274 auto w = pos.getLength();
282 case horizontalKeyboard:
return { x, 0, w, blackNoteLength };
283 case verticalKeyboardFacingLeft:
return { (
float)
getWidth() - blackNoteLength, x, blackNoteLength, w };
284 case verticalKeyboardFacingRight:
return { 0, (
float)
getHeight() - x - w, blackNoteLength, w };
293 case verticalKeyboardFacingLeft:
return { 0, x, (
float)
getWidth(), w };
305 octaveNumForMiddleC = octaveNum;
318 case horizontalKeyboard: angle = movesOctavesUp ? 0.0f : 0.5f;
break;
319 case verticalKeyboardFacingLeft: angle = movesOctavesUp ? 0.25f : 0.75f;
break;
320 case verticalKeyboardFacingRight: angle = movesOctavesUp ? 0.75f : 0.25f;
break;
325 path.
addTriangle (0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.5f);
329 .withAlpha (buttonDown ? 1.0f : (mouseOver ? 0.6f : 0.4f)));
338 static const float notePos[] = { 0.0f, 1 - ratio * 0.6f,
339 1.0f, 2 - ratio * 0.4f,
341 3.0f, 4 - ratio * 0.7f,
342 4.0f, 5 - ratio * 0.5f,
343 5.0f, 6 - ratio * 0.3f,
346 auto octave = midiNoteNumber / 12;
347 auto note = midiNoteNumber % 12;
349 auto start = (
float) octave * 7.0f * targetKeyWidth + notePos[note] * targetKeyWidth;
352 return { start, start + width };
360 for (
int octaveBase = 0; octaveBase < 128; octaveBase += 12)
362 for (
auto noteNum : whiteNotes)
364 const auto key = octaveBase + noteNum;
366 if (rangeStart <= key && key <= rangeEnd)
370 for (
auto noteNum : blackNotes)
372 const auto key = octaveBase + noteNum;
374 if (rangeStart <= key && key <= rangeEnd)
387 if (orientation != horizontalKeyboard)
390 auto kx2 = getKeyPos (rangeEnd).
getEnd();
392 if ((
int) firstKey != rangeStart)
394 auto kx1 = getKeyPos (rangeStart).
getStart();
396 if (kx2 - kx1 <= (
float) w)
398 firstKey = (
float) rangeStart;
404 scrollDown->setVisible (canScroll && firstKey > (
float) rangeStart);
410 auto scrollButtonW =
jmin (scrollButtonWidth, w / 2);
413 if (orientation == horizontalKeyboard)
415 scrollDown->setBounds (r.removeFromLeft (scrollButtonW));
416 scrollUp ->setBounds (r.removeFromRight (scrollButtonW));
418 else if (orientation == verticalKeyboardFacingLeft)
420 scrollDown->setBounds (r.removeFromTop (scrollButtonW));
421 scrollUp ->setBounds (r.removeFromBottom (scrollButtonW));
425 scrollDown->setBounds (r.removeFromBottom (scrollButtonW));
426 scrollUp ->setBounds (r.removeFromTop (scrollButtonW));
429 auto endOfLastKey = getKeyPos (rangeEnd).
getEnd();
431 auto spaceAvailable = w;
432 auto lastStartKey = remappedXYToNote ({ endOfLastKey - (
float) spaceAvailable, 0 }).note + 1;
434 if (lastStartKey >= 0 && ((
int) firstKey) > lastStartKey)
436 firstKey = (
float)
jlimit (rangeStart, rangeEnd, lastStartKey);
440 xOffset = getKeyPos ((
int) firstKey).
getStart();
444 firstKey = (
float) rangeStart;
447 scrollUp->setVisible (canScroll && getKeyPos (rangeEnd).
getStart() > (
float) w);
456 ? wheel.
deltaX : (orientation == verticalKeyboardFacingLeft ? wheel.
deltaY
459 setLowestVisibleKeyFloat (firstKey - amount * keyWidth);
void sendChangeMessage()
Causes an asynchronous change message to be sent to all the registered listeners.
bool contains(Point< int > localPoint)
Returns true if a given point lies within this component or one of its children.
bool reallyContains(Point< int > localPoint, bool returnTrueIfWithinAChild)
Returns true if a given point lies in this component, taking any overlapping siblings into account.
int getHeight() const noexcept
Returns the component's height in pixels.
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.
virtual void colourChanged()
This method is called when a colour is changed by the setColour() method, or when the look-and-feel i...
void addChildComponent(Component *child, int zOrder=-1)
Adds a child component to this one.
A graphics context, used for drawing a component or image.
void fillPath(const Path &path) const
Fills a path using the currently selected colour or brush.
void setColour(Colour newColour)
Changes the current drawing colour.
void fillAll() const
Fills the context's entire clip region with the current colour or brush.
A base class for drawing a custom MIDI keyboard component.
void setBlackNoteLengthProportion(float ratio) noexcept
Sets the length of the black notes as a proportion of the white note length.
void setLowestVisibleKey(int noteNumber)
If the keyboard extends beyond the size of the component, this will scroll it to show the given key a...
virtual void drawKeyboardBackground(Graphics &g, Rectangle< float > area)=0
Use this method to draw the background of the keyboard that will be drawn under the white and black n...
virtual void drawUpDownButton(Graphics &g, int w, int h, bool isMouseOver, bool isButtonPressed, bool movesOctavesUp)
This can be overridden to draw the up and down buttons that scroll the keyboard up/down in octaves.
virtual void drawWhiteKey(int midiNoteNumber, Graphics &g, Rectangle< float > area)=0
Use this method to draw a white key of the keyboard in a given rectangle.
void setOctaveForMiddleC(int octaveNumForMiddleC)
This sets the octave number which is shown as the octave number for middle C.
void resized() override
Called when this component's size has been changed.
void setScrollButtonWidth(int widthInPixels)
Changes the width used to draw the buttons that scroll the keyboard up/down in octaves.
void mouseWheelMove(const MouseEvent &, const MouseWheelDetails &) override
Called when the mouse-wheel is moved.
void setBlackNoteWidthProportion(float ratio) noexcept
Sets the width of the black notes as a proportion of the white note width.
virtual void drawBlackKey(int midiNoteNumber, Graphics &g, Rectangle< float > area)=0
Use this method to draw a black key of the keyboard in a given rectangle.
float getKeyStartPosition(int midiNoteNumber) const
Returns the position within the component of the left-hand edge of a key.
void setAvailableRange(int lowestNote, int highestNote)
Sets the range of midi notes that the keyboard will be limited to.
float getBlackNoteLength() const noexcept
Returns the absolute length of the black notes.
void setKeyWidth(float widthInPixels)
Changes the width used to draw the white keys.
void paint(Graphics &) override
Components can override this method to draw their content.
int getLowestVisibleKey() const noexcept
Returns the number of the first key shown in the component.
Rectangle< float > getRectangleForKey(int midiNoteNumber) const
Returns the rectangle for a given key.
virtual Range< float > getKeyPosition(int midiNoteNumber, float keyWidth) const
Calculates the position of a given midi-note.
KeyboardComponentBase(Orientation orientation)
Constructor.
void setScrollButtonsVisible(bool canScroll)
If set to true, then scroll buttons will appear at either end of the keyboard if there are too many n...
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 getTotalKeyboardWidth() const noexcept
Returns the total width needed to fit all the keys in the available range.
void setOrientation(Orientation newOrientation)
Changes the keyboard's current direction.
float getWhiteNoteLength() const noexcept
Returns the absolute length of the white notes.
float getBlackNoteWidthProportion() const noexcept
Returns the width of the black notes as a proportion of the white note width.
This structure is returned by the getNoteAndVelocityAtPosition() method.
static bool isMidiNoteBlack(int noteNumber) noexcept
Returns true if the given midi note number is a black key.
Contains position and status information about a mouse event.
A path is a sequence of lines and curves that may either form a closed shape or be open-ended.
void addTriangle(float x1, float y1, float x2, float y2, float x3, float y3)
Adds a triangle to the path.
AffineTransform getTransformToScaleToFit(float x, float y, float width, float height, bool preserveProportions, Justification justificationType=Justification::centred) const
Returns a transform that can be used to rescale the path to fit into a given space.
void applyTransform(const AffineTransform &transform) noexcept
Applies a 2D transform to all the vertices in the path.
A pair of (x, y) coordinates.
constexpr ValueType getY() const noexcept
Returns the point's y coordinate.
ValueType y
The point's Y coordinate.
ValueType x
The point's X coordinate.
A general-purpose range object, that simply represents any linear range with a start and end point.
constexpr ValueType getStart() const noexcept
Returns the start of the range.
constexpr ValueType getEnd() const noexcept
Returns the end of the range.
Manages a rectangle and allows geometric operations to be performed on it.
constexpr bool approximatelyEqual(Type a, Type b, Tolerance< Type > tolerance=Tolerance< Type >{} .withAbsolute(std::numeric_limits< Type >::min()) .withRelative(std::numeric_limits< Type >::epsilon()))
Returns true if the two floating-point numbers are approximately equal.
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.
float deltaY
The amount that the wheel has been moved in the Y axis.
float deltaX
The amount that the wheel has been moved in the X axis.
unsigned char uint8
A platform-independent 8-bit unsigned integer type.
Contains status information about a mouse wheel event.
Commonly used mathematical constants.