import pygame.mixer
pygame.mixer.init()

"""
Explanation: React to the Raspberry Pi GPIO pins
This IPython Notebook teaches us to connect hardware inputs to software. We will connect a button to the General Purpose Input/Output (GPIO) pins* on the Raspberry Pi and when the button is pushed, make the Pi run a function.
The GPIO pins are the 40 (numbered) pins that electronic components can be connected to.

But let's first make a function that the Pi can execute. There are a few sounds installed on the Pi in the form of .wav files (something similar to .mp3 files) and there is a library on the Pi that can play these sounds (pygame; a library used to develop games in Python)
So, we will import the mixer module from the pygame library and initialise it:
IPython Instructions:
Place your cursor in the cell below and press Shift+Enter or click the Play button in the toolbar above to execute the code in the cell.

Shift + Enter: Execute the cell and jump to the next one
Ctrl + Enter: Execute the cell, but stay in the current one
Alt + Enter: Execute the cell and create a new one below the current one

As long as a [*] appears to the left os the cell, it is still running. As soon as the code ends, a number appears and any output generated by the code is printed under the cell.
End of explanation
"""


drum = pygame.mixer.Sound("/opt/sonic-pi/etc/samples/drum_tom_mid_hard.wav")
drum.play()

"""
Explanation: We will now ask the pygame library to load a .wav file into memory and make it into a sound, ready to play.
(and because we can't control ourselves, we play it once; do not forget to attach boxes or headphones)
note: we get the .wav file from a folder that is installed as part of the Sonic Pi software
End of explanation
"""


def play():
    print("Drums !")
    drum.play()

"""
Explanation: The play() method of the drum sound can be put in a selfmade function, so we can add stuff and reuse it later.
End of explanation
"""


play()

"""
Explanation: Let's run our new play() function
End of explanation
"""


#load GPIO library
import RPi.GPIO as GPIO
#Set BCM (Broadcom) mode for the pin numbering
GPIO.setmode(GPIO.BCM)

#activate pin 17 as input and set the built-in pull-up/pull-down resistor to pull-up
GPIO.setup(17, GPIO.IN, GPIO.PUD_UP)

"""
Explanation: Time to connect a button I would say. We'll use the BCM numbering, the way they are engraved on the case we're using and on the GPIO pinout document.
Use the following illustration as a guide:
<img src="MusicBox01.png" height="300"/>
End of explanation
"""


def play(pin_number):
    print("Drum roll !")
    drum.play()

GPIO.add_event_detect(17, GPIO.FALLING, play, 200)

"""
Explanation: After this preparation, we can ask Python to "register an event".
We want our function to be called each time a certain event occurs, namely a "FALLING" event on pin 17. Because this (the voltage dropping on pin 17) means the button has been pressed. This can be done by attaching the function to the event with the GPIO.add_event_detect() function.
We will pass 4 parameters to the add_event_detect() function:
17:  the pin number to listen on
GPIO.FALLING:  the kind of event to react to
play:  the function to call when the event is detected
200:  the number of milliseconds to wait after an event detection before starting to listen again
There is, however, one change we have to make to our play() function.
The add_event_detect function wants to tell the play() function which pin the event came from, wo we need to add that as a parameter to the play() function, even if we don't use it (yet):
End of explanation
"""


GPIO.remove_event_detect(17)

"""
Explanation: You can test it out by pressing the button.
If you're happy with the result, you can remove the event registration again with the following function:
End of explanation
"""


cymbal = pygame.mixer.Sound("/opt/sonic-pi/etc/samples/drum_cymbal_open.wav")
cymbal.play()

"""
Explanation: Ok, this was so much fun, let's do another one:
End of explanation
"""


sound_pins = {
    17: drum,
    27: cymbal,
}

"""
Explanation: This may be a good moment to show how to make our function more generic and reusable.
We'll store the sounds in a dictionary, where the pin number is the key and the sound is the value linked to that key:
End of explanation
"""


sound_pins[17].play()

"""
Explanation: So we can now use (read: play) the sound object by pointing to the dictionary element with the pin number as the key.
Let's try it out?
End of explanation
"""


def play(pin):
    sound = sound_pins[pin]
    print("Geluid spelen voor pin %s" % pin)
    sound.play()

"""
Explanation: Which means that, instead of making two seperate functions that we register one by one, we can now create one funcction that uses the pin number as input to play the appropriate tune:
End of explanation
"""


play(27)

"""
Explanation: Play the cymbal:
End of explanation
"""


play(17)

"""
Explanation: And the drums:
End of explanation
"""


for pin in sound_pins:
    GPIO.setup(pin, GPIO.IN, GPIO.PUD_UP)
    GPIO.add_event_detect(pin, GPIO.FALLING, play, 200)

"""
Explanation: Let's build the schema below to get our first, very rudimentary, music box:  
<img src="MusicBox02.png" height="300" />
Thanks to our more generic function, we can now "automate" the pin setup and the registration of events with the appropriate buttons and sounds by looping over the keys in the dictionary.
End of explanation
"""


for pin in sound_pins:
    GPIO.remove_event_detect(pin)

#clean up the GPIO settings again
GPIO.cleanup()

"""
Explanation: Music time!
try it out...
Removing the registrations again happens in much the same way of course.
End of explanation
"""


