Frequently Questioned Answers

  1. General
    1. Why the name "Benben"?
    2. Why did you turn Benben into a general music player?
    3. What formats are planned for future versions?
    4. Why the AGPLv3 license?
    5. Will Benben every be ported to other operating systems?
    6. Will you add support for format $OTHER_FORMAT?
    7. Why a TUI instead of a GUI?
    8. Are there any "non-goals" for Benben?
    9. Did you use any AI when writing Benben?
    10. Can Benben help me make a pizza?
  2. Troubleshooting
    1. The audio plays way too fast!
    2. I have a custom skin, but the colors look all wrong.
    3. All the text looks wrong/missing.
    4. Benben crashed and I don't see an error message.
    5. My MIDI files use up too much CPU and/or are glitching out.
  3. Configuration
    1. How many equalizer bands can I have?
    2. I set max-loops to 1 and the song doesn't loop...
    3. What are some good sizes for buffer-size?
    4. What audio backend should I use?
    5. I have a song-specific config file, and I want it to match multiple files. How can I do that?
    6. I have a song that can be matched by two song-specific config files. Which one takes effect?
  4. Internals
    1. Why write it in Common Lisp?
    2. Which parts are written in native Common Lisp, and which are just C bindings?
    3. What's Benben's overall internal design?
    4. How do song-specific configurations work?

 

General

 

Why the name "Benben"?

The program is actually named after Tsukumo Benben from Touhou Project, a character who was transformed from a musical instrument. Given her connection to music, and Remilia's obsession with Touhou, it made sense to name the program after her.

 

Why did you turn Benben into a general music player?

Benben started life as a VGM player. But as Remilia used it, she found that the interface worked better for her than other places, and she kept wishing she could listen to all her other formats with it as well. Thus Benben became a general-purpose music player starting with v0.5.0.

Benben does prioritize certain formats during development, however. In rough order:

  1. MPEG-1, Modules/Tracker files, VGM
  2. WavPack, FLAC, Vorbis, QOA/XQAF
  3. SID
  4. Opus
  5. WAV/Au (as in, playing WAV/Au files)
  6. MIDI/MUS

 

What formats are planned for future versions?

These aren't guaranteed to arrive, but I hope to eventually add:

  • S98
  • Atari SAP
  • GYM
  • SHN

 

Why the AGPLv3 license?

To be as hostile towards any potential commercial use of its codebase while still being GPL-like. If there hadn't been a need to link against GPL code, then Benben probably would have been written under the Cooperative Source License instead.

 

Will Benben every be ported to other operating systems?

Benben is primarily a Linux program, and meant for use on desktop/laptop computers. General Unix support also a goal, but not generally tested. Haiku and/or Plan 9 would be nice to have. There are zero plans to port it to Windows or Mac, though you may be able to at least get it working on a Mac since it is still Unix under the hood. This is, however, totally unsupported.

 

Will you add support for format $OTHER_FORMAT?

Maybe. It depends on of there's a standalone library that can decode the format, if it's an open and free format, and if the library is open source. Chances are even better if there's a native Common Lisp library for it already, as native Common Lisp code is preferred over C bindings, though this isn't required.

An exception is AAC, which will never be supported.

 

Why a TUI instead of a GUI?

Four reasons, really:

  1. TUIs use a lot less memory and are very fast.
  2. GTK started to suck starting with Gtk 4 (at least IMO), and GTK+ 2.x is EOL.
  3. Qt is a pain to use from any language that isn't C++, and I've had more trouble themeing Qt apps than things written in other toolkits.
  4. TUIs are awesome, more portable, and can look really cool.

Also, Benben was partially inspired by a program Remilia used to use on MS-DOS called MPXPLAY, which she loved using as a kid.

 

Are there any "non-goals" for Benben?

A few, yeah:

  • Non-goal: Be the most lightweight, fastest player. Instead, Benben aims to be simply fast enough to run on a Raspberry Pi 4.
  • Non-goal: Be the absolute most accurate VGM and module player ever. While Benben will definitely try to be as accurate as possible, it's not exactly a goal to becoming the most accurate VGM/module player ever. This is because Benben is developed by one person (Remilia), who has limited time in her day. Instead, Benben's goal is to be accurate enough for 95% of the listeners out there. Essentially, equivalency with vgmplay is the goal.
  • Non-goal: MP4/AAC support. It ain't happening.
  • Non-goal: Be a "one-size-fits-all" music player. Benben was started because Remilia was tired of music players that try to please everyone, because inevitably they never really pleased her. Benben is primarily designed for people who like being on the command line, and for those who like to use their filesystem to sort music rather than letting the player maintain a library.
  • Non-goal: Snap and Flatpak releases.

You may also be interested in the project goals page.

 

Did you use any AI when writing Benben?

