Automated bird species detection from acoustic recordings.
- About
- Download
- Features
- Usage
- Workflow
- Keyboard shortcuts
- Core Concepts
- Models
- Troubleshooting
- Changelog
- Acknowledgements
- Citation
- License
PAM Analyzer is a cross-platform desktop application designed to help researchers performing Passive Acoustic Monitoring (PAM). It provides a complete workflow for processing Autonomous Recording Unit (ARU) field recordings: from importing SD card contents and running automated species detection (using BirdNET v2.4 or v3.0), to reviewing, annotating, and exporting detections. The application organizes data into a hierarchical structure of projects and campaigns, making it easy to manage large-scale monitoring studies.
Pre-built binaries are available for the following platforms:
- macOS (Apple Silicon): PAM-Analyzer-macos-arm64.zip
- Windows (x86_64): PAM-Analyzer-windows-x86_64.zip
- Linux (x86_64): PAM-Analyzer-linux-x86_64.tar.gz
- Linux (arm64): PAM-Analyzer-linux-arm64.tar.gz
Note: On any supported OS you can also easily run PAM Analyzer from source using uv run poe run
- Project & campaign management: Organizes monitoring deployments into self-contained project folders. A project folder contains the campaigns, each supporting independent species filters (via geographic coordinates and/or custom species lists). Projects and campaigns store no absolute paths, so they are relocatable.
- SD card import: Automatically detects ARU SD cards matching a configured volume name pattern and imports audio into a structured
campaign/ARU/weekdirectory layout. Both AudioMoth and Wildlife Acoustics Song Meter Micro cards are supported, including Song Meter'sData/subfolder layout. WAV recordings are transcoded to FLAC (lossless, 16-bit PCM) on import to save disk space. - Species detection: Run BirdNET-2.4 or BirdNET-3.0-preview3.1, picked from a model dropdown, per-campaign or batched across campaigns, with a configurable confidence threshold and segment overlap. Each model writes its own CSV per campaign, so runs of both coexist and are reviewed side by side (see Output files).
- Detection review: Provides a tabular interface for detections with multi-column sorting, filtering, inline annotation (verification status, species correction, comments), and integrated audio playback.
- Data export: Supports exporting filtered detections to CSV format and extracting annotated audio snippets with metadata embedded in filenames.
Download and execute the binary for your platform from the Download section.
Upon first launch, use New Project and pick (or create) the folder that will hold your data such as recordings and detection CSVs. The app marks it as a project by writing a pam-analyzer.toml settings file into it. Then create at least one campaign in the Campaigns panel (audio import from SD cards is also handled there), run species detection in the BirdNET panel, and review detections in the Examine panel. More details are in the workflow section below.
If you used an older version of PAM Analyzer that stored projects as .pamproj files, use File > Open Legacy Project File… to select the .pamproj file. The app will offer to migrate it: detection CSVs are moved into their campaign folders, and the old file is kept as .bak. If the audio recordings folder moved since the project was created, a folder picker lets you relocate it. When opening a project folder that contains a .pamproj file, migration is offered automatically.
The application is organized into four panels that map to the steps of a typical PAM analysis workflow.
Configure a study in the project settings.
- If needed adjust the SD card volume name pattern: A regular expression to match SD card volume names for your ARUs. The default matches both AudioMoth (
MSD-) and Song Meter (2MM) cards; widen or narrow it to suit your devices. - Model settings:
- Min confidence: the minimum detection score (0 to 1) a species prediction must reach to appear in the output CSV. Lower values result in more detections but increasingly more false positives.
- Overlap: how much consecutive analysis windows overlap, in seconds (0 to 2.5 s). Overlap might help to catch vocalizations that would otherwise be split across a window boundary, at the cost of longer analysis time and/or duplicate detections.
- Species languages:
- Main sets the preferred language for the Species column in all CSV outputs and for exported audio snippets
- Extra adds one additional common-name column per checked language to the examine data table.
All settings are saved automatically to the
pam-analyzer.tomlfile inside the project folder.
Create and manage the campaigns that belong to this project. The panel on the left shows a list of all discovered campaigns. When no campaign is selected, a project-wide overview is shown on the right, displaying total campaigns, ARUs, recordings, disk usage, and date range. Clicking a campaign opens its settings where you can:
- Create a new campaign using the
+button. Each campaign must be configured with a species filter:- Location mode: specify a lat/lon on a map or enter coordinates manually. BirdNET derives the species list from this location. Here you can also add species you want to have always included when feeding the detection model. Names written under the older BirdNET v2.4 taxonomy are accepted, so a must-have entry saying
Accipiter gentilisstill matches theAstur gentilisthe model emits. See Species names. - Species list mode: provide a
.txtspecies list file, which is copied into the campaign folder alongside the audio.
- Location mode: specify a lat/lon on a map or enter coordinates manually. BirdNET derives the species list from this location. Here you can also add species you want to have always included when feeding the detection model. Names written under the older BirdNET v2.4 taxonomy are accepted, so a must-have entry saying
- Edit species filter settings at any time.
- Specifying species (species list mode and the location-mode must-have list use the same input and format): type or paste species names directly into the text box, one per line, or drag-and-drop a
.txtfile onto it (or use the import button to browse for one). Either way, the file's contents are loaded into the box rather than just referenced by path.-
Example, one entry per line:
# This is comment Turdus merula Parus major_Great Tit # another comment Fringilla coelebs_Buchfink Corvus coraxEach line is a scientific (Latin binomial) name, e.g.
Turdus merula. Lines copied from a BirdNET-style species list inScientific name_Common nameform also work, since everything from the underscore onward is ignored, regardless of which language the common name is in, soParus major_Great Tit(English) andFringilla coelebs_Buchfink(German) are parsed the same way as their bare scientific names. A#starts a comment that runs to the end of the line, whether on its own line or trailing a species name; the app uses this to mark must-have entries when it writesapplied-species-list*.txt, so that file can be pasted straight back into the species list or must-have box. Blank lines are ignored.Lists written before the move to BirdNET v3.0 keep working: names the older v2.4 taxonomy spelled differently (e.g.
Accipiter gentilisfor what v3.0 callsAstur gentilis) are matched against both spellings. See Species names.
-
The label files each model ships (one per language, each listing every detectable class as Scientific name_Common name) come with the model, so any single language's file doubles as the full scientific-name reference. In the macOS app they sit inside the bundle under Contents/Resources/birdnet-models/. In a development checkout they land in the library cache at ~/Library/Application Support/birdnet/acoustic-models/v3.0/ and .../v2.4/.
- Delete a campaign via the trash icon on its list card, with an inline confirmation step.
- Import audio from SD cards directly within a campaign's detail view. Click the import button to start monitoring for SD card volumes matching the configured name pattern. When a matching card is inserted, files are imported into the
campaign/ARU/weekdirectory structure with deduplication and conflict resolution. WAV recordings are transcoded to FLAC (lossless, 16-bit PCM) to save disk space, and any GUANO metadata (timestamp, location, device) is carried across into the FLAC. The encode is verified against the source before a card is cleared, so a recording is never lost to a bad transcode; FLAC sources and the device's provenance file are copied through untouched. The device family is recognised from the card layout: AudioMoth keeps recordings and aCONFIG.TXTat the card root, while Song Meter keeps recordings underData/and a<serial>_Summary.txtlog at the root.
Campaigns are discovered automatically from the project folder: any subdirectory containing a campaign.toml sidecar is treated as a campaign.
Choose the model and which campaign(s) to run against. The min confidence, overlap, and species language settings come from Project Settings. See Models for what each model detects, how its scores are produced, and which to pick.
The model choice is saved in the project file, so it travels with the study folder. Running a second model on a campaign does not replace the first model's results: each writes its own CSV and the Examine panel shows both.
The downloadable builds ship with both acoustic models, both geographic models, and the label files for every language already inside them, so analysis runs fully offline and the first run starts straight away. Only a development checkout downloads them, on first use, into the birdnet library's own cache.
Each detection is assigned a within-segment Rank (1 = highest-confidence species in that window), useful for deprioritising detections that are consistently outcompeted by other species in the same clip. Analyses can be run per-campaign or across all campaigns. See Output files for what is written to disk.
Analysis results are written directly into each campaign folder, next to the audio, with one detections CSV per model run:
{project}/
└── {campaign}/
├── detections-BirdNET-3.0-preview3.1.csv # one row per detection
└── applied-species-list-week-NN.txt # per BirdNET week, when the audio is organised in week_NN folders
- For each campaign
detections-{model_key}.csvis the file where the species detections are stored. The{model_key}suffix identifies the model (BirdNET-2.4,BirdNET-3.0-preview3.1). CSVs written by models no longer shipped (detections-Perch-2.0.csv) remain readable alongside new runs. Every row carries aModelcolumn identifying its source, plus the annotation columns (Verified,Corrected_Species,Comment). The Examine panel loads every model file it finds for the campaign and concatenates them. Annotations are written back to the file the row came from. TheFilecolumn is stored relative to the campaign folder, so renaming or moving a campaign never breaks its CSVs. applied-species-list*.txtis the merged list (geographic list plus an optional must-have species list, the latter tagged# must-have) the run actually filtered against, exported in location mode for reference.
No combined, summary, or per-week CSVs are produced: the "All campaigns" view in the Examine panel concatenates the per-campaign CSVs in memory, so it always reflects the current per-campaign files.
Review and annotate results. Detection CSVs are loaded into a grid with multi-column sorting and filtering, inline annotation editing (Verified, Corrected_Species, Comment), and audio playback per detection. When a campaign carries CSVs from more than one model version, all detections appear in the same grid; sort or filter on the Model column to slice by source. The info label above the table shows detection counts per model.
- Column filters: Click a column header to open the filter menu. Text columns support
contains,starts with, andends withoperators. TheCampaign,ARU,Species,Model,Verified, andCorrected_Speciescolumns also support an "Is one of" operator for multi-value selection. Date and time columns have dedicated date range and time range filters. PressingEnterin a filter input applies the filter immediately and moves focus to the table. - Max per ARU/Species: This control caps how many detections to keep for each ARU and species pair, keeping the highest-confidence ones (set it to
Allto disable). The cap is applied after the per-column filters, so it thins only the rows that already passed those filters. For example, setting the cap to 1 shows the single best detection per ARU and species. - Playback padding: The
⚙button lets you configure how many seconds of audio to play before and after each detection, helpful for hearing context around the vocalization. These values are saved per-project. - Annotations: Verified, Corrected_Species, and Comment edits are written back to the source CSV automatically.
- Export: The
⬇button offers CSV export of the currently filtered rows and audio snippet extraction with configurable padding.
When exporting audio snippets, annotation values are reflected in the output filenames:
- Verified: appends
_confirmed,_incorrect, or_uncertaindepending on the value. - Corrected_Species: replaces the original species name in the filename with the corrected one (scientific name looked up from the project language) and appends
_corrected.
Both suffixes can appear together, e.g. …_corrected_confirmed.wav.
| Windows/Linux | macOS | Action | Description |
|---|---|---|---|
| Ctrl+N | ⌘N | New Project Folder | Initialize a folder as a new project |
| Ctrl+O | ⌘O | Open Project Folder... | Open an existing project folder |
| Ctrl+W | ⌘W | Close Project | Close the current project and return to the welcome screen |
| Ctrl+Q | ⌘Q | Quit | Exit the application |
| Key | Action |
|---|---|
Ctrl+N / ⌘N |
Create a new campaign |
F2 |
Rename the selected campaign |
Delete |
Delete the selected campaign |
Escape |
Leave campaign details and return to the overview |
These shortcuts work whenever a row is selected in the Examine panel and no cell editor is open.
| Key | Action |
|---|---|
Space |
Play / pause the current detection's audio |
J |
Jump to the detection start marker in the audio player |
B |
Seek to the beginning of the audio file |
T |
Set Verified to true |
F |
Set Verified to false |
U |
Set Verified to uncertain |
C |
Open the Comment field for text editing |
S |
Open the Corrected Species dropdown |
Tip: While the Comment field or the Corrected Species dropdown is open, all single-key shortcuts are automatically suspended so you can type freely. Press
EscapeorEnter/Returnto confirm and return to normal navigation.
The largest organisational unit. A project represents a study or monitoring programme, e.g. "Bird survey of Lake Constance wetlands 2026". A project is a folder: it holds a pam-analyzer.toml settings file (ARU SD card volume name pattern, preferred species name language) and one subfolder per campaign. The settings file stores no paths, so the whole project can be moved, backed up, or shared as one folder. The project name is simply the folder name.
Note: Species filter settings (lat/lon location or species list) are campaign-scoped, not project-scoped.
A campaign is a time-bounded field deployment during which a set of ARUs were active. The campaign name is chosen by the researcher and typically encodes start date, end date, and study area, e.g. Campaign-20260114-20260216-Federsee. On the file system each campaign lives in its own subdirectory under the project folder and carries a campaign.toml sidecar that stores its species filter configuration. Detection CSVs are written into the campaign folder too, with audio paths stored relative to it, so a campaign is fully self-contained and can be moved, archived, or shared, including its analysis results and annotations. Campaigns are discovered automatically from the project folder. Individual ARUs within a campaign may be deployed at distinct locations within the study area.
species_filter_mode = "location" # "location" or "list"
latitude = 47.94
longitude = 9.32
species_list_path = "" # relative path to .txt, empty when using location modeThe combination of campaign + ARU device ID uniquely identifies a recording set within a project while the same physical ARU redeployed at a different time usually belongs to a different campaign.
An individual recording device, identified by its SD card volume name (e.g. MSD-109 for AudioMoth, 2MM30692 for a Song Meter serial). Within a campaign folder, each ARU gets its own subfolder. Recordings are further organised into weekly subfolders (week_08) derived from the file timestamps.
After setting up a project and importing ARU SD cards, the resulting directory structure looks like this:
{project}/
├── pam-analyzer.toml # project settings, written automatically
└── {campaign}/
├── campaign.toml # species filter configuration sidecar
├── species_list.txt # species-list mode only: the campaign's species filter list
├── must_have_species.txt # optional: extra species forced into a location-mode run
└── {aru}/
campaign.toml, species_list.txt, must_have_species.txt, and (after a run) the detection CSVs live in the campaign folder, beside the audio, so a campaign stays self-contained and can be moved, archived, or shared independently of the project. The species-list files are present only when the corresponding filter option is used.
Example:
~/Studies/2026-SW-Germany-PAM-Project/
├── Campaign-20260114-20260216-Federsee/
│ ├── campaign.toml
│ ├── MSD-109/
│ │ ├── week_02/
│ │ ├── week_03/
│ │ ├── week_04/
│ │ ├── week_05/
│ │ └── week_06/
│ └── MSD-110/
│ ├── week_02/
│ ├── week_03/
│ ├── week_04/
│ ├── week_05/
│ ├── week_06/
│ └── week_07/
└── Campaign-20260317-20260328-Lake-Constance/
├── campaign.toml
├── MSD-109/
│ ├── week_11/
│ └── week_12/
└── MSD-110/
├── week_11/
└── week_12/
PAM Analyzer runs two BirdNET models locally on the CPU through the birdnet library.
| BirdNET v2.4 | BirdNET v3.0 | |
|---|---|---|
| Release | BirdNET_GLOBAL_6K_V2.4 (Zenodo) |
BirdNET+_V3.0-preview3.1_Global_11K (Zenodo) |
| Backend | TFLite on ai-edge-litert (FP32) |
ONNX Runtime (FP32) |
| Audio window | 3 s | 3 s |
| Sample rate | 48 kHz (the library resamples other rates) | 32 kHz (the library resamples other rates) |
| Classes | 6,522 (birds, plus a few amphibians and insects) | 11,560 (birds, plus amphibians, insects and mammals) |
| Taxonomy | older eBird-based axis | shared with the v3.0 geographic model |
| Species languages | 27 | 29 |
| Size | ~49 MB acoustic + ~28 MB geographic | ~542 MB acoustic + ~16 MB geographic |
| Speed (Apple M4 Pro, CPU) | ~1050x real-time, about 3 seconds per hour of audio | ~60x real-time, about 1 minute per hour of audio |
Both models are bundled in the distributables. Each is paired with the geographic model of its own generation for region filtering.
v3.0 is a preview model not a final v3.0 release, so scores and label set may still change. When a final v3.0 ships, it gets its own model key and its own CSV, so preview and release detections never mix and it stays clear which produced what. BirdNET v2.4 is the default. It is an older but established release. It analyzes roughly 17x faster than the Birdnet-3.0 preview but is also not as good.
BirdNET v3.0 names species under a newer taxonomy than v2.4 did, so a handful of birds changed genus: the Northern Goshawk is Astur gentilis where v2.4 called it Accipiter gentilis, and the same applies to Charadrius/Anarhynchus plovers, Ciccaba/Strix owls, Ixobrychus/Botaurus bitterns, and others.
Detections are written under one taxonomy, whichever model produced them. Both engines' output is mapped through a bundled alias table (infrastructure/data/legacy_species_aliases.tsv, 175 pairs) before it reaches the CSV, so the same bird appears under one spelling across both engines and sorts together in the Examine grid. The Model column still records which engine produced each row.
Which taxonomy that is, is a project setting: Project > Species taxonomy. It applies to the next run, not to CSVs already written.
- BirdNET-3.0 (default) is the right choice for a new study as it matches current ornithological taxonomy standards. A v2.4 run is rewritten up to the current spellings.
- BirdNET-2.4 is for a study whose earlier CSVs already use the older names. Picking it keeps a re-run on the same spellings the existing time series uses, instead of splitting one bird across two names partway through. A v3.0 run is rewritten down to match.
The setting changes only what is written, never what is matched. Each run still filters detections against the geographic model of its own generation, on that model's own axis, so the chosen output taxonomy cannot change which detections survive region filtering.
Species lists work under either taxonomy regardless of the setting. Each name you type is expanded to cover both spellings, so Accipiter gentilis and Astur gentilis both match whichever model runs. This applies only to names you type: the species-list file and the must-have list.
Detection CSVs written by earlier versions of the app are not rewritten. A campaign analyzed before the upgrade keeps its detections-Perch-2.0.csv under its original name, and the Examine panel still loads it.
In location mode the runner filters detections against a per-week species list from the geographic model of the same generation as the acoustic model, so both sides speak the same taxonomy. For v3.0 that model carries 14,082 classes to the acoustic model's 11,560. The overlap is 10,653 names, so 907 acoustic classes have no geographic entry and are always dropped in location mode: mostly narrowly-distributed birds, genus-level entries such as Acris, and insects and frogs.
To keep those detections, run in species-list mode, which applies no regional filter, or add the specific names to the must-have box in location mode. The debug log reports the split per campaign, for example birdnet: per-week species filter dropped 412 row(s): 190 out-of-region, 222 not on the model's axis (legacy name or non-bird). 1391 kept. A large second number points at the acoustic/geographic gap rather than at geography.
The bundled build is CPU-only. In a development checkout you can swap the runtime for the CUDA build without any code change:
uv pip uninstall onnxruntime && uv pip install onnxruntime-gpuThe application writes a rotating debug log (pam-analyzer.log, capped at 1 MB with one backup) to the platform's standard log directory:
- Windows:
%LOCALAPPDATA%\PAM Analyzer\Logs\pam-analyzer.log - macOS:
~/Library/Logs/PAM Analyzer/pam-analyzer.log - Linux:
~/.local/state/PAM Analyzer/log/pam-analyzer.log
The easiest way to find it is Help > Open Log Folder in the app, which opens the folder directly in your file browser.
On Windows, %LOCALAPPDATA% lives under a hidden AppData folder that File Explorer doesn't show by default, so browsing there manually is not straightforward. If you don't have access to the app's menu, paste the path above into File Explorer's address bar (not the search box) and press Enter; Explorer will expand %LOCALAPPDATA% and navigate straight there.
The changelog can be found at the CHANGELOG page.
The author would like to thank the following projects:
- BirdNET
- ONNX Runtime
- Qt / PySide6
- Python
- Polars
- SciPy
- GUANO
- Mutagen
- NumPy
- platformdirs
- soundfile
- psutil
If you use PAM Analyzer in your work, you can cite it:
@software{Werner_PAM_Analyzer_2026,
author = {Werner, Ken},
title = {PAM Analyzer},
url = {https://github.com/kenwer/pam-analyzer},
version = {0.6.0},
year = {2026}
}This project is licensed under the AGPL-3.0 license. See the LICENSE file for the full text.
