Introduction: Animal Crossing Music Player

About: Hello, my name is Michael. I am an electrical and computer engineer turned educator. I have a lot of experience in embedded software and hardware design. I am taking my technical experiences and leveraging the…

An Animal Crossing hourly music player that you can run in the background on your computer. Takes into account the time of day and weather for music selection!

--

For those unfamiliar with the Animal Crossing games, music plays an integral role in creating the atmosphere of the game. Animal Crossing games use the console's system clock to move at and simulate the real passage of time (meaning that when it is 9pm on September 1st in real life, it is 9pm on September 1st in the game). A key way that the developers indicate the passage of time in the game is with the hourly music. In each game, there is a specific song (with slight variations based on weather) for each of the twenty-four hours in a day that repeats for the duration of the hour.

In this project, I created a Python script to play the hourly music in the background on your computer just like in the game. The script uses multiprocessing to handle the various portions of the code. One of the processes handles retrieving the weather and time of day information. The other process handles playing the audio based on the time of day and weather information passed to it. This script is meant to be lightweight so it can run in the background without too much demand.

All of the code for this project can be found here: https://github.com/mjdargen/acmp

Step 1: Setting Up Environment & Dependencies

In order to run the code required for this project, you will need to install Python 3, git, and ffmpeg. The installation steps can vary slightly based on your operating system, so I will link you to the official resources which will provide the most up-to-date guides for your operating system.


Git Project Files

Now you will need to retrieve the source files. If you would like to run git from the command line, you can install git by following the instructions here: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git. However, you can just as easily download the files in your browser.

To download the files required, you can navigate to the repository in your browser and download a zip file or you can use your git client. The link and the git command are described below.

https://github.com/mjdargen/acmp

git clone https://github.com/mjdargen/acmp.git


Installing Dependencies

You need to install the following programs on your computer in order to run this program:


Installing ffmpeg

To install ffmpeg, go to the following web address for the full instructions: https://ffmpeg.org/. For Linux, it's as easy as running the following command:

sudo apt install ffmpeg


For Windows, it is a bit more difficult. Go to the web address below to find the latest builds. Find the latest ffmpeg-release-essentials.zip and download it. This zip file contains the executables for ffmpeg, ffprobe, and avprobe which you will need for pydub. Extract the zip file into the default folder name "ffmpeg-release-essentials". Copy this folder to your C:/ drive.

https://www.gyan.dev/ffmpeg/builds/

Next, you will need to add the executable files to your path. To do this, go to the start menu and search for "Edit the system environment variables" in the control panel. Click the "Environment Variables" button at the bottom of the window. Under "User variables", double-click "Path" to bring up the new window with the list of everything under the Path environment variable. Create a new entry for the location of your ffmpeg executables. It should look something like "C:\ffmpeg-4.4.1-essentials_build\bin" depending upon your version. To test, open the command prompt and type "ffmpeg". As long as it doesn't say "ffmpeg is not recognized as an internal or external command", it should now work properly!

Step 2: Getting Location Information

To retrieve the current location of the user, we will use the Python requests module and make a request to https://ipinfo.io/. ipinfo.io provides geographical information based on your IP address. By going to https://ipinfo.io/json, we can retrieve a raw json file with the following information: IP, hostname, city, region, country, latitude/longitude, ISP, zip code, and time zone.

# gets location (lat/lon) based on IP address
def get_location():
    url = 'http://ipinfo.io/json'
    r = requests.get(url)
    data = r.json()
    return data['loc'].split(',')

We use the code above to retrieve the latitude and longitude information from our IP address. We will use this location information to determine the weather conditions in our area to determine whether we will play the sunny, rainy, or snowy version of the hourly Animal Crossing songs.

Step 3: Getting Weather Information

OpenWeather API

In order to retrieve weather data from the OpenWeather API, you need to create a free account and generate an API key. Go to the following URL and follow the steps to create an account: https://openweathermap.org/api. Once you have created an account, generate an API key and save the key. Keep in mind, it may take a few hours for your API key to be activated.

