Keyboard#

[1]:
from manimusic import *
config.media_embed = True; config.media_width = "100%"
_RV = "-v WARNING -qm --progress_bar None --disable_caching Example"
_RI = "-v WARNING -s --progress_bar None --disable_caching Example"
_RV_mid = "-v WARNING -qm -r 1600,400 --progress_bar None --disable_caching Example"
_RI_mid = "-v WARNING -s -r 1600,400 --progress_bar None --disable_caching Example"

NORMAL_RENDER = False
Manim Community v0.17.3

Creation#

[2]:
class Example(Scene):
  def construct(self):
    keyboard = Keyboard()
    self.play(ShowKeyboard(keyboard))
    self.wait()

%manim $_RV_mid
class ShowKeyboard(LaggedStart):
  def __init__(self, keyboard: Keyboard, anim=GrowFromEdge, *args, lag_ratio=0.05, **kwargs):
      self.keyboard = keyboard
      if DOWN not in args and anim == GrowFromEdge and "edge" not in kwargs.keys():
         args = [UP, *args]
      super().__init__(*[
          anim(key, *args, **kwargs)
          for key in keyboard
        ],
        lag_ratio=lag_ratio,
      )

Warning

If you are going to manipulate the keyboard, it is advisable to move the black keys to the front.

[3]:
class Example(Scene):
  def construct(self):
    self.camera.background_color = WHITE
    k = Keyboard(octaves=3)
    k.save_state()

    self.play(ShowKeyboard(k))
    self.add_foreground_mobjects(*k.black_keys)
    self.play(k.animate.arrange(RIGHT, aligned_edge=UP, buff=0))
    # NOTE: Move keys like stairs
    k.generate_target()
    k.target.to_edge(DOWN)
    for i in range(len(k)-1):
      k.target[i+1].shift(UP * 0.15 * i)
    self.play(MoveToTarget(k))
    self.wait()
    self.play(Restore(k))
    self.wait()

%manim $_RV

Chords#

[4]:
class Example(Scene):
  def construct(self):
    keyboard = Keyboard(octaves=5, ref=1)

    chord_1 = keyboard.get_chord(0, "C0", "E0", "G0", "C0").set_color(RED)
    chord_2 = keyboard.get_chord(2, "C0", "E0", "G0", "C0").set_color(BLUE)
    #                             octaves
    chord_3 = keyboard.get_chord(3, "F0", "F1", "A0").set_color(YELLOW)

    self.play(ShowKeyboard(keyboard))
    self.add(chord_1, chord_2, chord_3)

%manim $_RI_mid
_images/CHP_3_7_0.png
[5]:
class Example(Scene):
  def construct(self):
    keyboard = Keyboard(octaves=5, ref=1)

    # C, D, F, G, A can have #
    # D, E, G, A, B can have b

    chord_1 = keyboard.get_chord(0, "C#0", "E0", "G0", "C0").set_color(RED)
    chord_2 = keyboard.get_chord(2, "C0", "E0", "Gb0", "C0").set_color(BLUE)
    #                             diminished octaves
    chord_3 = keyboard.get_chord(3, "F#0", "F0", "Ab0").set_color(YELLOW)

    self.play(ShowKeyboard(keyboard))
    self.add(chord_1, chord_2, chord_3)

%manim $_RI_mid
_images/CHP_3_8_0.png
[6]:
class Example(Scene):
  def construct(self):
    keyboard = Keyboard(octaves=3, ref=1, chord_colors=[PINK, RED])
    chord_1 = keyboard.get_chord(0, *fm("Ab0|A#1|B0"))

    self.play(ShowKeyboard(keyboard))
    self.add(chord_1)

%manim $_RI_mid
_images/CHP_3_9_0.png

Sounds function#

Remember that Jupyter Notebook does not support audio, so we will have to render the scenes using the render method.

