(Topic ID: 114187)

Gottlieb System 80b - Raspberry Pi Soundboard Emulator

By jhanson

9 years ago


Topic Heartbeat

Topic Stats

  • 28 posts
  • 10 Pinsiders participating
  • Latest reply 7 years ago by twinankhs
  • Topic is favorited by 15 Pinsiders

You

Linked Games

No games have been linked to this topic.

    Topic Gallery

    View topic image gallery

    sys80-rpi.jpg

    You're currently viewing posts by Pinsider jhanson.
    Click here to go back to viewing the entire thread.

    13
    #1 9 years ago

    So, my fun little project has yielded some results. I built myself a little interface board (ugly as heck but it works) that lets me connect a Raspberry Pi to my Gottlieb System 80b machine via the wiring harness that connects to the soundboard. No modifications need to be made to the machine's wiring - I can pull this out and put the soundboard back in any time. It sends the control signals to the Pi's GPIO header for processing, 5v from the game to power the Pi, and the audio output from the Pi to the game's audio amp.

    I wrote a little Python script (also ugly since I had never used Python before) that decodes the signals and plays the appropriate sound effect (stored as a wav file). The script is still a little rough around the edges since it needs more logic around which sounds are allowed to overlap each other and which ones should stop the previous sound before starting, but all the basics are there and it plays the right sounds at the right times, just like the original soundboard.

    Once I figure out if there's a pattern to what is allowed to play over each other (I already figured out the easy ones - background music), I'll use that info to finish up the standard emulation script. I'm also planning to work on an enhanced version of the script that will be selectable via a switch I'll be adding to the board. The enhanced version will ignore music changing signals and play random selections from a playlist, plus monitor for contextual information about the game - like if no sound has been triggered in 30 seconds and the game over sound wasn't the last thing played, play a sound or something. I might also add a header to the board to allow, with a couple of extra wires run through the machine, the two buttons on the front of the cabinet (normally used for selecting initials if you get a high score) to skip forward and backward through the musical playlist.

    By the way, the crazy resistor in the pic below is there because I figured out at the last minute that I needed a pull up resistor for one of the signal lines (but none of the other four, oddly enough). It will find a permanent home later.

    My ugly sys80b to Raspberry Pi adapterMy ugly sys80b to Raspberry Pi adapter

    My ugly code (sorry the tabs disappeared, also not the most recent but not far off):

    import pygame.mixer
    from time import sleep
    import RPi.GPIO as GPIO
    from sys import exit
    #import sys

    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(40, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) #1
    GPIO.setup(37, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) #2
    GPIO.setup(35, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) #4
    GPIO.setup(33, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) #8
    GPIO.setup(31, GPIO.IN, pull_up_down=GPIO.PUD_UP) #16
    GPIO.setup(29, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) #reset
    #GPIO.setup(7, GPIO.IN) #aux input 1
    #GPIO.setup(11, GPIO.IN) #aux input 2
    #GPIO.setup(13, GPIO.IN) #aux input 3
    #GPIO.setup(15, GPIO.IN) #aux input 4

    pygame.mixer.init(22050, -16, 2, 1024)

    TOTALSOUNDS = 31
    SOUNDFOLDER = "./sounds/"
    SOUNDNAME = "sound"
    SOUNDEXT = ".wav"

    pygame.mixer.set_num_channels(3)

    snd = [""]
    soundChannel = [pygame.mixer.Channel(0), pygame.mixer.Channel(1), pygame.mixer.Channel(2)] #[""]
    sndMatrix = ["0","0","0","0","0"]
    lastplayed = 1
    debounce = 0
    reset = 0
    snch = 1
    sloop = 0

    for x in range(1, TOTALSOUNDS+1):
    snd = snd + [pygame.mixer.Sound(SOUNDFOLDER+SOUNDNAME+str(x)+SOUNDEXT)]

    print "Soundboard Ready."
    soundChannel[1].play(snd[18])

    while True:
    try:
    if (GPIO.input(40) == True):
    sndMatrix[0] = "0"
    else:
    sndMatrix[0] = "1"
    if (GPIO.input(37) == True):
    sndMatrix[1] = "0"
    else:
    sndMatrix[1] = "1"
    if (GPIO.input(35) == True):
    sndMatrix[2] = "0"
    else:
    sndMatrix[2] = "1"
    if (GPIO.input(33) == True):
    sndMatrix[3] = "0"
    else:
    sndMatrix[3] = "1"
    if (GPIO.input(31) == True):
    sndMatrix[4] = "0"
    else:
    sndMatrix[4] = "1"
    if (GPIO.input(29) == True):
    reset = 0
    else:
    reset = 1
    sndtoplay = sndMatrix[4]+sndMatrix[3]+sndMatrix[2]+sndMatrix[1]+sndMatrix[0]
    # #temp keyboard code
    # sndtoplay = sys.stdin.readline()
    # if "r" in sndtoplay:
    # reset = 1
    # sndval = 0
    # else:
    # sndval = int(sndtoplay,2)
    # #end temp keyboard code - uncomment next line
    sndval = int(sndtoplay,2)
    snch = 1
    sloop = 0
    sleepval = 0.001
    if sndval == 16:
    continue
    if sndval < 9:
    snch = 0
    if sndval <> debounce:
    debounce = sndval
    continue
    if sndval > 8 and sndval < 16:
    snch = 2
    sloop = -1
    sleepval = 0.005
    if sndval == 12: #end game music, don't loop
    sloop = 0
    if sndval > 15 and sndval < TOTALSOUNDS:
    if sndval <> debounce:
    debounce = sndval
    continue
    if reset == 1:
    soundChannel[lastplayed].stop()
    reset = 0
    print "stop "+str(lastplayed)
    if sndval > 0 and sndval < TOTALSOUNDS:
    if soundChannel[snch].get_sound() == snd[sndval]:
    print ('.'),
    sleep(sleepval)
    continue
    if sndval > 0 and sndval < 8:
    soundChannel[0].play(snd[sndval],sloop)
    lastplayed = snch
    soundChannel[1].stop()
    #soundChannel[1].fadeout(1000)
    print "play "+str(sndval)
    elif sndval == 8:
    soundChannel[0].play(snd[sndval],sloop)
    print "play "+str(sndval)
    elif sndval > 8 and sndval < TOTALSOUNDS:
    soundChannel[snch].play(snd[sndval],sloop)
    lastplayed = snch
    print "play "+str(sndval)
    elif sndval == TOTALSOUNDS:
    pygame.mixer.stop()
    print ('!'),
    elif sndval > TOTALSOUNDS or sndval < 0:
    print "invalid code - "+str(sndval)
    #print sndtoplay
    sleep(sleepval)
    except KeyboardInterrupt:
    exit()

    Post edited by jhanson: Updated code

    #3 9 years ago

    It was mostly academic, really. Right now I'm playing with my Gold Wings machine and want to add some Top Gun quotes and music to it. But as cheesy as the backglass is, the sound on that machine really isn't terrible so that probably won't be its permanent home. Ultimately I want to use it to improve the sound on a different machine, like an earlier System 80 that has all the beeps and boops instead of proper sound effects and background music. The interface and logic will be basically the same - I'll just have to swap in a different connector.

    #5 9 years ago

    Yeah, sorry, I should have probably said rewire the connector. Here's the reference I've been using: http://www.papinball.com/tips/gtb_sound.html

    Need to be careful because there's 12v where signal lines are and vice versa. Could smoke a board really easily.

    #7 9 years ago

    If you search for "JAMMA fingerboard" on eBay, you should see some 28x2 pin boards for like $4.50 shipped. I got one of those, cut it down to size, and dremeled the slots in it to fit the keyed connector.

    If I had been more confident in my ability to get everything right, I would have avoided the mess of wires by gluing and bolting the edge connector right to the board. Maybe version 2.

    #9 9 years ago

    Thanks! Arduino would definitely work. If the wav trigger board can play multiple wavs at the same time, you're set. Otherwise you'll need multiple wav trigger boards. I just went with the Pi because I wanted to try one out.

    You're looking at 6 input lines (5 data, 1 reset), all either high or low, and you need to output an audio signal. The inputs need a simple voltage divider to bring it down from 5v to 3.3v. That's what all those resistors you see on the board are (2 for each input). The only catch is input #16, on which you'll need a pull up resistor to get a usable signal, which is why you see the stray resistor in the pic.

    The base logic is simple. The sound files are numbered from 1 to 31, and you use the 5 inputs to come up with the number using binary math (each input is 0 or 1). You multiply input 1 by 1, 2 by 2, 4 by 4, 8 by 8, and 16 by 16 to get the number of the sound file being requested. So if you're getting a 1 on input 16 and a 1 on input 2, that's 16+2, or sound #18 being requested. If all the inputs are 0, there's no sound being requested.

    The part I'm still cleaning up is knowing which sounds are allowed to overlap and which ones immediately stop the currently playing sound. On my game I know that sounds 9-15 are all music, so I play them on their own channel that doesn't stop the other sounds. There are some others that sound like they should overlap a bit too, whereas right now they're getting stopped and the next one is started. My guess is that it's all of them that use input 8, or some other simple pattern like that, but I haven't watched the numbers closely enough yet to validate that guess.

    By the way, when I say watch the numbers, I mean it literally. I have a wifi dongle on the Raspberry Pi that I configured to connect to my network when the machine's on. And I use my laptop to SSH into it and watch the debug output, which prints to the screen which number is being requested when.

    #11 9 years ago

    You're right about #16 being transistor driven. I found some YouTube video where the guy is testing sound signals on an 80b machine and says something about it being different because it comes from the driver board while the others don't. I wish I had seen that video before doing this because then instead of pulling it up to 5v and then dividing it to get it back down to 3.3v, I might have been able to just pull it straight up to 3.3v using the 3.3v pin on the GPIO header and be done with it. That just seems like it would be more efficient.

    #13 9 years ago

    Cool. Thanks for the clarification!

    #15 9 years ago

    The .wav files were captured by running the ROM in PinMAME using its "sound command mode." Then I played each one individually, hitting F5 before/after each one (which saves them as a wav). Finally, I took each one and edited them in Audacity to strip out the silence at the beginning/end of each one.

    I feel like some of the sound effects should have been louder or quieter than what PinMAME outputted, so I played with normalizing them, amplifying certain ones, etc. I don't have it perfect, but it's close enough for my testing.

    One thing I noticed when doing that is that the music sounds were either really, really long, or somehow PinMAME knows to loop them. So instead of having 15 minute long wav files to contend with, I just trimmed them in Audacity so they loop correctly after 30-60 seconds. My code above handles the looping (that's what the "sloop=-1" is for).

    I'm considering sticking a text file in with the sound files that gets read before the sounds are loaded that lets you flag which ones loop and which ones play on each channel. That way if other people want to use the code, and not every system 80b uses sounds 9-15 for music (for example), it'll be easy for them to change without having to learn Python.

    #17 9 years ago

    @BloodyCactus - Correct and correct. That's why I put voltage dividers in to take each signal down to a little less than 3.3v.

    #19 9 years ago

    Nope, the only pull up is on #16. BTW, in case anybody else wants to do this, for the voltage dividers I used a 2.2K Ohm resistor first and a 3.3K second. That brings the 5v down to 3v, which is more than enough to trigger a high on the GPIO input but low enough to leave you some head room.

    #20 9 years ago

    I updated the code at the top to be my latest version. It's pretty accurate, at least on my Gold Wings. It overlaps the sounds where it should, polls frequently enough as not to miss anything, and doesn't play any extraneous sounds.

    I found that my original code played an extra sound every once in a while. The problem seemed to be that every so often I would just happen to poll for signals while one was being changed, so if the sound about to be played had 4 high signals, and the polling happened when only 3 of the 4 had been set, you'd get the wrong sound, immediately followed by the correct one. So I added "debounce" (couldn't think of a better name for it) code that makes it wait until a signal has been the same for a couple of milliseconds before it plays it. So far I haven't had an extra sound get played since that change.

    Now that I have the standard logic working, I'm going to work on an enhanced version with some new features.

    1 week later
    #22 9 years ago
    Quoted from GetTheJackpot:

    That is why logic level converters were created, like this bidirectional one from Sparkfun;
    https://www.sparkfun.com/products/12009

    Those are great if you need to go bi-directionally. If you only need to drop from 5v to 3.3v, you can just use a couple of resistors to build a voltage divider and save some $$.

    You're currently viewing posts by Pinsider jhanson.
    Click here to go back to viewing the entire thread.

    Reply

    Wanna join the discussion? Please sign in to reply to this topic.

    Hey there! Welcome to Pinside!

    Donate to Pinside

    Great to see you're enjoying Pinside! Did you know Pinside is able to run without any 3rd-party banners or ads, thanks to the support from our visitors? Please consider a donation to Pinside and get anext to your username to show for it! Or better yet, subscribe to Pinside+!


    This page was printed from https://pinside.com/pinball/forum/topic/gottlieb-system-80b-raspberry-pi-soundboard-emulator?tu=jhanson and we tried optimising it for printing. Some page elements may have been deliberately hidden.

    Scan the QR code on the left to jump to the URL this document was printed from.