To use your API key with the code, create a ".env" file in the same directory as the "main.py" file. Place the following line inside of the file (insert your API key where stated).

WEATHER_KEY=<your_api_key_here> 


Weather Data Retrieval

OpenWeather provides a number of different endpoints and lots of different data. For this project, we will use the current weather endpoint and only look in the main field of the weather section. OpenWeather provides the data as a json file. The following portion of code retrieves the json file, parses out the necessary information, then determines whether the sunny, rainy, or snowy song should be played.

# gets weather based on lat/lon
# go to https://openweathermap.org/api to register for free API key
# add to .env file like this: WEATHER_KEY=<your_api_key_here>
def get_weather(lat, lon):
    key = os.getenv('WEATHER_KEY')
    url = f'http://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={key}'
    r = requests.get(url)
    data = r.json()

    if 'rain' in data['weather'][0]['main'].lower():
        return 'raining'
    elif 'snow' in data['weather'][0]['main'].lower():
        return 'snowing'
    else:
        return 'sunny'

Step 4: Timing Process

The timing process is responsible for calling the get_location() function and the get_weather() function at the top of every hour to get up-to-date information. This information is used to determine the appropriate song and which version should be played. The song info is passed to the audio process using a Pipe (multiprocessing communication pathway).

All of these steps are repeated in a "while True" loop meaning that it will run forever until interrupted by the user with a keyboard interrupt or by exiting the active Python window. After retrieving the information and sending it to the audio process at the top of the hour, this process computes the number of seconds until the start of the next hour and sleeps until then. See the code below.

# process for handling timeing to switch over
def timing(conn, ):
    prev = None
    while True:
        # get location
        lat, lon = get_location()
        # get weather
        weather = get_weather(lat, lon)
        # get current time
        now = datetime.datetime.now().strftime("%I%p").lower()
        if now[0] == '0':
            now = now[1:]
        # send message with time and date
        if prev != now:
            conn.send(f'{now}_{weather}')
            prev = now

        # compute how long to sleep for
        now = datetime.datetime.now()
        delta = datetime.timedelta(hours=1)
        next_hour = (now + delta).replace(microsecond=0, second=0, minute=0)
        wait_seconds = (next_hour - now).seconds
        time.sleep(wait_seconds)

Step 5: Audio Process

The audio process is responsible for playing the songs. It checks for messages from the timing process. Once it receives a new message, it will switch over to the new track for the next hour. The audio process is also responsible for repeating the tracks once they have finished. It continues to check to see if a track has ended and queues it up again to play until it receives another message from the timing process.

# process for handling audio
def audio(conn, game):
    DIR_PATH = os.path.dirname(os.path.realpath(__file__))

    # start with silence to initialize objects before loop
    file = f'{DIR_PATH}/silence.mp3'
    clip = AudioSegment.from_mp3(file)
    playback = _play_with_simpleaudio(clip)

    while True:
        # check for new message
        if conn.poll():
            name = conn.recv()
            print(f'Switching to {name}.')
            file = f'{DIR_PATH}/{game}/{name}.mp3'
            # stop old song and play new one
            playback.stop()
            clip = AudioSegment.from_mp3(file)
            playback = _play_with_simpleaudio(clip)
        # song finished, repeat
        if not playback.is_playing():
            # stop old song and play new one
            playback.stop()
            clip = AudioSegment.from_mp3(file)
            playback = _play_with_simpleaudio(clip)
        time.sleep(2)

Step 6: Run!

Now, it's time to run the program! The run command takes a single optional command-line argument that allows you to select which game you would like to hear the music from. You can choose music from the original Animal Crossing, Wild World, New Leaf, or New Horizons. The command-line argument options are shown in the image above. To run the program, use the following command:

python3 main.py --game new-horizons

Step 7: More Projects

Hour of Code Speed Challenge

Participated in the
Hour of Code Speed Challenge