# CircuitPython Chiptune Player

## Overview

![](https://cdn-learn.adafruit.com/assets/assets/000/144/752/medium800/feather_hero-speaker.jpg?1782840465 )

You can listen to your favorite chiptune music with CircuitPython! In this project, you'll build a smol arcade cabinet that houses a Feather RP2350, I2S DAC, and TFT FeatherWing. The Feather runs CircuitPython code that uses the&nbsp;[synthio module](https://docs.circuitpython.org/en/latest/shared-bindings/synthio/) to emulate the [AY-3-8910](https://en.wikipedia.org/wiki/General_Instrument_AY-3-8910), a 3-voice programmable sound generator that was used in a lot of arcade video games.

![](https://cdn-learn.adafruit.com/assets/assets/000/144/737/medium640/feather_hero-build.jpg?1782831963)

You'll load your favorite video game music ([.VGM/.VGZ](https://en.wikipedia.org/wiki/VGM_(file_format))) audio files onto a microSD card and enjoy all the vintage bleeps, bloops, and noise filters. These files can be found on video game archival sites or video game asset sites like [itch.io](https://itch.io/game-assets/tag-vgm). A lot of music tracker software also offers VGM file export for your original music. There is an [example track](https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/main/CircuitPython_Chiptune_Player/computer_music.vgz) included in the CircuitPython code folder for testing.

https://youtu.be/y7Jsi24hr3c

## Parts
Featured
### Adafruit Feather RP2350 with HSTX Port and 8MB PSRAM

[Adafruit Feather RP2350 with HSTX Port and 8MB PSRAM](https://www.adafruit.com/product/6130)
RP2350&nbsp;flies high with the&nbsp;Feather&nbsp;format - now you can use any FeatherWings with this battery-powered dev board. It comes with 8MB of flash, 22pin HSTX output port, Stemma QT, debug SWD, and optional PSRAM spot. It's our first RP2350 board and we crammed a ton of goodies...

In Stock
[Buy Now](https://www.adafruit.com/product/6130)
[Related Guides to the Product](https://learn.adafruit.com/products/6130/guides)
![Angled shot of black, rectangular microcontroller.](https://cdn-shop.adafruit.com/640x480/6130-06.jpg)

Featured
### Adafruit TFT FeatherWing - 3.5" 480x320 Touchscreen for Feathers

[Adafruit TFT FeatherWing - 3.5" 480x320 Touchscreen for Feathers](https://www.adafruit.com/product/3651)
Spice up your Feather project with a beautiful 3.5" touchscreen display shield with built in microSD card socket. This TFT display is 3.5" diagonal with a bright 6 white-LED backlight. You get a massive 480x320 pixels with individual 16-bit color pixel control. It has way more...

In Stock
[Buy Now](https://www.adafruit.com/product/3651)
[Related Guides to the Product](https://learn.adafruit.com/products/3651/guides)
![Overhead shot of a Black woman's hands with a blue and pinkish-red manicure drawing a heart on a touchscreen breakout.](https://cdn-shop.adafruit.com/640x480/3651-05.jpg)

Featured
### Adafruit PCM5102 I2S DAC with Line Level Output - 112dB SNR

[Adafruit PCM5102 I2S DAC with Line Level Output - 112dB SNR](https://www.adafruit.com/product/6250)
We&nbsp;stock a lot of chips and development boards&nbsp;that are able to do high quality digital I2S out, which makes for great quality audio playback. That's great when you have enough processing power to decode WAVs or MP3s in real time. However, we don't have a good selection of...

In Stock
[Buy Now](https://www.adafruit.com/product/6250)
[Related Guides to the Product](https://learn.adafruit.com/products/6250/guides)
![Angled shot of black, rectangular breakout board with audio jack.](https://cdn-shop.adafruit.com/640x480/6250-00.jpg)

Featured
### 512MB micro SD Memory Card

[512MB micro SD Memory Card](https://www.adafruit.com/product/5252)
Add storage in a jiffy using this **512MB** microSD card. Preformatted to FAT32, so it works out of the packaging with our projects. Works great with any device in the Adafruit shop that uses microSD cards. Ideal for use with Feathers, data loggers, or small Linux SBCs (not good...

In Stock
[Buy Now](https://www.adafruit.com/product/5252)
[Related Guides to the Product](https://learn.adafruit.com/products/5252/guides)
![Angel shot of Small microSD card 512mb](https://cdn-shop.adafruit.com/640x480/5252-00.jpg)

Featured
### Premium Silicone Covered Male-Male Jumper Wires - 200mm x 40

[Premium Silicone Covered Male-Male Jumper Wires - 200mm x 40](https://www.adafruit.com/product/4482)
These premium male-male jumper wires are handy for making wire harnesses or jumpering between headers on PCBs. They're&nbsp;200mm (~7.8") long and come loose as a pack of 40&nbsp;(10&nbsp;pieces of red, blue, yellow, and Adafruit black). They have 0.1" male header contacts on...

In Stock
[Buy Now](https://www.adafruit.com/product/4482)
[Related Guides to the Product](https://learn.adafruit.com/products/4482/guides)
![Bundle of Premium Silicone Covered Male-Male Jumper Wires - 200mm x 40](https://cdn-shop.adafruit.com/640x480/4482-01.jpg)

Featured
### Pink and Purple Woven USB A to USB C Cable - 1 meter long

[Pink and Purple Woven USB A to USB C Cable - 1 meter long](https://www.adafruit.com/product/5153)
This cable is not only super-fashionable, with a woven pink and purple Blinka-like pattern, it's also made for USB C for our modernized breakout boards, Feathers, and more.&nbsp;&nbsp;[If you want something just like it but for Micro B, we...](https://www.adafruit.com/product/4111)

Out of Stock
[Buy Now](https://www.adafruit.com/product/5153)
[Related Guides to the Product](https://learn.adafruit.com/products/5153/guides)
![Angled shot of coiled pink and purple USB cable with USB A and USB C connectors.](https://cdn-shop.adafruit.com/640x480/5153-02.jpg)

### USB C Round Panel Mount Extension Cable

[USB C Round Panel Mount Extension Cable](https://www.adafruit.com/product/4218)
If you need to add a panel-mount connection&nbsp;but don't have the time or ability to cut a custom oval or square hole, this **USB C&nbsp;Round Panel Mount Extension Cable&nbsp;** is the easiest and fastest way to panel-ify your project. The adapter can fit holes...

In Stock
[Buy Now](https://www.adafruit.com/product/4218)
[Related Guides to the Product](https://learn.adafruit.com/products/4218/guides)
![USB C Round Panel Mount Extension Cable - 30 cm](https://cdn-shop.adafruit.com/640x480/4218-03.jpg)

### USB Powered Speakers

[USB Powered Speakers](https://www.adafruit.com/product/1363)
Add some extra boom to your audio project with these powered loudspeakers. We sampled half a dozen different models to find ones with a good frequency response, so you'll get quality audio output for music playback. Simply connect the standard 3.5mm stereo plug into your Raspberry Pi model...

In Stock
[Buy Now](https://www.adafruit.com/product/1363)
[Related Guides to the Product](https://learn.adafruit.com/products/1363/guides)
![Two square-ish USB Powered Speakers](https://cdn-shop.adafruit.com/640x480/1363-07.jpg)

### Stereo 3.5mm Plug/Plug Audio Cable - 6 feet

[Stereo 3.5mm Plug/Plug Audio Cable - 6 feet](https://www.adafruit.com/product/876)
This basic cable comes with two 3.5mm (1/8" headphone jack size) stereo connectors. It's fairly straight forward, you'll commonly need these to connect two audio devices together.  
  
Cable is 6 ft long.

In Stock
[Buy Now](https://www.adafruit.com/product/876)
[Related Guides to the Product](https://learn.adafruit.com/products/876/guides)
![Long, black fabric woven stereo plug cable coiled in a circle.](https://cdn-shop.adafruit.com/640x480/876-04.jpg)

## Hardware Fasteners

These screws are required for assembling this project.

- 4x M2.5 x 6mm long machine screws
- 4x M3 x 4mm long machine screws
- 2x M3 x 8mm long machine screws

# CircuitPython Chiptune Player

## Circuit Diagram

![](https://cdn-learn.adafruit.com/assets/assets/000/144/620/medium800/feather_chiptunePlayerCircuitDiagram_bb.png?1782228087)

- **Board 3.3V** &nbsp;to&nbsp;**DAC VIN (red wire)**
- **Board GND** &nbsp;to&nbsp;**DAC GND (black wire)**
- **Board A1** &nbsp;to&nbsp;**DAC BCK (yellow wire)**
- **Board A2** &nbsp;to&nbsp;**DAC WSEL (green wire)**
- **Board A3** &nbsp;to&nbsp;**DAC DIN (blue wire)**

The Feather RP2350 is plugged into the TFT FeatherWing. The I2S DAC wires are plugged into the duplicate Feather pins on the FeatherWing.

# CircuitPython Chiptune Player

## 3D Printing

## CAD Parts

Individual 3MF files for 3D printing are oriented and ready to print on FDM machines using PLA filament. Original design source files may be downloaded using the links below.

![](https://cdn-learn.adafruit.com/assets/assets/000/144/681/medium640/feather_3d-parts.jpg?1782759087)

[Download 3MF.zip](https://cdn-learn.adafruit.com/assets/assets/000/144/685/original/3MF.zip?1782760081)
[Download CAD Source](https://cdn-learn.adafruit.com/assets/assets/000/144/684/original/CAD.zip?1782760067)
## Build Volume

The parts require a 3D printer with a minimum build volume of&nbsp;168 (X) x 168 (Y) x 76mm (Z).

![](https://cdn-learn.adafruit.com/assets/assets/000/144/682/medium640/feather_slicer-part.jpg?1782759919)

## 3D Models of Adafruit Parts

Electronic components like Adafruit's boards and more can be downloaded from the&nbsp;[Adafruit CAD parts GitHub Repo](https://github.com/adafruit/Adafruit_CAD_Parts/).

![](https://cdn-learn.adafruit.com/assets/assets/000/144/683/medium640/feather_6000_Feather_RP2350.jpg?1782760032)

# CircuitPython Chiptune Player

## Install CircuitPython

[CircuitPython](https://github.com/adafruit/circuitpython) is a derivative of [MicroPython](https://micropython.org) designed to simplify experimentation and education on low-cost microcontrollers. It makes it easier than ever to get prototyping by requiring no upfront desktop software downloads. Simply copy and edit files on the **CIRCUITPY** drive to iterate.

## CircuitPython Quickstart

Follow this step-by-step to quickly get CircuitPython running on your board.

[Download the latest version of CircuitPython for this board via circuitpython.org](https://circuitpython.org/board/adafruit_feather_rp2350/)
 **Click the link above to download the latest CircuitPython UF2 file.**

Save it wherever is convenient for you.

![install_circuitpython_on_rp2040_RP2040_UF2_downloaded.jpg](https://cdn-learn.adafruit.com/assets/assets/000/101/655/medium640/install_circuitpython_on_rp2040_RP2040_UF2_downloaded.jpg?1618943202)

![](https://cdn-learn.adafruit.com/assets/assets/000/132/269/medium800/adafruit_products_reset_boot.jpg?1726082893)

To enter the bootloader, hold down the **BOOT/**** BOOTSEL button**(highlighted in red above), and while continuing to hold it (don't let go!), press and release the**reset button**(highlighted in red or blue above).&nbsp;**Continue to hold the BOOT/BOOTSEL button until the RP2350 drive appears!**

If the drive does not appear, release all the buttons, and then repeat the process above.

You can also start with your board unplugged from USB, press and hold the BOOTSEL button (highlighted in red above), continue to hold it while plugging it into USB, and wait for the drive to appear before releasing the button.

A lot of people end up using charge-only USB cables and it is very frustrating! **Make sure you have a USB cable you know is good for data sync.**

You will see a new disk drive appear called **RP2350**.

&nbsp;

Drag the **adafruit-circuitpython-_boardname_-_language_-_version_.uf2** file to **RP2350.**

![install_circuitpython_on_rp2350_Screenshot_2024-09-11_111518.png](https://cdn-learn.adafruit.com/assets/assets/000/132/253/medium640/install_circuitpython_on_rp2350_Screenshot_2024-09-11_111518.png?1726067809)

![install_circuitpython_on_rp2350_Screenshot_2024-09-11_111742.png](https://cdn-learn.adafruit.com/assets/assets/000/132/254/medium640/install_circuitpython_on_rp2350_Screenshot_2024-09-11_111742.png?1726067866)

The **RP2350** drive will disappear and a new disk drive called **CIRCUITPY** will appear.

That's it, you're done! :)

![install_circuitpython_on_rp2350_Screenshot_2024-09-11_111843.png](https://cdn-learn.adafruit.com/assets/assets/000/132/255/medium640/install_circuitpython_on_rp2350_Screenshot_2024-09-11_111843.png?1726067932)

## Safe Mode

You want to edit your **code.py** or modify the files on your **CIRCUITPY** drive, but find that you can't. Perhaps your board has gotten into a state where **CIRCUITPY** is read-only. You may have turned off the **CIRCUITPY** drive altogether. Whatever the reason, safe mode can help.

Safe mode in CircuitPython does not run any user code on startup, and disables auto-reload. This means a few things. First, safe mode _bypasses any code in_ **boot.py** (where you can set **CIRCUITPY** read-only or turn it off completely). Second, _it does not run the code in_ **code.py**. And finally, _it does not automatically soft-reload when data is written to the_ **CIRCUITPY** _drive_.

Therefore, whatever you may have done to put your board in a non-interactive state, safe mode gives you the opportunity to correct it without losing all of the data on the **CIRCUITPY** drive.

### Entering Safe Mode
To enter safe mode when using CircuitPython, plug in your board or hit reset (highlighted in red above). Immediately after the board starts up or resets, it waits 1000ms. On some boards, the onboard status LED (highlighted in green above) will blink yellow during that time. If you press reset during that 1000ms, the board will start up in safe mode. It can be difficult to react to the yellow LED, so you may want to think of it simply as a slow double click of the reset button. (Remember, a fast double click of reset enters the bootloader.)

### In Safe Mode

If you successfully enter safe mode on CircuitPython, the LED will intermittently blink yellow three times.

If you connect to the serial console, you'll find the following message.

```terminal
Auto-reload is off.
Running in safe mode! Not running saved code.

CircuitPython is in safe mode because you pressed the reset button during boot. Press again to exit safe mode.

Press any key to enter the REPL. Use CTRL-D to reload.
```

You can now edit the contents of the **CIRCUITPY** drive. Remember, _your code will not run until you press the reset button, or unplug and plug in your board, to get out of safe mode._

## Flash Resetting UF2

If your board ever gets into a really _weird_ state and CIRCUITPY doesn't show up as a disk drive after installing CircuitPython, try loading this 'nuke' UF2 to RP2350. which will do a 'deep clean' on your Flash Memory. **You will lose all the files on the board** , but at least you'll be able to revive it! After loading this UF2, follow the steps above to re-install CircuitPython.

[Download flash erasing "nuke" UF2 for RP2350](https://cdn-learn.adafruit.com/assets/assets/000/132/526/original/rp2350_flash_nuke.uf2)
# CircuitPython Chiptune Player

## Code the Player

Once you've finished setting up your RP2350 Feather with CircuitPython, you can access the code and necessary libraries by downloading the Project Bundle.

To do this, click on the **Download Project Bundle** button in the window below. It will download to your computer as a zipped folder.

https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/main/CircuitPython_Chiptune_Player/code.py

## Upload the Code and Libraries to the RP2350 Feather

After downloading the Project Bundle, plug your RP2350 Feather into the computer's USB port with a known good USB data+power cable. You should see a new flash drive appear in the computer's File Explorer or Finder (depending on your operating system) called **CIRCUITPY**. Unzip the folder and copy the following items to the RP2350 Feather's **CIRCUITPY** drive.

- **lib** folder
- **code.py**

Your RP2350 Feather **CIRCUITPY** drive should look like this after copying the **lib** folder&nbsp;and **code.py** file:

![CIRCUITPY drive](https://adafruit.github.io/Adafruit_Learning_System_Guides/CircuitPython_Chiptune_Player.png )

There is an example VGM track ([computer\_music.vgz](https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/main/CircuitPython_Chiptune_Player/computer_music.vgz)) included in the bundle. You can copy this file to your microSD card for testing.

## How the CircuitPython Code Works

After SPI is initialized, the SD card is mounted. A search is performed in the **/sd** directory for any video game music (.VGM) audio files. These files are added to the `playlist` list.

```python
displayio.release_displays()
spi = board.SPI()
SD_CS = board.D5
VGM_EXTS = (".vgm", ".vgz")

playlist = []
for attempt in range(3):
    try:
        sdcard = sdcardio.SDCard(spi, SD_CS)
        vfs = storage.VfsFat(sdcard)
        storage.mount(vfs, "/sd")
        playlist = sorted(
            (n for n in os.listdir("/sd") if n.lower().endswith(VGM_EXTS)),
            key=lambda n: n.lower(),
        )
        print(f"Found {len(playlist)} VGM file(s) on SD")
        break
    except OSError as exc:
        print(f"SD init attempt {attempt + 1} failed:", exc)
        time.sleep(0.25)
else:
    print("SD card not available - running with an empty playlist.")
```

### Display and Audio Init

Next is the display initialization with the TSC2007 touchscreen driver. I2S audio is initialized for the I2S DAC.

```python
# init display after sd card
tft_cs = board.D9
tft_dc = board.D10
display_bus = fourwire.FourWire(spi, command=tft_dc, chip_select=tft_cs)
display = HX8357(display_bus, width=320, height=480, rotation=90)  # portrait
SCREEN_W = 320
SCREEN_H = 480

i2c = board.I2C()
tsc = adafruit_tsc2007.TSC2007(i2c, invert_x=True, invert_y=True)

audio = audiobusio.I2SOut(board.A1, board.A2, board.A3)
```

### Graphics

There are two display groups:&nbsp;`play_group` and `menu_group`. `menu_group` shows the list of VGM files loaded from the SD card. Each file name is shown in a touch button to select for playback.&nbsp;

```python
# ====================================================================
#  MENU VIEW  (for selecting tracks)
# ====================================================================
menu_group = displayio.Group()

MENU_X, MENU_W = 10, 300
MENU_ROW_H, MENU_ROW_GAP, MENU_TOP = 38, 4, 8
PER_PAGE = 9

# Persistent bottom nav row: [&lt;]  NOW PLAYING  [&gt;]
NAV_Y, NAV_H = 420, 52
PAGE_W = 56
prevpage_btn, _ = make_button("page_prev", MENU_X, NAV_Y, PAGE_W, NAV_H, "&lt;", 0x000000, 0x00FFFF)
np_btn, _ = make_button("show_play", MENU_X + PAGE_W + GAP, NAV_Y,
                        MENU_W - 2 * (PAGE_W + GAP), NAV_H, "NOW PLAYING", 0x00FFFF)
nextpage_btn, _ = make_button("page_next", MENU_X + MENU_W - PAGE_W, NAV_Y,
                              PAGE_W, NAV_H, "&gt;", 0x000000, 0x00FFFF)
for btn in (prevpage_btn, np_btn, nextpage_btn):
    menu_group.append(btn)

page_label = text_label.Label(terminalio.FONT, text="", color=0x888893,
                              anchor_point=(0.5, 0.5), anchored_position=(NP_X, 404))
menu_group.append(page_label)

if not playlist:
    menu_group.append(text_label.Label(
        terminalio.FONT, text="No VGM files found on /sd", color=0xFFFFFF,
        anchor_point=(0.5, 0.5), anchored_position=(NP_X, 180)))

file_buttons = []
menu_page = 0
menu_buttons = [prevpage_btn, np_btn, nextpage_btn]
menu_actions = ["page_prev", "show_play", "page_next"]
```

The `play_group` has all of the playback controls: stop, play, pause, forward, backward and repeat. The "now playing" info is shown in the middle of the display and a `HorizontalProgressBar` shows the playback progress.

```python
# ====================================================================
#  PLAY VIEW
# ====================================================================
play_group = displayio.Group()

# Repeat toggle, top-left. Its "selected" state is the lit state:
# off -&gt; black body + cyan text; on -&gt; cyan body + black text.
repeat_button = Button(
    x=6, y=8, width=76, height=34,
    style=Button.ROUNDRECT,
    fill_color=0x000000, outline_color=OUTLINE,
    label="REPEAT", label_font=terminalio.FONT, label_color=0x00FFFF,
    selected_fill=0x00FFFF, selected_label=0x000000,
)
play_group.append(repeat_button)

# Track info, centered in the open top area (filled in by load_track).
NP_X = SCREEN_W // 2
title_label = text_label.Label(terminalio.FONT, text="(no track)", color=0x00FFFF,
                               scale=2, anchor_point=(0.5, 0.5), anchored_position=(NP_X, 150))
author_label = text_label.Label(terminalio.FONT, text="", color=0xFFFF00,
                                anchor_point=(0.5, 0.5), anchored_position=(NP_X, 182))
game_label = text_label.Label(terminalio.FONT, text="", color=0xFF00FF,
                              anchor_point=(0.5, 0.5), anchored_position=(NP_X, 204))
play_group.append(title_label)
play_group.append(author_label)
play_group.append(game_label)

# Progress bar + time readout.
BAR_W, BAR_H = 288, 24
BAR_X = (SCREEN_W - BAR_W) // 2
BAR_Y = 360
progress_bar = HorizontalProgressBar(
    (BAR_X, BAR_Y), (BAR_W, BAR_H),
    min_value=0.0, max_value=1.0, value=0.0,
    bar_color=0x00FFFF, outline_color=0xFF00FF, fill_color=0x000000,
    direction=HorizontalFillDirection.LEFT_TO_RIGHT,
)
play_group.append(progress_bar)
time_label = text_label.Label(terminalio.FONT, text="0:00 / 0:00", color=0x00FFFF,
                              anchor_point=(1.0, 1.0), anchored_position=(BAR_X + BAR_W, BAR_Y - 4))
play_group.append(time_label)

# Transport row: STOP  &lt;&lt;  PLAY/PAUSE  &gt;&gt;  MENU
ROW_Y = 400
BTN_H = 55
PLAY_W = 84
CTRL_W = 50
GAP = 6
play_x = (SCREEN_W - PLAY_W) // 2
rew_x = play_x - GAP - CTRL_W
stop_x = rew_x - GAP - CTRL_W
ffwd_x = play_x + PLAY_W + GAP
menu_x = ffwd_x + CTRL_W + GAP

play_buttons = [repeat_button]
play_actions = ["repeat"]
for btn, act in (
    make_button("stop", stop_x, ROW_Y, CTRL_W, BTN_H, "STOP", 0xFF0000),
    make_button("prev", rew_x, ROW_Y, CTRL_W, BTN_H, "&lt;&lt;", 0xFF00FF),
    make_button("play", play_x, ROW_Y, PLAY_W, BTN_H, "PLAY", 0x00FFFF),
    make_button("next", ffwd_x, ROW_Y, CTRL_W, BTN_H, "&gt;&gt;", 0xFFFF00),
    make_button("show_menu", menu_x, ROW_Y, CTRL_W, BTN_H, "MENU", 0x000000, 0xFFFFFF),
):
    play_group.append(btn)
    play_buttons.append(btn)
    play_actions.append(act)
play_button = play_buttons[3]   # the PLAY/PAUSE button (for label toggling)
```

### Chiptune Playback

The VGM decoding and playback is handled by the AY8912 emulator helper library.

```python
# ====================================================================
#  AY8912 emulator setup
# ====================================================================
ay = AY8912(sample_rate=22050, clock_rate=1773400)
ay.begin(audio)

vgm = None
song_duration = 0.0
current_index = 0
is_playing = False
is_paused = False
repeat_mode = False
last_progress = -1.0
last_sec = -1

current_view = "play"
wait_release = False
```

### Helpers

There are a few helper functions:

- `build_menu_page()` - builds out the file selection buttons in the `menu_group`. Handles overflow to multiple pages.
- `load_track()` - queues up the next VGM file for playback
- `play_track()` - plays the VGM file and handles play/pause controls
- `change_track()` - changes track based on the forward or backward button inputs
- `show_menu()` - switches to the `menu_group`
- `show_play()` - switches to the `play_group`
- `dispatch()` - handle buttons inputs and change states or values depending on which button is pressed
- `touched_index()` - tracks if a button contains a touch point on the screen

### The Loop

In the loop, if a VGM file is selected, it is played through the AY8912 `synthio` emulator.

```python
while True:
    # Feed audio as tightly as possible, in any view.
    if is_playing and vgm is not None:
        vgm.update()
        if not vgm.playing:               # track reached its end
            if repeat_mode:
                vgm.play(ay)              # replay the same track (no SD reload)
                last_progress = -1.0
                last_sec = -1
            else:
                change_track(1)           # auto-advance, wraps at the end
        elif vgm.loop_count &gt;= 1 and not repeat_mode:
            change_track(1)               # looping file finished a pass; move on
```

`time.monotonic()` is used as a non-blocking time tracker. During the `POLL_INTERVAL`, the buttons are checked for any inputs. If you are on the playback screen, the progress bar position is also updated.

```python
now = time.monotonic()
    if now - last_poll &gt;= POLL_INTERVAL:
        last_poll = now

        btns = play_buttons if current_view == "play" else menu_buttons
        acts = play_actions if current_view == "play" else menu_actions
        index = touched_index(btns)
        for i, btn in enumerate(btns):
            if btn is repeat_button:      # its lit state reflects repeat_mode, not touch
                continue
            btn.selected = i == index

        if wait_release:
            if index is None:             # finger lifted; accept presses again
                wait_release = False
            last_index = index
        else:
            if index is not None and index != last_index:
                dispatch(acts[index])
            last_index = index

        if is_playing and vgm is not None:
            progress = vgm.progress
            if abs(progress - last_progress) &gt;= 0.01:
                progress_bar.value = progress
                last_progress = progress
            sec = int(vgm.elapsed)
            if sec != last_sec:
                time_label.text = f"{fmt_time(sec)} / {fmt_time(song_duration)}"
                last_sec = sec
```

# CircuitPython Chiptune Player

## Assembly

## Solder Headers

Install the 12-pin and 16-pin headers onto the bottom of the Feather.

Use a pair of helping hands to assist while soldering.

Solder all of the pins on the Feather.

See the guide below on some tips how to do this.

![](https://cdn-learn.adafruit.com/assets/assets/000/144/686/medium640/feather-headers.jpg?1782760131)

![](https://cdn-learn.adafruit.com/assets/assets/000/144/687/medium640/feather-headers-install.jpg?1782760175)

![](https://cdn-learn.adafruit.com/assets/assets/000/144/689/medium640/feather-headers-solder.jpg?1782760238)

## Install Feather to TFT FeatherWing

Orient the Feather with the socket headers on the TFT FeatherWing.

Press the Feather into the sockets to install it onto the TFT FeatherWing.

Insert the microSD card (with preloaded VGM files) into the microSD card slot on the TFT FeatherWing.

![](https://cdn-learn.adafruit.com/assets/assets/000/144/690/medium640/feather-TFT.jpg?1782760260)

![](https://cdn-learn.adafruit.com/assets/assets/000/144/691/medium640/feather-TFT-installed.jpg?1782760283)

## Jumper Wires

Get five colored jumper wires from the pack. Red, black, two yellow, and a blue are suggested.

![](https://cdn-learn.adafruit.com/assets/assets/000/144/693/medium640/feather_jumper-wires.jpg?1782760359)

## Prep Jumper Wires

Use wire cutters to remove one of the ends from all five jumper wires.

Use wire strippers to remove a bit of insulation from the cut ends.

Tin the exposed wire with a bit of solder.

Repeat this process for all five wires.

![](https://cdn-learn.adafruit.com/assets/assets/000/144/694/medium640/feather_jumpers-cut.jpg?1782760374)

![](https://cdn-learn.adafruit.com/assets/assets/000/144/695/medium640/feather_jumpers-tin.jpg?1782760466)

![](https://cdn-learn.adafruit.com/assets/assets/000/144/696/medium640/feather_jumpers-prepped.jpg?1782760490)

## Solder Wires to PCM

Solder the jumper wires to the following pins on the PCM.

- Red wire to&nbsp; **VCC&nbsp;** on PCM
- Black wireto **GND&nbsp;** on PCM
- Yellow wireto **WSEL** on PCM
- Blue wire to&nbsp; **DIN** on PCM
- 2nd Yellow wire to **BCK&nbsp;** on PCM

![](https://cdn-learn.adafruit.com/assets/assets/000/144/700/medium640/feather_PCM-wired.jpg?1782819274)

## Connect PCM to TFT FeatherWing

Plug in the connectors from the jumper wires to the following pins on the TFT FeatherWing socket headers.

- **VCC** to **3.3V** on TFT FeatherWing
- **GND** to **GND** on TFT FeatherWing
- **WSEL** to **A2** on TFT FeatherWing
- **DIN** to **A3** on TFT FeatherWing
- **BCK** to **A1** on TFT FeatherWing

![](https://cdn-learn.adafruit.com/assets/assets/000/144/701/medium640/feather_PCM-TFT-jumpers.jpg?1782819295)

## Test Circuit

Connect 5V USB power to the Feather. Connect a pair of headphones (or powered speakers) to the PCM5102.

Ensure the enable switch on the back of the TFT FeatherWing is on the **ON** position.

Use the on-screen controls to play, pause, forward, etc. to test the circuit.

![](https://cdn-learn.adafruit.com/assets/assets/000/144/702/medium640/feather_circuit-test.jpg?1782819318)

## Secure PCM to Mount

Use 4x M2.5 x 6mm long machine screws to secure the PCM5102 to the PCB mount.

Orient the PCM5102 with the PCB Mount then place it over the four standoffs.

Insert and fasten the M2.5 screws to secure the PCB.

![](https://cdn-learn.adafruit.com/assets/assets/000/144/703/medium640/feather_PCM-mount-screws.jpg?1782819341)

![](https://cdn-learn.adafruit.com/assets/assets/000/144/704/medium640/feather_PCM-mount-secure.jpg?1782819350)

![](https://cdn-learn.adafruit.com/assets/assets/000/144/705/medium640/feather_PCM-mount-secured.jpg?1782819366)

## Secure TFT FeatherWing

Use 4x. M3 x 4mm long machine screws to secure the TFT FeatherWing to the TFT Mount.

Orient and place the TFT FeatherWing over the four standoffs.

Insert and fasten the M3 screws to secure the PCB.

![](https://cdn-learn.adafruit.com/assets/assets/000/144/706/medium640/feather_TFT-mount-screws.jpg?1782819405)

![](https://cdn-learn.adafruit.com/assets/assets/000/144/707/medium640/feather_TFT-mount-place.jpg?1782819427)

![](https://cdn-learn.adafruit.com/assets/assets/000/144/708/medium640/feather_TFT-mount-secure.jpg?1782819445)

## Install Slide Switch

Carefully insert and fit the slide switch actuator into the cutout on the TFT Mount.

Press the slide switch the prongs are fitted over the actuator on the enable switch.

Test the switch by sliding it back and forth.

![](https://cdn-learn.adafruit.com/assets/assets/000/144/709/medium640/feather_TFT-mount-switch.jpg?1782819456)

![](https://cdn-learn.adafruit.com/assets/assets/000/144/710/medium640/feather_TFT-mount-switch-installed.jpg?1782819466)

## Install Left Side Panel

Place the TFT Mount next to the Left Side Panel and orient them.

Line up the left edge of the TFT Mount with the inset channel on the Left Side Panel.

Firmly press the two parts together to secure them in place.

![](https://cdn-learn.adafruit.com/assets/assets/000/144/711/medium640/feather_side-panel.jpg?1782819476)

![](https://cdn-learn.adafruit.com/assets/assets/000/144/712/medium640/feather_side-tft-fit.jpg?1782819507)

![](https://cdn-learn.adafruit.com/assets/assets/000/144/713/medium640/feather_side-tft-install.jpg?1782819525)

## Install Frame

Place the Frame with the Left Side Panel and orient them correctly.

Line up the left edge of the Frame with the inset channel on the Left Side Panel.

Firmly press the two parts together to secure them in place.

![](https://cdn-learn.adafruit.com/assets/assets/000/144/714/medium640/feather_frame-side-prefit.jpg?1782819558)

![](https://cdn-learn.adafruit.com/assets/assets/000/144/715/medium640/feather_frame-side-install.jpg?1782819572)

## Secure PCM Mount to Side Panel

Insert and fit the PCM Mount into the clips on the Left Side Panel.

The TRS audio jack should line up with the circular cutout on the Frame.

The mounting tab should also line up with the mounting hole on the Left Side Panel.

Insert and fasten an M3 x 8mm long machine screw to secure the PCM Mount to the Left Side Panel.

![](https://cdn-learn.adafruit.com/assets/assets/000/144/716/medium640/feather_PCM-mount-frame-fit.jpg?1782819596)

![](https://cdn-learn.adafruit.com/assets/assets/000/144/717/medium640/feather_PCM-mount-frame-placed.jpg?1782819729)

![](https://cdn-learn.adafruit.com/assets/assets/000/144/718/medium640/feather_PCM-mount-frame-secure.jpg?1782819744)

## Secure USB-C Cable

Remove the plastic hex nut from USB-C panel mount extension cable.

Insert the USB-C plug end of the cable through the large hole on the Frame.

Press the USB-C socket end of the cable so it sits flush with the Frame.

Insert the plastic hex nut back onto the USB-C cable and finger tighten to secure to the Frame.

![](https://cdn-learn.adafruit.com/assets/assets/000/144/719/medium640/feather_usbc-cable.jpg?1782819814)

![](https://cdn-learn.adafruit.com/assets/assets/000/144/720/medium640/feather_usbc-cable-installing.jpg?1782819831)

![](https://cdn-learn.adafruit.com/assets/assets/000/144/721/medium640/feather_usbc-cable-installed.jpg?1782819851)

![](https://cdn-learn.adafruit.com/assets/assets/000/144/722/medium640/feather_usbc-nut-secured.jpg?1782819865)

## Connect USB-C to Feather

Carefully fit the excess USB-C cable into the enclosure and connect the USB-C plug end to the USB-C port on the Feather.

![](https://cdn-learn.adafruit.com/assets/assets/000/144/723/medium640/usbc-feather-plug.jpg?1782819885)

## Install Right Side Panel

Orient the Right Side Panel with the enclosure assembly so the right edges of the TFT mount and Frame are lined up correctly with the inset channel.

Firmly press the Right Side Panel to secure it to the two parts, ensuring all of the edges have been fully inserted into the channel.

![](https://cdn-learn.adafruit.com/assets/assets/000/144/724/medium640/feather_side-preclose.jpg?1782819919)

![](https://cdn-learn.adafruit.com/assets/assets/000/144/725/medium640/feather_side-align.jpg?1782819931)

![](https://cdn-learn.adafruit.com/assets/assets/000/144/726/medium640/feather_side-back-closing.jpg?1782819948)

## Secure PCM Mount to Right Side Panel

Use another M3 x 8mm long machine screw to secure the PCM mount to the Right Side Panel.

Insert and fasten the M3 screw to secure the PCM mount to the Right Side Panel.

![](https://cdn-learn.adafruit.com/assets/assets/000/144/727/medium640/feather_last-screw.jpg?1782820025)

## Connect Power, Audio and Play

Plug in USB-C 5V power to the USB-C extension cable on the back of the Frame.

Turn the slide switch to the ON position on TFT FeatherWings enable switch.

Connect a pair of headphones or powered speakers to the TRS audio jack on the PCM5102.

![](https://cdn-learn.adafruit.com/assets/assets/000/144/728/medium640/feather_usb-power.jpg?1782820065)

![](https://cdn-learn.adafruit.com/assets/assets/000/144/729/medium640/feather_switch-power.jpg?1782820087)

![](https://cdn-learn.adafruit.com/assets/assets/000/144/730/medium640/feather_audio-plug.jpg?1782820109)

## Final Build

Congratulations on your build! Check out the usage page for information on using the on-screen interface to play chiptunes.

![](https://cdn-learn.adafruit.com/assets/assets/000/144/731/medium800/feather_final.jpg?1782820120 )

# CircuitPython Chiptune Player

## Use

https://youtu.be/y7Jsi24hr3c

First, load your favorite VGM files onto a microSD card, then insert the card into the player. You'll have the best playback experience with files that are targeted for the AY-3-8910. There is an [example track](https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/main/CircuitPython_Chiptune_Player/computer_music.vgz) included in the CircuitPython code folder for testing.

![](https://cdn-learn.adafruit.com/assets/assets/000/144/734/medium640thumb/feather_microSD-GIF.jpg?1782830138)

Power up the player via a USB-C cable and turn the enable switch to the ON position. You'll see the VGM files listed for playback on the TFT.

![](https://cdn-learn.adafruit.com/assets/assets/000/144/735/medium640thumb/feather_power-GIF.jpg?1782831288)

Select the file that you want to play. This will bring you to the playback screen. You'll see the track info along with a playback progress bar and timestamp.

![](https://cdn-learn.adafruit.com/assets/assets/000/144/732/medium640thumb/feather_play-GIF.jpg?1782828494)

While you're playing a file, you can control playback with play, pause and stop. You can advance or rewind tracks from the list. You can also loop a track.

![](https://cdn-learn.adafruit.com/assets/assets/000/144/733/medium640thumb/feather_pause-stop-GIF.jpg?1782828517)


## Guide Products

### Adafruit Feather RP2350 with HSTX Port and 8MB PSRAM

[Adafruit Feather RP2350 with HSTX Port and 8MB PSRAM](https://www.adafruit.com/product/6130)
RP2350&nbsp;flies high with the&nbsp;Feather&nbsp;format - now you can use any FeatherWings with this battery-powered dev board. It comes with 8MB of flash, 22pin HSTX output port, Stemma QT, debug SWD, and optional PSRAM spot. It's our first RP2350 board and we crammed a ton of goodies...

In Stock
[Buy Now](https://www.adafruit.com/product/6130)
[Related Guides to the Product](https://learn.adafruit.com/products/6130/guides)
### Adafruit TFT FeatherWing - 3.5" 480x320 Touchscreen for Feathers

[Adafruit TFT FeatherWing - 3.5" 480x320 Touchscreen for Feathers](https://www.adafruit.com/product/3651)
Spice up your Feather project with a beautiful 3.5" touchscreen display shield with built in microSD card socket. This TFT display is 3.5" diagonal with a bright 6 white-LED backlight. You get a massive 480x320 pixels with individual 16-bit color pixel control. It has way more...

In Stock
[Buy Now](https://www.adafruit.com/product/3651)
[Related Guides to the Product](https://learn.adafruit.com/products/3651/guides)
### Adafruit PCM5102 I2S DAC with Line Level Output - 112dB SNR

[Adafruit PCM5102 I2S DAC with Line Level Output - 112dB SNR](https://www.adafruit.com/product/6250)
We&nbsp;stock a lot of chips and development boards&nbsp;that are able to do high quality digital I2S out, which makes for great quality audio playback. That's great when you have enough processing power to decode WAVs or MP3s in real time. However, we don't have a good selection of...

In Stock
[Buy Now](https://www.adafruit.com/product/6250)
[Related Guides to the Product](https://learn.adafruit.com/products/6250/guides)
### 512MB micro SD Memory Card

[512MB micro SD Memory Card](https://www.adafruit.com/product/5252)
Add storage in a jiffy using this **512MB** microSD card. Preformatted to FAT32, so it works out of the packaging with our projects. Works great with any device in the Adafruit shop that uses microSD cards. Ideal for use with Feathers, data loggers, or small Linux SBCs (not good...

In Stock
[Buy Now](https://www.adafruit.com/product/5252)
[Related Guides to the Product](https://learn.adafruit.com/products/5252/guides)
### Premium Silicone Covered Male-Male Jumper Wires - 200mm x 40

[Premium Silicone Covered Male-Male Jumper Wires - 200mm x 40](https://www.adafruit.com/product/4482)
These premium male-male jumper wires are handy for making wire harnesses or jumpering between headers on PCBs. They're&nbsp;200mm (~7.8") long and come loose as a pack of 40&nbsp;(10&nbsp;pieces of red, blue, yellow, and Adafruit black). They have 0.1" male header contacts on...

In Stock
[Buy Now](https://www.adafruit.com/product/4482)
[Related Guides to the Product](https://learn.adafruit.com/products/4482/guides)
### Pink and Purple Woven USB A to USB C Cable - 1 meter long

[Pink and Purple Woven USB A to USB C Cable - 1 meter long](https://www.adafruit.com/product/5153)
This cable is not only super-fashionable, with a woven pink and purple Blinka-like pattern, it's also made for USB C for our modernized breakout boards, Feathers, and more.&nbsp;&nbsp;[If you want something just like it but for Micro B, we...](https://www.adafruit.com/product/4111)

Out of Stock
[Buy Now](https://www.adafruit.com/product/5153)
[Related Guides to the Product](https://learn.adafruit.com/products/5153/guides)
### USB C Round Panel Mount Extension Cable

[USB C Round Panel Mount Extension Cable](https://www.adafruit.com/product/4218)
If you need to add a panel-mount connection&nbsp;but don't have the time or ability to cut a custom oval or square hole, this **USB C&nbsp;Round Panel Mount Extension Cable&nbsp;** is the easiest and fastest way to panel-ify your project. The adapter can fit holes...

In Stock
[Buy Now](https://www.adafruit.com/product/4218)
[Related Guides to the Product](https://learn.adafruit.com/products/4218/guides)
### USB Powered Speakers

[USB Powered Speakers](https://www.adafruit.com/product/1363)
Add some extra boom to your audio project with these powered loudspeakers. We sampled half a dozen different models to find ones with a good frequency response, so you'll get quality audio output for music playback. Simply connect the standard 3.5mm stereo plug into your Raspberry Pi model...

In Stock
[Buy Now](https://www.adafruit.com/product/1363)
[Related Guides to the Product](https://learn.adafruit.com/products/1363/guides)

## Related Guides

- [Adafruit 3.5" 480x320 TFT FeatherWing](https://learn.adafruit.com/adafruit-3-5-tft-featherwing.md)
- [Adafruit Feather RP2350 with HSTX](https://learn.adafruit.com/adafruit-feather-rp2350.md)
- [Adafruit PCM510x I2S DAC](https://learn.adafruit.com/adafruit-pcm510x-i2s-dac.md)
- [NeoTrellis M4 Noisy Grains of Sand](https://learn.adafruit.com/neotrellism4sand.md)
- [Star Trek Soundboard with NeoTrellis](https://learn.adafruit.com/star-trek-sound-board-with-neotrellism4.md)
- [Qualia S3 Compass](https://learn.adafruit.com/qualia-s3-compass.md)
- [Remote Shutter Button for MEMENTO](https://learn.adafruit.com/memento-shutter.md)
- [Creating Custom Symbol Fonts for Adafruit GFX Library](https://learn.adafruit.com/creating-custom-symbol-font-for-adafruit-gfx-library.md)
- [Daily Cheer Automaton](https://learn.adafruit.com/daily-cheer-automaton.md)
- [Running PyPortal Code on Blinka with Displayio](https://learn.adafruit.com/running-pyportal-code-on-blinka-with-displayio.md)
- [Trellis M4 Synth Design Tool](https://learn.adafruit.com/synthesizer-design-tool.md)
- [Case for Feather RP2040 USB Host](https://learn.adafruit.com/case-for-feather-rp2040-usb-host.md)
- [Demofruit: Demoscene for Fruit Jam](https://learn.adafruit.com/demo-jam-fruit-scene.md)
- [Trellis M4 Audio Filter Visualizer](https://learn.adafruit.com/trellis-m4-audio-visualizer-and-filter.md)
- [No-Code Battery Monitoring with WipperSnapper](https://learn.adafruit.com/no-code-battery-monitoring-with-wippersnapper.md)
- [NeoPixel Light Slate](https://learn.adafruit.com/neopixel-light-slate.md)
- [16-Step Drum Sequencer](https://learn.adafruit.com/16-step-drum-sequencer.md)
- [RP2040 RunCPM Emulator with USB Keyboard & HDMI screen](https://learn.adafruit.com/rp2040-runcpm-emulator-with-usb-keyboard-hdmi-screen.md)
