Load a folder of audio files, arrange them in any order, and join them into a single WAV with a click marker between each file.
Fetching latest release… • All releases →
Because the app is not signed with a platform certificate, each operating system will warn you before it runs for the first time. This is expected — the steps below take about 30 seconds and only need to be done once per downloaded copy.
macOS Gatekeeper blocks apps that are not notarized with an Apple Developer certificate. You will see "The application 'concat' can't be opened." (error −47).
concat-macos-arm64.zip. A concat.app
file appears.Windows Defender SmartScreen warns about executables that are not digitally signed by a known publisher.
concat-windows-x86_64.exe.AppImage files need the executable bit set before they can be launched. Do this once after downloading.
Option A — file manager (no terminal):
concat-linux-amd64.AppImage in your file
manager.Option B — terminal:
chmod +x concat-linux-amd64.AppImage
./concat-linux-amd64.AppImage
libfuse.so.2, install it
with your package manager — e.g.
sudo apt install libfuse2 on Ubuntu/Debian.
After opening the app, click Load Folder… to get started. The app accepts WAV, MP3, FLAC, AIFF, OGG, M4A, and MP4 files; everything is converted to WAV on output. No audio files need to be converted beforehand.
| Section | What it does |
|---|---|
| Load Folder… | Opens a folder picker. All recognised audio files found inside are
loaded into the list, sorted alphabetically by default. The output
path is pre-filled as {folder}_joined.wav next to the
source folder. |
| File list | Shows the join order. Drag rows to rearrange. Shift+Click for a contiguous range; Ctrl/Cmd+Click to toggle individual items. Drag a multi-selection to move the whole group at once. |
| Sort mode | Toggle between Simple (GUI) and Advanced (Regex) sort controls. Only one is active at a time. |
| Save / Load Settings | Save all current sort settings to a YAML file and reload them later or share them with a colleague. The file is human-readable and includes inline comments explaining every option. |
| Output | Path for the joined WAV. Click Browse… to choose a different location or filename. |
| Bit depth | 16-bit (default, CD quality), 24-bit (studio), or 32-bit (maximum headroom). All input files are resampled to 44 100 Hz mono at the chosen bit depth before being joined. |
| Join Files | Concatenates all files in list order, inserting a 5 ms click burst and 500 ms silence between each pair. Runs in the background so the UI stays responsive. |
The Simple (GUI) radio button activates the standard sort controls. Choose a sort field and direction, then click Apply Sort.
| Field | Sorts by |
|---|---|
| Name (alphabetical) | Filename, A→Z or Z→A |
| Name (numerical) | Natural/numeric order — track2 before track10 |
| Date created | File creation time (birth time on macOS) |
| Date modified | Last modification time |
| Date accessed | Last access time |
Sorting only reorders what is already in the list. You can apply multiple sorts sequentially, or mix sorting with manual drag-and-drop.
The Suffix Order panel lets you define a custom ordering
for files that share a base name but differ by a suffix. This is designed for
Dekereke databases, where
a stimulus entry like 0001 produces files such as
0001-ynq-irt.wav, 0001-ans-irt.wav, etc.
How to use it:
Example — for files numbered 0001–0006, each with seven suffix variants:
Suffix list (in desired order):
ynq-irt
whq-irt
ans-irt
emphq-irt
emph-ans-irt
cntr-foc-irt
cntr-top-irt
After clicking Apply Suffix Order, the file list becomes:
0001-ynq-irt.wav
0001-whq-irt.wav
0001-ans-irt.wav
0001-emphq-irt.wav
0001-emph-ans-irt.wav
0001-cntr-foc-irt.wav
0001-cntr-top-irt.wav
0002-ynq-irt.wav
0002-whq-irt.wav
…
emph-ans-irt is matched
before ans-irt even if ans-irt appears higher
in your list — no special ordering of the entries is required for
disambiguation.
Entries can be plain substrings or full Python regex patterns. If you type an invalid regex, the app auto-escapes it and treats it as a literal string.
Switch to Advanced (Regex) to sort by text extracted directly from filenames using regular expressions. This mode replaces the Simple sort entirely — use it when the GUI sort fields are not flexible enough for your naming convention.
A regular expression (regex) is a pattern that describes text.
The most useful concept here is the capture group: any text
wrapped in ( ) parentheses is “captured” and used
as the sort key.
Pattern: ^(\d+)
Filename: 0042-ans-irt.wav
Captured: 0042 ← this value is used to sort the file
Each row in the Advanced sort panel is a sort layer. Layers work like columns in a spreadsheet sort:
Use ▲ / ▼ to reorder layers. Click + Add Layer to add a new one. The minimum is one layer.
The two-layer example above replicates the Suffix Order result: Layer 1 groups files by their leading number; Layer 2 sub-sorts them by suffix.
| Control | What it does |
|---|---|
| ▲ / ▼ | Move the layer up (higher priority) or down (lower priority). |
| Pattern | A Python regex containing at least one capture group ( ).
A ✓ appears when the pattern is valid;
✗ appears (with error tooltip) when
it is not. |
| Grp | Which capture group to sort by. Group 1 is the first
( ), group 2 is the second, etc. Default: 1. |
| Sort as | How to interpret the captured text (see table below). |
| Ascending / Descending | Sort direction for this layer. |
| ✕ | Remove this layer (minimum one layer always remains). |
| Mode | Behaviour | Example |
|---|---|---|
| Natural text | String sort, but runs of digits are compared numerically. Recommended for most filenames. | track2 < track10 |
| Numeric | Parse the captured text as a number (float). Use when the capture group contains only digits or a decimal value. | 120.5 < 130 |
| Alphabetical | Plain case-insensitive A–Z string comparison. Digits sort as characters, not numbers. | track10 < track2 |
If a file’s name does not match a layer’s pattern, that file is sorted after all files that did match — but it still keeps its position relative to other unmatched files, and is still sorted correctly by any higher-priority layer it did match.
These are the building blocks you’ll use most often. Every example below is a complete pattern ready to paste into the Pattern field.
| Pattern | Captures | Example filename → captured |
|---|---|---|
^(\d+) |
Leading digits at the start of the filename | 0042-ans-irt.wav → 0042 |
-(\w+-irt)\. |
A hyphen-separated suffix ending in -irt |
0001-ynq-irt.wav → ynq-irt |
_(\d{4}-\d{2}-\d{2}) |
A date in YYYY-MM-DD format preceded by an underscore |
rec_2024-03-15.wav → 2024-03-15 |
(\d{2,3})bpm |
A 2–3-digit BPM value | loop_128bpm_A.wav → 128 |
_([A-G][#b]?)_ |
A musical key between underscores | chord_Eb_maj.wav → Eb |
(\d+)$ |
Trailing digits at the end of the stem (before the extension) | take007.wav → 007 |
-(spkr\d+)- |
A speaker ID like spkr03 |
item01-spkr03-cond2.wav → spkr03 |
| Symbol | Meaning |
|---|---|
( ) | Capture group — the text inside is used as the sort key |
\d | Any digit (0–9) |
\w | Any word character (letters, digits, underscore) |
. | Any single character (escape as \. for a literal dot) |
+ | One or more of the preceding item |
* | Zero or more of the preceding item |
{n,m} | Between n and m of the preceding item |
^ | Start of the string |
$ | End of the string |
- | Literal hyphen (outside a character class) |
[abc] | Any one of a, b, or c |
[A-Z] | Any uppercase letter |
(?:…) | Non-capturing group (does not count toward the Grp number) |
Suppose your folder contains files named:
0001-ans-irt.wav 0002-ans-irt.wav 0003-ans-irt.wav
0001-ynq-irt.wav 0002-ynq-irt.wav 0003-ynq-irt.wav
0001-cntr-foc-irt.wav 0002-cntr-foc-irt.wav 0003-cntr-foc-irt.wav
You want the output order: all variants of 0001 first (ynq, ans, cntr-foc), then all of 0002, then 0003.
ynq-irt
ans-irt
cntr-foc-irt
Note: Alphabetical gives ans → cntr-foc
→ ynq. For a custom suffix order use Option A.
| Setting | Sample width | Use case |
|---|---|---|
| 16-bit (default) | 2 bytes / sample | CD quality, broadest compatibility, smallest file size |
| 24-bit | 3 bytes / sample | Studio / archival quality |
| 32-bit | 4 bytes / sample | Post-production, maximum headroom before mixdown |
All input files are normalised to 44 100 Hz mono at the chosen bit depth before being joined. The click marker between each file is a 5 ms 2 kHz sine burst with 500 ms of silence on each side.
WAV, MP3, FLAC, AIFF/AIF, OGG, M4A, MP4 — any format that ffmpeg can decode. The output is always WAV. The bundled ffmpeg binary handles all formats without any additional installation.
No. The portable app bundles everything it needs — Python, ffmpeg, ffprobe, and all libraries — in a single download. Nothing else needs to be installed.
Yes. Sort operations only rearrange the list — you can drag items freely before or after applying any sort. The list order at the moment you click Join Files is what gets used.
In Suffix Order mode, unmatched files sort after all matched files within their base group, using natural filename order. In Advanced Regex mode, unmatched files sort after matched files for that layer.
The ✓ only confirms the pattern is syntactically valid, not that
it matches your filenames. Check that your capture group ( )
is in the right place and that the Grp number matches the
group you intend. Test patterns at
regex101.com —
choose “Python” flavour.
Decoding, resampling, and writing audio is CPU-bound and scales with total audio duration. The join runs in the background so the UI stays responsive. A folder of 50 short WAV clips should finish in a few seconds; long MP3s decoded through ffmpeg take longer.
Click Save Settings… in the sort mode row to write a YAML file. Click Load Settings… to restore them in any future session. The YAML file is plain text and can be edited by hand or shared with colleagues; every field includes an inline comment listing the allowed values.
The current release is built for Apple Silicon (arm64) only. It will not run on older Intel Macs. Intel Mac support may be added in a future release. In the meantime, Intel Mac users can run the app from source — see the repository README.