Obviously I can't speak for any 3rd party libraries. But, as far as Benben (and CL-RemiAudio, CL-RemiChips, SatouSynth, CL-Remi-Slang, CL-SDM, and any other support library that I (Remilia) have written) is concerned, absolutely not. No AI has been used to write Benben. Vibe coding is trash.

 

Can Benben help me make a pizza?

Technically yes, it will inspire and entertain you as you cook it, so the time passes faster.


 

Troubleshooting

 

The audio plays way too fast!

Are you using the PortAudio backend? If you are, try using PulseAudio or libao instead. Sometimes the PortAudio backend will have synchronization problems.

 

I have a custom skin, but the colors look all wrong.

You probably have a skin that uses 24-bit color values, so the first thing to do is ensure that your terminal emulator supports 24-bit colors. If it does, then the TUI toolkit probably just isn't detecting the support correctly. Try setting the environment variable COLORTERM=truecolor.

 

All the text looks wrong/missing.

Benben's default configuration assumes you have a terminal that supports Unicode, so first check that your terminal does indeed support this. If it does, it's possible that you have a font that doesn't have Unicode support, or lacks the characters you need.

If you don't want to change to a different terminal, and it's only the VU meters that look incorrect, then your only option is to change the VU characters. This can be done in the configuration file in the vu-meter section.

 

Benben crashed and I don't see an error message.

Check in ~/.local/share/benben/stderr.log and see if an error message appeared in that file. Note that this file is cleared each time you run Benben.

 

My MIDI files use up too much CPU and/or are glitching out.

Try using a different SoundFont. Some SoundFonts can be pretty complex (even if their file size is small), and can really slow down the SoundFont synthesizer.

If your SoundFont is on a slow HDD or on a network, try moving it onto a faster medium, like an SSD.

If that doesn't work, try increasing your buffer size a bit. This is the buffer-size config option. The default is 256, so try 512, 1024, or even 2048.

Disable the reverb and chorus effects (the r and x keys during playback). Also try disabling the equalizer if it's enabled (the e key).


 

Configuration

 

How many equalizer bands can I have?

You can have one low-shelf, one high-shelf, and as many normal bands as you wish. Just keep in mind that each one adds a small amount of processing time, so more bands means more CPU usage.

 

I set max-loops to 1 and the song doesn't loop...

max-loops is kinda poorly named, sorry about that 😅 When max-loops is equal to 1, Benben interprets this as "play the song through one time." If it's equal to 2 then it will play the song through twice (so, it loops once).

 

What are some good sizes for buffer-size?

In nearly all cases, a buffer size of 1024 should be perfectly fine. If your computer has trouble playing files, try increasing it to 2048. You shouldn't need to go over 4096, though the program will let you.

Keep in mind that very large buffer sizes will cause the VU meter to become choppy, and could also decrease performance.

 

What audio backend should I use?

In most cases, PulseAudio will work just fine. PortAudio is also a good one to try, and may make the VU meter work smoother, depending on your computer's setup. The ao driver (short for libao) is solid as well, but has two drawbacks: it always uses 16-bit integer output (as opposed to 32-bit float), and it needs just a tiny bit more processing power. But, should neither PortAudio nor PulseAudio work for you, ao is a very good option.

For what it's worth, Remilia uses the PortAudio backend in most cases.

 

I have a song-specific config file, and I want it to match multiple files. How can I do that?

There are two ways you can do this. The most simple way is to just list multiple matches:

match:
  - "/path/to/file1.ogg"
  - "/path/to/file2.ogg"
  - "/path/to/file3.ogg"

The alternative is to use wildcards ("globbing")). Benben's glob syntax is slightly different and follows how Crystal's standard library behaves. Here are some examples from Remilia's personal collection of song-specific configs:

# Look for files that are in a "vgms-and-mods/VGMs/X68000" directory (any
# parent), and then any subdirectory under that folder.
#
# So this would match any of these:
#   - /home/remilia/mnt/server/vgms-and-mods/VGMs/X68000/Granada/03 Heavy Line (Stage 1 - Dead City).vgzst
#   - /mnt/other-server/vgms-and-mods/VGMs/X68000/Akumajo Dracula/02 Black Mass (Opening).vgz
#
# And so on...
match:
  - "/**/vgms-and-mods/VGMs/X68000/**/*.vg*"