[7]:
def render(scene):
  with tempconfig({
    "frame_rate": 30,
    "quality": "medium_quality",
    "preview": True,
    "disable_caching": True, # <- For sounds
    "verbosity": "WARNING"
  }):
    scene().render()
[8]:
class Example(Scene):
  def construct(self):
    keyboard = Keyboard(octaves=5, ref=1)

    chord_1 = keyboard.get_chord(0, "C0", "E0", "G0", "C0")
    chord_2 = keyboard.get_chord(2, "C0", "E0", "G0", "C0")
    chord_3 = keyboard.get_chord(3, "F0", "F1", "A0")

    self.add(keyboard, keyboard.black_keys)

    self.play(
      FadeIn(chord_1),
      KeyboardSound(keyboard, chord_1)
    )
    self.play(
      FadeIn(chord_2),
      KeyboardSound(keyboard, chord_2)
    )
    self.play(
      FadeIn(chord_3),
      KeyboardSound(keyboard, chord_3, sound_kwargs={"gain":-18})
      # Scene.add_sound(file, **sound_kwargs)
    )

if NORMAL_RENDER:
  render(Example)

%manim $_RV_mid
[9]:
class Example(Scene):
  def construct(self):
    keyboard = Keyboard(octaves=5, ref=1)
    arpeggio = VGroup(*[
      keyboard.get_chord(0, ch)
      for ch in fm("C#0|F0|G#0|C#1")
    ])
    self.add(keyboard, keyboard.black_keys)
    sound_kwargs={
      # "gain": -15
    }

    indexes = []
    for ch in arpeggio:
      self.play(
        GrowFromCenter(ch),
        KeyboardSound(keyboard, ch, sound_kwargs=sound_kwargs)
      )
      indexes += ch.indexes
    self.play(
      arpeggio.animate.set_color(YELLOW),
      KeyboardSoundIndexes(keyboard, indexes, sound_kwargs=sound_kwargs)
    )
    self.wait()

if NORMAL_RENDER:
  render(Example)

%manim $_RV_mid

All together#

[10]:
class Example(Scene):
  def construct(self):
    colors = [RED, TEAL, ORANGE, BLUE]
    staff = Staff(clefs="gf", width=12, height=0.8).to_edge(UP)
    k = Keyboard(octaves=4, chord_colors=colors, ref=1).to_edge(DOWN)
    k_chords = VGroup(
      k.get_chord(1, "C0", "G0", "Eb0", "C0"),
      k.get_chord(0, "F0", "Ab1", "F0", "C0"),
      k.get_chord(0, "G0", "G1", "D0", "Bb0"),
      k.get_chord(0, "C0", "G1", "Eb0", "C0"),
    )

    chords = HarmonyProgression(
      staff,
      "c|Cd  ,Gu |Ebd,C1u",
      "c|F-1d,Abu|Fd ,C1u",
      "c|G-1d,Gu |Dd ,Bbu",
      "m|C-1d,Gu |Ebd,C1u",
      colors=k.chord_colors
    )

    t_chords = VGroup(*[
      MathTex("\\rm %s"%t_name)
        .next_to(staff, DOWN, buff=0.6)
        .match_x(c)
      for c,t_name in zip(chords, "I-|IV-|V-|I-".split('|'))
    ])

    self.play(
      ShowStaff(staff),
      ShowKeyboard(k),
      run_time=2
    )
    self.play(
      chords.show_chord(0, Write),
      KeyboardSound(k, k_chords[0], pause=1),
      LaggedStartMap(GrowFromCenter, k_chords[0]),
      Write(t_chords[0])
    )
    for i in range(1,len(chords)):
      self.play(
        *chords.transform(i-1, i),
        ReplacementTransform(k_chords[i-1], k_chords[i]),
        Write(t_chords[i]),
        run_time=2
      )
      self.play(KeyboardSound(k, k_chords[i]), run_time=0.1)
    self.wait()

if NORMAL_RENDER:
  render(Example)

%manim $_RV