How to build a Raspberry Pi soundpad with Python and Tkinter (Easy Guide!)

Google Supports Mini Computers For Berlin Schools
Google Supports Mini Computers For Berlin Schools | Adam Berry/GettyImages

If you’re looking for a fun and practical Raspberry Pi project, creating a soundpad using Python, Tkinter, and Pygame is a great choice. This project lets you store 12 sounds in a folder that you can play by touching the buttons on the touchscreen. It’s perfect for working parts out in your home studio and is also a great tool for adding sounds to a live podcast or radio show. Best of all, it’s extremely easy. We’ll go over the code, and then you can copy and paste it to get it running.

What are we using?

I’m using the Elecrow Pi Terminal 7” with a 1024x600 IPS touchscreen and a Raspberry Pi Compute Module 4 (CM4) to run the soundpad. It does a great job and is only the tip of what it can do. However, this code should be used on most devices using a Raspberry Pi. You can also run it on Linux-based touchscreen devices and even on Android and Windows with some modifications.

How do we get the sounds?

I exported each of the sounds I used from my home studio (DAW), and I primarily used drum sounds. However, there are many ways to download free samples on the internet, and they can be anything.

Breaking down the script

The script begins by importing the necessary libraries. These libraries came preinstalled with my version of the Raspberry Pi operating system, but you may need to install them separately if you are using something else.

  • Pygame is for sound playback.
  • Tkinter creates our graphical user interface.
  • Os helps with file handling so we can get the sounds from the library

import pygame
import tkinter as tk
from tkinter import Button, messagebox
import os

Initialize the mixer module

Here, we re-initialize the sound module to ensure it plays our sounds properly. We also set the sample rate, set the bit size, and set it for a stereo sound output. We also set the buffer size and set the number of channels to 12.

pygame.mixer.quit()
pygame.mixer.init(frequency=44100, size=-16, channels=2, buffer=64)
pygame.mixer.set_num_channels(12)

You can adjust any of the settings here for your system and needs. A less powerful Raspberry Pi might need to turn up the buffer. The Elecrao Pi Terminal can likely go to down to 32 or even 16 to reduce lag without having a problem. You can change the number of sounds if you want to use more or less. Since the size is set to 16, you will only be able to use 16-bit WAV files in your folder.

Loading sound files dynamically

This next part of the script sets the directory where you will store the sounds. Change this to the folder you are using. The script then converts the sound for playback.

SOUND_FOLDER = "/home/pi/soundpad/sounds/"
sound_files = sorted([f for f in os.listdir(SOUND_FOLDER) if f.endswith(".wav")])
sounds = [pygame.mixer.Sound(os.path.join(SOUND_FOLDER, f)) for f in sound_files]

Creating a sound playback function

This next function plays a sound when called at full volume. The sounds will layer as set, but you can change “overlap”  to stop_previous to stop each sound when a new sound starts.

def play_sound(index, mode="overlap"):
sounds[index].set_volume(1.0)
if mode == "stop_previous":
sounds[index].stop()

channel = pygame.mixer.find_channel()
if channel:
channel.play(sounds[index])

Creating the Tkinter GUI

This net part of the script initializes the GUI and gives it a name. You will want to set it to the dimensions of your screen.

root = tk.Tk()
root.title("Pi Terminal Sound Pad")
root.geometry("800x480")

Top modern programming languages driving the future of development

Dynamically generating buttons

This next function sets up the buttons to trigger the sounds. There are many things you can do here, from adding colors to changing the font to suit your needs.

num_buttons = len(sound_files)
cols = 3
rows = (num_buttons // cols) + (1 if num_buttons % cols else 0)

button_width = 20
button_height = 5

buttons = []
for i, sound_name in enumerate(sound_files):
btn = Button(root, text=f"Sound {i+1}", font=("Arial", 18), width=button_width, height=button_height)
btn.config(command=lambda i=i: on_button_press(i))
btn.grid(row=i // cols, column=i % cols, padx=5, pady=5, sticky="nsew")
buttons.append(btn)

Adding an exit button

This next function adds a way to exit the script and asks for a confirmation.

exit_btn = Button(root, text="Exit", font=("Arial", 18), width=button_width, height=button_height, command=confirm_exit)
exit_btn.grid(row=rows, column=cols - 1, padx=5, pady=5, sticky="nsew")

def confirm_exit():
if messagebox.askyesno("Exit", "Are you sure you want to exit?"):
root.destroy()

Finalizing the GUI layout

This last function ensures that the grid is responsive and adjusts to the number of buttons. So it should not break if you want to change the number of sounds on the screen.

for i in range(rows + 1):
root.grid_rowconfigure(i, weight=1)
for j in range(cols):

root.grid_columnconfigure(j, weight=1)

Finally, the program enters the Tkinter event loop:

root.mainloop()

Complete Script for GeekSided Soundpad

import pygame
import tkinter as tk
from tkinter import Button, messagebox
import os

# Initialize pygame mixer
pygame.mixer.quit()
pygame.mixer.init(frequency=44100, size=-16, channels=2, buffer=64)
pygame.mixer.set_num_channels(12) # Ensure 12 channels are available

# Sound folder path
SOUND_FOLDER = "/home/pi/soundpad/sounds/"

# Load sound files
sound_files = sorted([f for f in os.listdir(SOUND_FOLDER) if f.endswith(".wav")])
sounds = [pygame.mixer.Sound(os.path.join(SOUND_FOLDER, f)) for f in sound_files]

# Function to play sound
def play_sound(index, mode="overlap"):
sounds[index].set_volume(1.0)
if mode == "stop_previous":
sounds[index].stop() # Stop previous instance before playing

# Ensure sound plays on an available channel
channel = pygame.mixer.find_channel()
if channel:
channel.play(sounds[index])

# Function to handle button press
def on_button_press(index):
play_sound(index)

# Function to confirm exit
def confirm_exit():
if messagebox.askyesno("Exit", "Are you sure you want to exit?"):
root.destroy()

# Create GUI
root = tk.Tk()
root.title("Pi Terminal Sound Pad")
root.geometry("800x480") # Adjust to screen resolution

# Adjust button sizes dynamically
num_buttons = len(sound_files)
cols = 3 # Number of columns
rows = (num_buttons // cols) + (1 if num_buttons % cols else 0)

button_width = 20 # Increased width
button_height = 5 # Increased height

# Create buttons for sounds
buttons = []
for i, sound_name in enumerate(sound_files):
btn = Button(root, text=f"Sound {i+1}", font=("Arial", 18), width=button_width, height=button_height)
btn.config(command=lambda i=i: on_button_press(i)) # Pass the index
btn.grid(row=i // cols, column=i % cols, padx=5, pady=5, sticky="nsew")
buttons.append(btn)

# Exit button
exit_btn = Button(root, text="Exit", font=("Arial", 18), width=button_width, height=button_height, command=confirm_exit)
exit_btn.grid(row=rows, column=cols - 1, padx=5, pady=5, sticky="nsew")

# Configure grid to be responsive
for i in range(rows + 1):
root.grid_rowconfigure(i, weight=1)
for j in range(cols):
root.grid_columnconfigure(j, weight=1)

root.mainloop()

What’s next?

There are many ways to expand and improve this script. You can add volume controls for each sound, set up a way to choose between different folders of sounds, and make the GUI more attractive.

Follow GeekSided for more easy scripts that work with Raspberry Pi