12 Notes in an Octave & 12 Steps to Put the Raspberry Pi in “Pi”ano

12 Notes in an Octave & 12 Steps to Put the Raspberry Pi in “Pi”ano

 

What if our team here at Vilros told you that you could make your own fun electronic piano, kind of like FAO Schwarz used to have?  

Well, with some ‘for’ looping, connective code between Python and Sonic Pi, as well as Minecraft software, you can do just that!

Before we start, you will need to…

Install Python Software with the following stretch of code in a terminal window: ‘sudo pip3 install python-osc.’ 

1.  Your Big Minecraft piano

Whenever Minecraft Steve steps on a key, Sonic Pi will play the notes, Minecraft will visualize the piano and input your movement on the keys, and Python will build the piano in Minecraft and allow the softwares to communicate. 

2. Allow Sonic Pi to ‘hear’ the notes

Using Open Sound Control (OSC), Sonic Pi can process aural data as sound synthesizers ‘speak’ over a network.

  • Click on Menu > Programming > Sonic Pi, and then click into Buffer 0 to start writing code.
  • Allow Sonic Pi to pick up the programming with the code below:

live_loop :listen do

use_real_time

note = sync "/osc/play_this"

play note[0]

end

    1. live_loop :listen do begins an endless live loop, and this one has appropriately been named listen.
    2. use_real_time closes the gap between stepping on the key and hearing the note.
    3. note = sync "/osc/play_this" determines how Minecraft responds when /osc/play_this is processed by Sonic Pi. The message will be the list of possible notes.
    4. play note[0] will play the appropriate note based on where you are standing.

    3. Use Python to Send a Message to Sonic Pi

    • Click Menu > Programming > Python 3 (IDLE), then File > New File to open a new Python 3 file.
    • Check the software page to ensure you’ve got the python-osc
    • The first few lines you need just import the necessary methods from the module.
    from pythonosc import osc_message_builderfrom pythonosc import udp_clientfrom time import sleep
    • UDP (User Datagram Protocol) is what OSC does to allow open communication, and is often used when computers ‘speak to one another’ over the internet. In this case, your two different programs will use it to make beautiful music together.
    • The home address of the Raspberry Pi will tell Python what to ‘talk’ to. To enable programs on the same computer to ‘talk’, set the IP address to 0.0.1.
    • UDP also uses port numbers to allow messages to be ‘heard’ by the right program; Sonic Pi will ‘listen’ with port number 4559, so Python’s messages must go there through this code: sender = udp_client.SimpleUDPClient('127.0.0.1', 4559)
    • Save (Ctrl+S) and run (F5) your code. Test the connection in the shell by typing the following: >>> sender.send_message('/play_this', 60)
    • If all is properly set up, you should middle C (note 60).
    • To make this note-playing a function, return to Python 3 file and take a single argument note to play that note:
    def play_note(#note):'''Take an integer note value and send it to Sonic Pi'''sender.send_message('/play_this', note)sleep(0.5)
    • Make as many lines of code as notes you would like to be at your disposal to play.

     4. Be The Keeper of the Keys

    The 88 keys on a standard piano are divided up into multiple octaves consisting of 12 notes/keys each.  Using the common programmer trick called decomposition, we can take a daunting task, like building a keyboard, and complete it, one note at a time.

    • Click on Menu > Games Minecraft, then Start Game, and last, Create New World.
    • To get your current real-world position, import the Minecraft Pi module and add a few more lines of code so that the start of your file looks like this:

    from pythonosc import osc_message_builder

    from pythonosc import udp_client

    from mcpi.minecraft import Minecraft

    from time import sleep

    sender = udp_client.SimpleUDPClient('127.0.0.1', 4559)

    mc = Minecraft.create()

    player_x, player_y, player_z  = mc.player.getTilePos()

     5. Plan Out the Play

    Planning and sketching out the musical scheme you want to build before you begin is something we here at Vilros highly recommend. Here is a quickly drawn image of a keyboard octave, showing the x and z block positions.

    6. Make Space

    To prevent putting your piano in an impossible place, you can make a cube full of air around Minecraft Steve by using a bulldozer function.

    def bulldozer(x, y, z):

    mc.setBlocks(x - 30, y - 3, z - 30, x + 30, y + 20, z + 30, 0)

    You can test this out by saving and running (Ctrl S = F5) your code and then calling the function, with Steve’s position, in the shell, thereby flattening the surrounding area and clearing way for a piano.

    >>> bulldozer(player_x, player_y, player_z)

     7. The Black Keys

    • Use a function, black_key, to build your first black key. The function will need to know where to place it, so an x, y, and z position in the Minecraft world will determine that:
    def black_key(x, y, z):
    • With setBlocks function, you can set a few black Minecraft blocks. If you consult our sketch, you can see that each black key is 2 by 9. Therefore, if the first block is placed at an x and z coordinate, then you need the one to its right to be placed at x + 1, and the ones below it to be placed at z + 1 up to z + 8. All the blocks can be placed at 1 block below the player’s position - y – 1 to make the piano reachable. We recommend using Obsidian with a blockId of 49:
    def black_key(x, y, z):mc.setBlocks(x, y - 1, z, x + 1, y - 1, z + 8, 49)
    • Move Minecraft Steve, run your code, and then type the following into the shell to call your function and place the first key:

    >>> black_key(player_x, player_y, player_z)

    8. The White Keys

    • According to the sketch, the white keys are 3 by 15, so set white (7) tiles (44) from x up to x + 2, and from z up to z + 14. Use a function with blockId of 44, 7.

    def white_key(x, y, z):

    mc.setBlocks(x, y - 1, z, x + 2, y - 1, z + 14, 44, 7)

    • To make the white key, run the code and type the following into the shell:

    >>> white_key(player_x, player_y, player_z) 

    9. Use a ‘For’ Loop to Set Multiple Keys

    • Use a for loop to make many keys. To test this process, click on File > New File and write some test code.
    • You can write a simple for loop in your new file. This loop will make sure each value is printed with commas separating the numbers:

    for i in range(0,18):

    print(i, end = ',')

    >>> 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18

    • You can see that the first value is 0 and the last is 18.
    • If you wanted to get every third number, then you need to add a step value to the range() function. Here, the function provides values from 0 to 19 with a step value of 3:

    for i in range(0, 18, 3):

    print(i, end = ',')

    >>> 0,3,6,9,12,15,18,

    10. Make Your Octaves

    • One octave consists of 7 white keys and 5 black ones. If you check the sketch from before, the blocks stretch from x to x + 18, so the for loop needs to place a white key every 3 blocks on the x axis from 0 up to 18. Make an octave step function accordingly for the white keys:

    def make_octave(x, y, z):

    for i in range(0, 19, 3):

    white_key(player_x + i, player_y, player_z)

    • Save and run your code, then type the function make_octave(player_x, player_y, player_z) into the shell.
    • The black keys need to be placed every third block on the x-axis starting at x = 2. At the end of the make_octave function, add another for loop:

    for i in range(2, 18, 3):

    black_key(player_x + i, player_y, player_z)

    • Save and run your code again, then call the make_octave function.
    • There’s one key too many; a black key has been placed at x = 8. Use a conditional statement to fix this small issue; ‘if the value of i is 8 then the black_key function should not be called’:

    def make_octave(x, y, z):

    for i in range(0, 19, 3):

    white_key(player_x + i, player_y, player_z)

    for i in range(2, 18, 3):

    if i != 8:       ## leave a space as only 5 black keys

    black_key(player_x + i, player_y, player_z)

    • Save and run your code, and then call the make_octave function. This should produce 7 white keys and 5 black ones.

    11. Octave-a-thon!

    • You can now add the functions to your code with just use three lines; bulldoze the area, make the piano, and set the player’s position, so that Steve moves to the middle of the piano:

    bulldozer(player_x, player_y, player_z)

    make_octave(player_x, player_y, player_z)

    mc.player.setPos(player_x + 8, player_y + 3, player_z + 12)

    • Now each time you run the code, Minecraft Steve will get a new octave.

    12. Play Your Piano!

    • All that piano-building has to be for something, right? To play!
    • First, create infinite loop that will constantly get Steve’s latest position with this code that must be inside this while True loop:
    while True:new_x, new_y, new_z = mc.player.getTilePos()
    • Next, you need to find the key near Steve’s feet, despite the fact that the white keys are shorter than the black ones. If Steve is standing on a white tile, because of their smaller height, block_below ends up being the air that’s beneath the piano. To fix this, write a conditional, and check if the block below is a white or black key:
      block_below = mc.getBlock(new_x, new_y - 1, new_z)if block_below != 44 and block_below != 49:block_below = mc.getBlock(new_x, new_y, new_z)
    • Now, find Steve’s position relative to the piano. The piano was placed at player_x, but Steve is at new_x. Simply subtracting will reveal where Steve is standing on the piano:
    relative_position = player_x - new_x ## find the position on the piano
    • Now, make a list of the MIDI values of notes. Starting from middle C, the white keys are 60, 62, 64, 65, 67, 68, and 71, while black notes are all the numbers in between the white ones in each octave:
    white_notes = [60, 62, 64, 65, 67, 69, 71]black_notes = [61, 63, 66, 68, 70]
    • To determine which specific white note to play, use floor division in Python by using the // Divide Steve’s relative x position by 3 and ignoring the remainder.
    if block_below == 44:notes_along = relative_position // -3play_note(white_notes[notes_along])
    • To determine which specific black note to play, subtract 1 from Steve’s relative position, floor divide, and then subtract 1 This is because these notes are only 2 blocks wide:
    if block_below == 49:notes_along = ((relative_position - 1) // -3) - 1play_note(black_notes[notes_along])
    • Lastly, try running your code and moving over the blocks. As long as Sonic Pi is open and running your initial script, you should hear the piano being played each time Steve is on a particular key. 

    And that’s it!  If you want to challenge yourself further, you could expand your piano by calling your function in a for loop and adding more numbers to your black notes and white notes groups. Add a few LEDs and light up the keys or change colors when the keys are pressed.  Maybe the white keys could change colors not only once but EACH time they’re played. 

    If you want further information about this project, you can find out more at this step-by-step guide.

    And be sure check out Vilros for more projects! There will be something entertaining and fun at that link - we promise!


    Leave a comment

    Comments will be approved before showing up.

    GET IN TOUCH

    MAKE:INSPIRE TEAM