Once upon a time, I was 250 pounds. In January 2009, I discovered Dance Dance Revolution. Over the next nine months, I shed 90 pounds. Through diet, exercise, and consistency, I’ve kept it off since.
DDR has been a huge part of my life.
For years, I’ve played it on my softmodded Playstation 2. Sadly, it’s starting to show it’s age. The hard drive is starting to fail. CRT monitors (the big old-school tubes) are harder to find. And most recently, my RedOctane Afterburner hard pad finally threw in the towel.
I’ve repaired it quite a few times. But this time, the repair bill was greater than a new pad. With cracked panels, disintegrated foam, resoldering needed, and just general age, it was time for something new.
I invested in a pad from Maty-Taneczne.pl. Even in the midst of this global pandemic (and at order time, riots in Poland), it arrived in under a week.
The build quality of this new pad is phenomenal.
Only one problem: it doesn’t natively connect to my PS2 or softmodded Xbox original.
Oh well. Time to upgrade the setup anyway.
For a while, I’ve had a spare Raspberry Pi 3b+ lying around. This seemed like the perfect platform for DDR, or the open-source equivalent – StepMania.
I’ve looked at the Pi as a possible DDR platform for a while. However, I’ve been put off by the sketchy support, and reports of low frame rates and poor audio. That along with the need to overclock the system to get “acceptable” performance.
- No overclocking required, or minimum overclocking (stock hardware as much as possible),
- No custom kernels or weird binaries,
- Outputs to a CRT monitor so I don’t have to replace mine.
Yes, I still play DDR on my old CRT TV. Yes, I know there are monitors and projectors with low lag. I didn’t want to replace the CRT yet, since the only free wall I have is the concrete foundation, and mounting the new TV looks like a bit of a process.
First Attempt – SpottyMatt’s Raspbian Stepmania Arcade Github Repo
First, I tried Spottymatt’s automated script for the Pi.
This script auto-downloads StepMania, applies some settings for the Pi to run smoother, and sets StepMania to auto-launch on startup.
The results were as predicted – pretty poor. The build of StepMania used just isn’t optimized for the Pi. It was laggy, even at the lowest possible resolution. Not the fault of the repo author.
I did notice something though. I saw that CPU usage was pegged at 100% nearly the whole time. This would become important later.
Second Attempt – Project Moondance
After some more research, I found Project Moondance – a fork of StepMania, and it had a native ARM/Raspberry Pi build!
I uninstalled the old build of StepMania and downloaded the latest StepMania 5.3 from Moondance. When I tried to launch it, I immediately got a loading error: “error while loading shared libraries: libOpenGL.so.0:”.
Thankfully it was a simple fix, as detailed on the Project Moondance site – install libopengl0. See: https://projectmoon.dance/faq
I launched Stepmania. It immediately crashed with a GL_OUT_OF_MEMORY error.
Memory on the Pi
I’ve coded display wall engines before, and am no stranger to GL_OUT_OF_MEMORY errors. As I understand, StepMania loads textures into memory, and depending on the skin/theme you use, that can require quite a bit of GPU memory.
First, I tweaked /boot/config.txt to allocate 384 MB of the Pi’s 1 GB of RAM to the GPU.
Next, I lowered the MaxTextureResolution in ~/.stepmania-5.3/Save/Preferences.ini to be 512. I found 256 was blurry, and didn’t provide performance gains.
I launched StepMania again. The performance was better, but not phenomenal. Low 30’s for frame rate, if I had to guess. The default theme was slow too.
Turn On OpenGL Hardware Acceleration
With the CPU being hit hard by StepMania, it was a clue that OpenGL acceleration was not happening at the hardware level. After some googling, I found that you could turn on an experimental OpenGL driver.
- Run sudo raspi-config
- Go to option 6 – advanced options
- Go to A2 – GL Driver
- Select option G3 – GL Full KMS
I fired up StepMania again. Things were still laggy, so I decreased the resolution to 720×480, the maximum I would need for my CRT monitor.
While the theme was still a bit slow, in-game was just about perfect!
Output to a CRT
This was the hardest part to make work reliably. I’ll document my /boot/config.txt since I’ve had to force a specific video mode, otherwise my TV has massive sync issues.
hdmi_safe=1 hdmi_group=2 hdmi_mode=4 sdtv_mode=0 sdtv_aspect=1 dtparam=i2c_arm=off dtparam=spi=off dtparam=audio=on [all] dtoverlay=vc4-kms-v3d gpu_mem=384 [all] start_x=0 enable_uart=0
I had to force hdmi_safe on, and specify the 720×480 HDMI mode to get any picture to appear on my monitor.
I had overscan issues too – which I could not fix while the OpenGL driver was enabled. I did manage to fix this later though.
Increase Performance with the Simply Love Theme
I found the default theme to be very laggy on the Pi 3b+. Having seen the Simply Love theme, the Moondance team has a modded version available. Grab it here: https://github.com/Tiny-Foxes/Simply-Love-OutFox.
Once I switched to Simply Love, I was able to adjust Overscan as well, which fixed the cut off problems!
My next issue was with synchronization. A practice round of Cascada’s Can’t Stop the Rain netted me a poor score.
Don’t laugh. This is the first song I played, and it’s my warm-up song. I know it better than any other song in my collection. I routinely AAA it, so when my score is that far off, something’s wrong.
After talking with the community on their Discord, they suggested the problem might be dirty power introducing noise into the USB signal. To solve it, they recommended a powered USB hub for the pad.
I ordered one, hooked it up, and… it was better. But still delayed to the point where Marvelous and Perfect were registering as Great and Good.
Then I remembered the F6 key. When in song, press F6 twice to switch the machine into Autosync – Machine mode. This attempts to auto-calibrate the music and monitor sync with your steps.
And it was the same story for all the other songs. Once calibrated, the sync was perfect.
Bonus – Add a Dedicated Assist Tick Button with Phidgets and Python
In 2019, before the pandemic hit, I went to the Educational Computing Organization of Ontario’s Bring IT Together conference. There, I met the wonderful people from Phidgets Education. Phidgets has figured out how to take electrical components like LEDs, buttons, and volume knobs and snap them together like Lego.
I had a spare starter kit that has two LEDs and two buttons. These connect to a central hub, which connects to your computer via USB. Recently they brought their SDK to the Pi, so I figured this would be a good opportunity to build some “extra” controls.
I like to play with the Assist Tick on. I’ve already got enough coordination issues with my cerebral palsy, and the tick helps keep me lined up with the arrows. I followed Phidgets’ instructions on how to install the Pi SDK. It’s pretty simple – just follow the directions.
Once I had the library installed, I installed both Python 2 and 3, and installed the Phidgets22 library in both environments using Pip. I also installed pynput. This allows a Python program to send keystrokes to other X (windowed) applications.
I whipped up a quick Python script that:
- Blinks the LEDs when the script starts.
- “Presses” F7 (Assist tick) when the Red button is pressed.
- “Presses” / (Song options menu for the SuperNOVA 2 theme) when the Green button is pressed.
- Quits the script when both buttons are pressed.
from Phidget22.Phidget import * from Phidget22.Devices.DigitalOutput import * from Phidget22.Devices.DigitalInput import * import time from pynput.keyboard import Key, Controller keyboard = Controller() greenBtn = DigitalInput() greenLed = DigitalOutput() redLed = DigitalOutput() greenBtn.setHubPort(1) greenBtn.setIsHubPortDevice(True) greenLed.setHubPort(3) greenLed.setIsHubPortDevice(True) greenBtn.openWaitForAttachment(1000) greenLed.openWaitForAttachment(1000) redBtn = DigitalInput() redBtn.setHubPort(0) redBtn.setIsHubPortDevice(True) redBtn.openWaitForAttachment(1000) redLed.setHubPort(2) redLed.setIsHubPortDevice(True) redLed.openWaitForAttachment(1000) greenLed.setState(True) redLed.setState(True) time.sleep(0.2) greenLed.setState(False) redLed.setState(False) time.sleep(0.2) greenIn = False redIn = False while(True): redIn = redBtn.getState() greenIn = greenBtn.getState() if(redIn and greenIn): break if(greenIn): greenLed.setState(True) keyboard.press('/') keyboard.release('/') if(redIn): redLed.setState(True) keyboard.press(Key.f7) keyboard.release(Key.f7) if(greenIn or redIn): time.sleep(2) redLed.setState(False) greenLed.setState(False) time.sleep(0.1) redLed.setState(True) time.sleep(0.2) redLed.setState(False) time.sleep(0.2) redLed.setState(True) time.sleep(0.2) redLed.setState(False) time.sleep(0.2) greenBtn.close() greenLed.close() redBtn.close() redLed.close()
Finally, I added an “autostart” entry in ~/.config/autostart for both Stepmania and my script.
Smbhid – Stepmania HID Helper – smbhid.desktop
[Desktop Entry] Name=StepmaniaPhidgetHelper Comment=Elias Phidget Helper Exec=xterm -hold -e '/usr/bin/python3 /home/pi/p.py'
Stepmania – stepmania.desktop
[Desktop Entry] Type=Application Name=StepMania Exec=/home/pi/Stepmania-Outfox-Alpha-4.9.6-arm32v7-date-20201216/stepmania Terminal=false Type=Application NoDisplay=false
Now Stepmania and this HID helper both run at startup.
Conclusions and Future Work
In the future, I’d like to modify the pad to poll at 1000 Hz instead of the 125 Hz. I can definitely notice the difference on Expert songs. Shout-out to StarlightLumi on instructables for posting this mod. That could allow me to hook up the pad to a PS2 as well – after all, I have built my own pads in the past. Badly.
I’d like to eventually upgrade to the Pi 4 and see if that makes a performance difference. Though given my requirements to play on a CRT, I don’t know if it would be worth it.
Before anyone comments on the sync issues, I found the sync to be perfect on a laptop LCD and with that laptop projecting to an LED projector. I’m sure there’s some weirdness with the component video output + forced HDMI modes screwing up sync somewhere. After doing the machine auto-calibration though, everything has been fine.
What do you think? See anywhere I could have done better on this project? Have you built your own Stepmania rig using a Raspberry Pi? Let me know.