# Matches any file that ends in .flac (regardless of the case of the extension)
match:
  - "**.[fF][lL][aA][cC]"

 `

I have a song that can be matched by two song-specific config files. Which one takes effect?

Song-specific config files are processed in alphabetical order, so files with later names take precedence.


 

Internals

 

Why write it in Common Lisp?

Because Common Lisp is fast, elegant, fun to use, doesn't use off-side rule syntax, and isn't C++ or Rust. Remilia has also been using Common Lisp for over 20 years, so she knows her way around it fairly well.

Versions prior to v0.7.0 were written in Crystal. See here and here for information as to why Benben was ported from Crystal to Common Lisp.

 

Which parts are written in native Common Lisp, and which are just C bindings?

These components are 100% pure native Common Lisp code:

  • SatouSynth (the VGM library, including all emulator cores)
  • MIDI sequencer and SoundFont synthesizer
  • FLAC decoder
  • Vorbis decoder
  • QOA and XQAF codec
  • BZip2 implementation
  • All DSP effects (reverbs, EQ, filters, stereo enhancer, soft clipping, etc.)
  • High-level interfaces to libao, ALSA, and out123.
  • High-level interfaces to S-Lang, libopus, libmpg123, libxmp, and libwavpack.
  • Config management
  • Ogg demuxer
  • WAV/Au rendering
  • WAV/Au reading
  • Vorbis Comment parsing
  • CUE writing
  • XSPF/JSPF parsing
  • Resampling
  • Various other core stuff (bitreaders, arg parsing, etc. - basically the stuff in CL-SDM)
  • All of Benben itself

The rest are bindings to C libraries:

  • Opus decoder (libopus)
  • MPEG-1 loader/decoder (libmpg123)
  • Module loader/decoder (libxmp)
  • WavPack loader/decoder (libmpg123)
  • SID player (libsidplayfp and libremicsid)
  • Low-level TUI stuff (S-Lang)
  • Low-level libao, ALSA, and out123 bindings
  • ZStandard bindings

 

What's Benben's overall internal design?

In normal playback mode, Benben is basically a collection of subsystems that pass messages over a common bus. When a message is sent, all subsystems have access to this message, and can choose if they need to act on it.

Benben's core is the PROGRAM-MANAGER class, which handles the setup and teardown of other subsystems, and also dispatches messages between them. The subsytems are:

  • The interface, which updates the screen and handles input.
  • The player, which sets up the playback engine, then generates audio and passes it to the backend driver.
  • The remote control system.
  • A logging system that can handle concurrent requests, and either prints stuff to the screen (only during startup) or to the stderr log file (after playback). This isn't part of Benben itself, but part of Remilia's CL-SDM library.

There are also three subsystems which do not run on their own threads, but exist as singletons. These all run on the main thread:

  • The FILE-MANAGER class is responsible for loading files.
  • The PLAY-QUEUE class is the virtual playback queue.
  • The PROGRAM-SETTINGS class handles the current in-memory configuration state for the program.
  • The PLAYER-MANAGER class handles the setup of individual PLAYER subclass instances and a few other player-related functions.
  • The SOUNDFONT-MANAGER handles the loading/unloading of SoundFonts.
  • The actual backend audio driver.

Files to be played are represented using classes that all derive from the PLAYABLE-FILE class. These allow audio data to be loaded on-demand, decreasing overall memory usage, and allow instances of TAG-INFO to be created that store metadata info. Each subclass of PLAYABLE-FILE does the actual rendering of audio data to a buffer using the PLAYABLE-DECODE generic function.

The PLAYER subclasses handle looping, resampling, effects, and other playback-related functions. These subclasses, the PLAYABLE-FILE subclasses, and the EFFECTS-STACK class are the real meat and potatoes of Benben's audio pipeline.

Things change when Benben is in rendering mode since rendering files to disk is an embarrassingly parallel problem. In this mode, Benben has a central RENDERER instance that acts as a general manager, and also is responsible for updating the screen. It launches multiple JOB instances in parallel, one for each file to be rendered, where each JOB is an instance of a subclass that's specific to a particular format of music. The JOB class is itself a subclass of PLAYER. These JOB subclasses do the actual rendering, and send their audio to a RENDER-STREAM subclass while reporting back their progress via message passing to the RENDERER. The RENDER-STREAM subclasses handle the final output to disk; there is one subclass per output format.

 

How do song-specific configurations work?

When Benben first starts up, it loads its main configuration file into RAM. This is then turned into an PROGRAM-SETTINGS, which is the actual class that handles the current configuration state of the program. After this, command line arguments are applied.

Once the main configuration is loaded, all song-specific configuration files are loaded RAM.

Before playing a file, Benben will check its collection of song-specific configurations to see if any of them match the song's filename. When one is found, then that song-specific configuration is applied to the PROGRAM-SETTINGS instance.

The PROGRAM-SETTINGS knows which settings can be overridden by a song-specific configuration, and stores these settings as a stack that always has either one element or two, where the 0th element is the setting from the main configuration file and the 1st element (if it exists) is the song-specific setting. When a setting is accessed, the latest value is always returned. When the song changes, any song-specific configurations that were applied are popped.