A Project of the Resonance Committee
An educational programming assignment where you'll implement core functionality for a virtual piano application using Python and Pygame.
This is a freshman-level programming project where you'll complete a partially-implemented virtual piano application. The visual interface is already built for you - your job is to implement the core logic functions that make the piano interactive and capable of playing MIDI files.
Estimated Time: 4-6 hours
Difficulty: Intermediate
Topics Covered: Functions, File I/O, Audio Processing, Algorithm Implementation
By completing this project, you will:
- โ Practice implementing functions from detailed specifications
- โ Work with audio libraries (pygame.mixer)
- โ Parse and process MIDI files using the mido library
- โ Implement search algorithms (binary search concepts)
- โ Apply mathematical concepts (square root for audio limiting)
- โ Handle file I/O with proper error handling
- โ Work with global variables and understand scope
The following components are fully implemented - you don't need to modify these:
- โ
Complete Visual Interface - All drawing functions (
draw_piano,draw_hands,draw_title_bar) - โ Event Handling - Mouse clicks, keyboard input, arrow keys
- โ Main Game Loop - Display updates, frame rate control
- โ Audio File Loading - All 88 piano note sound files are loaded
- โ User Interface - Instructions, logo, hand position indicators
- โ MIDI Playback Logic - The playback system in the main loop
You need to implement the following functions in the template file:
Purpose: Convert MIDI note numbers (0-127) to musical note names like "C4" or "A#5"
What You'll Learn:
- Integer division and modulo operations
- List indexing
- String formatting with f-strings
Difficulty: โญ Easy
Purpose: Load a MIDI file and convert it to a list of playable notes with timestamps
What You'll Learn:
- File I/O with error handling (try-except)
- Working with external libraries (mido)
- Parsing file formats
- Building data structures (lists of tuples)
- Dictionary lookups
Difficulty: โญโญโญ Medium
Purpose: Search through notes to find the first note at or after a given timestamp
What You'll Learn:
- Linear search algorithms
- Working with sorted data
- Loop control (break statements)
- Edge case handling
Difficulty: โญโญ Easy-Medium
Purpose: Play piano notes with intelligent volume control to prevent audio distortion
What You'll Learn:
- Audio channel management
- Dynamic volume calculations
- Using mathematical functions (square root)
- Conditional logic
- Working with pygame.mixer
Difficulty: โญโญ Medium
- Install Python 3.7+ if you haven't already
- Install required packages:
pip install pygame mido
Your project should have this structure:
arpeggio-piano/
โโโ main.py # Your template file (implement functions here)
โโโ piano_lists.py # Note mappings (already complete)
โโโ pyproject.toml # Package dependencies
โโโ assets/
โโโ Terserah.ttf # Font file
โโโ logo.png # Logo image
โโโ notes/ # Piano sound files (88 .wav files)
โ โโโ C0.wav
โ โโโ C#0.wav
โ โโโ ... (88 total)
โโโ MIDI/ # MIDI files for playback
โโโ Thomas_Bergersen_-_Made_of_Air_(2_Pianos).mid
Open main.py and read through:
- The extensive comments explaining each variable
- The detailed function specifications
- The examples provided for each function
Work on the functions in this recommended order:
- Start with
midi_to_note_name()- It's the easiest and will help you understand the note naming system - Then do
find_first_note_after()- Practice with list searching - Next tackle
play_note_with_limiter()- Learn audio management - Finally implement
load_midi_file()- The most complex function that ties everything together
After implementing each function, test it:
# Test midi_to_note_name
print(midi_to_note_name(60)) # Should print "C4" (Middle C)
print(midi_to_note_name(61)) # Should print "C#4"
# Test the piano by running the program
python main.pyLeft Hand (Octave 4 by default):
Z= CS= C#X= DD= D#C= EV= FG= F#B= GH= G#N= AJ= A#M= B
Right Hand (Octave 5 by default):
R= C5= C#T= D6= D#Y= EU= F8= F#I= G9= G#O= A0= A#P= B
- โ/โ Arrow Keys - Change left hand octave
- โ/โ Arrow Keys - Change right hand octave
- Spacebar - Play/Pause/Resume MIDI playback
- Mouse Click - Click any piano key to play it
# Remember: There are 12 notes per octave
# MIDI note 0 = C-1, note 12 = C0, note 24 = C1, etc.
# Use // for integer division and % for remainder# Don't forget the global keyword!
global playback_messages, current_msg_index, playback_active
# Use try-except to catch file errors
try:
mid = mido.MidiFile(filepath)
except Exception as e:
print(f"Error: {e}")
return False# Remember: msg[0] is the timestamp
# Use enumerate() to get both index and message
for i, msg in enumerate(playback_messages):
if msg[0] >= time_ms:
return i# The limiter prevents audio clipping when many notes play
# Volume formula: (BASE_VOLUME * limiter_factor) * velocity_factor
# Use sqrt() from math module for smooth volume reductionOnce you've implemented all functions, verify:
- Program runs without errors
- Clicking piano keys with mouse produces sound
- Typing keyboard letters plays notes
- Arrow keys change octaves (indicators move)
- Keys light up green when pressed
- Spacebar loads MIDI file (check console output)
- Spacebar starts/pauses MIDI playback
- MIDI notes play automatically during playback
- Multiple notes can play simultaneously without distortion
Solution: Install the packages:
pip install pygame midoSolution: Make sure the assets/ folder is in the same directory as main.py
Solution: Add global playback_messages at the start of your function
Solution:
- Check that
play_note_with_limiter()is fully implemented - Verify you're calling
channel.play(sound_to_play) - Make sure your system volume is up
Solution:
- Check that
load_midi_file()is fully implemented - Verify the MIDI file exists in
assets/MIDI/ - Check the console for error messages
| Component | Points | Criteria |
|---|---|---|
midi_to_note_name() |
15 | Correctly converts MIDI numbers to note names |
find_first_note_after() |
15 | Properly searches and returns correct index |
play_note_with_limiter() |
30 | Plays sounds with proper volume limiting |
load_midi_file() |
30 | Successfully loads and parses MIDI files |
| Code Quality | 10 | Clean code, proper comments, follows style |
| Total | 100 |
Once you've completed the basic assignment, try these challenges:
- Add Recording Feature - Record what the user plays and save it as a MIDI file
- Volume Control - Add slider to adjust overall volume
- More MIDI Files - Load different songs from a menu
- Visual Note Display - Show falling notes like Guitar Hero
- Pedal Simulation - Implement sustain pedal (hold notes longer)
- Metronome - Add a visual/audio beat counter
- Chord Detection - Display what chord is being played
If you're stuck:
- Read the documentation in the template file carefully
- Check the variable summary at the bottom of the file
- Use print statements to debug your code
- Test functions individually before running the whole program
- Ask your instructor or TA during office hours
- Search the error message on Google or Stack Overflow
When you're ready to submit:
- Test thoroughly - Run through the testing checklist above
- Comment your code - Explain your logic
- Submit only
main.py- Don't modify other files - Include a brief writeup (optional):
- Which function was hardest?
- What did you learn?
- How long did it take?
You'll know you're successful when:
- โ All 4 functions are implemented
- โ The program runs without errors
- โ You can play notes with mouse and keyboard
- โ MIDI playback works correctly
- โ Audio doesn't distort with many simultaneous notes
- โ You understand how each function works
Phase: Introduction to Programming
Instructors:
- Seif Zakaria
- Ahmed Khalid
- Bishoy Ehab
- Mohamed Nasser
A Project of the Resonance Committee
Good luck, and happy coding! ๐น๐ต