Is there any way to include all of the sound effects for my pygame game into my .exe with pyinstaller?

Viewed 76

I have made a relatively simple pygame program with 9 different sound effects (.wav format, if this information is important). When converting my main.py to an executable file with the pyinstaller module, is there any way I can get the sound effects to be included in the executable, or will I need to find a work-around (such as having the sound effects in the C:\Program Files path)?

Here's the contents of my .spec file:

# -*- mode: python ; coding: utf-8 -*-


block_cipher = None

added_files = [('C:\\Users\\joshu\\CursorHunters\\Assets\\Sounds\\*.wav', 'Sounds')]

a = Analysis(['main.py'],
             pathex=[],
             binaries=[],
             datas=added_files,
             hiddenimports=[],
             hookspath=[],
             hooksconfig={},
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)

exe = EXE(pyz,
          a.scripts,
          a.binaries,
          a.zipfiles,
          a.datas,  
          [],
          name='Cursor Hunters',
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          upx_exclude=[],
          runtime_tmpdir=None,
          console=False,
          disable_windowed_traceback=False,
          target_arch=None,
          codesign_identity=None,
          entitlements_file=None )

As you can see, the sounds are located in \C:\Users\joshu\CursorHunters\Assets\Sounds, and my main.py is located in \C:\Users\joshu\CursorHunters.

Instead of packing the .wav files together with the .exe file, it returns this error when ran without the Assets\Sounds folder in the same directory: Error message when .exe is ran without the Assets\Sounds folder in the same directory.

This is how I access my .wav files in sound_effects.py followed by main.py:

import os.path, pygame, sys

pygame.mixer.init()


class SoundEffects(object):


    def __init__(self,
                 EXE_LOCATION: str):
        self.explosion1 = pygame.mixer.Sound(os.path.join(EXE_LOCATION, "Assets", "Sounds", "explosion1.wav"))
        self.explosion2 = pygame.mixer.Sound(os.path.join(EXE_LOCATION, "Assets", "Sounds", "explosion2.wav"))
        self.explosion3 = pygame.mixer.Sound(os.path.join(EXE_LOCATION, "Assets", "Sounds", "explosion3.wav"))
        self.explosion4 = pygame.mixer.Sound(os.path.join(EXE_LOCATION, "Assets", "Sounds", "explosion4.wav"))
        self.explosion5 = pygame.mixer.Sound(os.path.join(EXE_LOCATION, "Assets", "Sounds", "explosion5.wav"))
        self.explosion_sounds = (self.explosion1,
                                 self.explosion2,
                                 self.explosion3,
                                 self.explosion4,
                                 self.explosion5)
        self.gameover1 = pygame.mixer.Sound(os.path.join(EXE_LOCATION, "Assets", "Sounds", "gameover1.wav"))
        self.gameover2 = pygame.mixer.Sound(os.path.join(EXE_LOCATION, "Assets", "Sounds", "gameover2.wav"))
        self.gameover3 = pygame.mixer.Sound(os.path.join(EXE_LOCATION, "Assets", "Sounds", "gameover3.wav"))
        self.gameover_sounds = (self.gameover1,
                                self.gameover2,
                                self.gameover3)
        self.restart_sound = pygame.mixer.Sound(os.path.join(EXE_LOCATION, "Assets", "Sounds", "restart.wav"))

main.py:

if getattr(sys, "frozen", False):  # Gets the directory of this script
    EXE_LOCATION = os.path.dirname(sys.executable)

else:
    EXE_LOCATION = os.path.dirname(os.path.realpath(__file__))

pygame.init()  # Initialises all the pygame contents
sound_effects = SoundEffects(EXE_LOCATION)  # Sound effects library
1 Answers

The pyinstaller documentation states:

The list of data files is a list of tuples. Each tuple has two values, both of which must be strings:

  • The first string specifies the file or files as they are in this system now.

  • The second specifies the name of the folder to contain the files at run-time.

That means that if you do this:

('C:\\Users\\joshu\\CursorHunters\\Assets\\Sounds\\*.wav', 'Sounds')

then at runtime those .wav files will be located in a relative directory Sounds. That means you can access them using

os.path.join(EXE_LOCATION, "Sounds", "explosion1.wav")

and in the main.py you should provide the path as simply the path to the directory of the main file (first import os or use any other method to get the directory name)

sound_effects = SoundEffects(os.path.dirname(__file__))

Also a bit of improvement would be to use loops to load the files:

self.explosion_sounds = []
for i in range(1, 5 + 1):
    sound = pygame.mixer.Sound(os.path.join(EXE_LOCATION, "Assets", "Sounds", f"explosion{i}.wav"))
    self.explosion_sounds.append(sound)
self.explosion_sounds = tuple(self.explosion_sounds)
Related