Mountaineering Acoustics Network

Mountaineering Acoustics at the Summit of Discovery

Running field-audio-tools on a long dual-mic F3 take

Yema Haizi, Kangding, Sichuan

written by Gewenxin Yu on 2026-07-24, last updated 2026-08-07

Background

Info about the selected recording file

We ran all three commands on Take 002, a three-hour Zoom F3 recording. The notes below cover the input, the files written, and two gaps we found in lowdom reporting and threshold calibration.

Field details

The GPX trail to the recording site begins on highway S434 at Yagya Ge, Sichuan, at roughly 3,894 m elevation, with roadside access on the pass before the walk in. It ends at the dome tent at Yema Haizi, about 4,055 m, with Yema Haizi lake to the east of that camp. We were guided by Dong Sheng & Xiao Zhang (local mountain guides in Kangding) and made most of the recordings the next morning.

The recording files used in this report were captured at specific positions on Yema Haizi lake itself. The season was suitable and the lake had frozen over. On the northeast side, the ice thinned toward the deeper water shown in Fig. 2 and along the shelf leading into the area of Fig. 3; that is, to the right of where I stood in Fig. 4, where thinning ice and open water were clearly visible. We treated that edge with caution when passing through. Other positions on the lake were safe for extended recording.

In fact, melting ice that season produced especially vivid sounds. Our local guides call the phenomenon long hou (“dragon roar”); people who live in the area know it well. On the multimedia report, that signature is easy to find in green bar areas (it is of course not limited to them). It sits within the wider biological soundscape of the basin in a way that still feels astonishing.

Those same green bars are what we handed to birdidpv. Running BirdNET over the 135 unflagged ranges, 43 minutes of the three-hour take, returned 128 detections in 11 species. Red-billed Chough and Large-billed Crow account for 99 of them and are unambiguous. The other nine are all deep-voiced waterbirds, on a lake that had frozen over, and we think most of them are the dragon roar being heard as birds.

Not all of them, though. The second highest score any species reached in three hours, Ruddy Shelduck at 0.928, is Xiao Zhang calling across the lake to me. There is no bird in it. The identification report renders every detected frame as a spectrogram so these readings can be checked rather than taken on trust, and that frame is the one to open first: it shows a voice, it shows how easily a voice reads as a call, and it shows why the only thing that settled the question was listening.

This is worth more thought. We would like the toolkit to find human speech on its own and leave it out of what gets uploaded, so that an outdoor ecological recording can be shared without a voice riding along in it. This matters most on trips with a guide. We value the talk that happens along the way and would not want to lose it, but when the aim is the soundscape itself, or a soundscape captured while walking, every voice left in the take becomes work someone has to do by hand later.

Our recording setups in the field vary; for this three-hour test take, the chain we used is described under Input. See Fig. 4 for a field photograph of the session.

field-audio-tools

Three CLI tools for long Zoom F3 takes: excerpt a timestamped range with provenance, screen fixed windows for low-frequency dominance, or ask BirdNET what it hears in the windows that screening left alone.

Prerequisites

  • Python 3.11 or newer
  • FFmpeg on PATH, both ffmpeg and ffprobe
  • NumPy, installed with the package

There is no PyPI release yet. Install from a checkout:

python -m venv .venv
. .venv/bin/activate
python -m pip install -e '.[test]'
pytest

A checkout on FAT or exFAT has no symbolic links, which venv needs for its bin/. Build the environment on a Linux filesystem instead and point it at the checkout:

python -m venv ~/.venvs/field-audio-tools
~/.venvs/field-audio-tools/bin/pip install -e '/path/to/field-audio-tools[test]'

That is all soundcite and lowdom need. birdidpv takes one more extra:

pip install -e '.[birdnet]'

It pulls in TensorFlow, which is much heavier than the rest of the project, and downloads the BirdNET model files on first run. Without it birdidpv exits with an install hint. soundcite and lowdom are unaffected either way.

Result formats

Bare HTML from the CLI; demo packages linked here add site navigation and shared media styling.

Input

Both commands used the same pair of synchronized mono WAV exports from Take 002. They read the files in track order, in place, without creating a merged stereo intermediate. Site access, camp, and on-lake conditions are under Field details above.

  • Files: 260115_002_Tr1.WAV and 260115_002_Tr2.WAV
  • Recordist: Gewenxin Yu
  • Microphones: two Primo EM273 omni-directional microphones, −37 dB ± 3 dB at 1 kHz sensitivity, 80 dB S/N ratio, 60 Hz ~ 20 kHz frequency range
  • Recorder: Zoom F3, firmware 1.10 metadata
  • Encoding: 48 kHz, mono, 32-bit floating-point PCM per track
  • Duration: 03:02:24 per track
  • Input size: 2,101,379,072 bytes per track

soundcite: an arbitrary excerpt

We cut a 20-second passage beginning one hour into the take. Because we launched the command from the recording directory, the manifest kept the source filenames without exposing local absolute paths.

soundcite 260115_002_Tr1.WAV 260115_002_Tr2.WAV \
  --start 01:00:00 \
  --duration 20 \
  --title "Take 002 - 01:00:00 excerpt" \
  --recordist "Gewenxin Yu" \
  --recorded-at "2026-01-15T13:19:25+08:00" \
  --location "Yema Haizi, Kangding, Sichuan" \
  --license "CC BY-NC-SA 4.0" \
  --notes "Recorded with two Primo EM273 omni-directional microphones (−37 dB ± 3 dB at 1 kHz, 80 dB S/N ratio, 60 Hz ~ 20 kHz) connected to a Zoom F3." \
  --output 260115-002-soundcite
  • Runtime: about 2.2 seconds on the test workstation
  • Selected range: 01:00:00.000–01:00:20.000
  • Exact sample range: 172,800,000–173,760,000 at 48 kHz
  • Track excerpts: two 32-bit float WAV files, 3,840,206 bytes each
  • Preview: stereo Ogg Opus with an explicitly recorded peak limiter
  • Other files: lossless-source spectrogram, manifest, SHA256SUMS, and static HTML

Two-channel logarithmic spectrogram generated by soundcite

Browser preview and two-channel logarithmic spectrogram generated in the same run. The preview is convenient but lossy; the spectrogram is generated from the two WAV excerpts, which preserve floating-point samples.

soundcite output

  • The two mono F3 files can be treated as one paired take without first creating a multi-gigabyte stereo file. Before extraction, the tool verifies their sample rate, duration, channel count, recording time, and BWF time reference.
  • Embedded F3 fields such as scene, take, date, recording time, track name, and coding history survive into the manifest.
  • SHA256SUMS covers both excerpts, the preview, spectrogram, manifest, and static page. We did not ask it to hash the complete source files in this run.
  • The package labels the lossy listening copy separately from the preservation-oriented excerpts.

lowdom: screening the take

lowdom decoded both tracks to mono at 4 kHz and divided them into 10-second windows. It measured those windows without writing a transformed audio file.

lowdom 260115_002_Tr1.WAV 260115_002_Tr2.WAV \
  --window 10 \
  --threshold 0.95 \
  --report both \
  --output 260115-002-lowdom
  • Runtime: about 67 seconds on the test workstation
  • Analysed duration: 03:02:20
  • Windows: 1,094 ten-second rows
  • Default threshold: 0.95
  • Rows above threshold: 833 (76.14%)
  • Rows below threshold: 261 (23.86%)

Testing the threshold by blind listening

We tested 0.95 with two blinded listening rounds: each round contained ten randomly selected windows above the threshold and ten below it. Of the 20 windows above 0.95, 19 were judged to contain obvious wind noise. Of the 20 below it, 17 were judged not to contain obvious wind noise.

The threshold and listening judgement agreed on 36 of 40 samples. This is useful for prioritising listening on this take, but it is not a validation study: the sample is small, it comes from one recording setup, and it was reviewed by one listener. The score remains a low-frequency screening heuristic rather than a wind probability.

lowdom output

  • windows.csv retains both tracks' score, RMS level, low-frequency ratio, spectral flatness, and peak for every interval.
  • candidate-clean-intervals.csv lists contiguous below-threshold ranges for listening queues (135 intervals on this take).
  • summary.json records the frequency bands, sample rate, threshold, merge rule, source metadata, and totals.
  • The static report allows a quick overview without Python, a notebook, or a server. The Mantle site report adds navigation and styling; the bare HTML 4.01 page that lowdom writes to report.html is also kept in the package as report-bare.html.
  • The multimedia report adds a lossy 48 kbit/s stereo preview. After enabling audio once, hovering over the timeline plays the corresponding ten seconds. The Mantle site report adds navigation and styling; the bare HTML 4.01 page the CLI writes as report-multimedia.html is also kept here as report-multimedia-bare.html (same recording-preview-48k/ segments).
  • No interval is silently removed from the source audio; the candidate clean file is only a list of timestamps.

Known gaps

  1. Unreported partial tail. The source lasts 03:02:24 but analysis stopped at 03:02:20. The current code drops a final fragment shorter than half a window without recording the dropped duration in summary.json or the reports.
  2. Threshold placeholder. The initial 0.6 CLI default separated almost nothing on this take; it had not been checked on this recording before that exploratory run. We recalibrated with blind listening and adopted 0.95 for the run documented here.

birdidpv: the clean windows

lowdom says where to listen. It does not say what is there. It has already found the stretches of this three-hour take where wind is low enough to work with, and birdidpv starts from that answer. The candidate-clean-intervals.csv file above becomes the list of ranges we hand to BirdNET, as timestamps rather than as copied audio. The model then hears only the 23.86% of the take that screening left unflagged.

None of this says the windows at or above 0.95 are empty. Plenty of birds call through wind, and the orange stretches of the timeline hold some of them (the orange bars next to a green one are the ones to try first, and they often turn something up). The aim here is narrower: find which species turn up where wind is low, and collect those seconds as recordings clean enough to send to a free archive like xeno-canto.

birdidpv 260115_002_Tr1.WAV \
  --intervals 260115-002-lowdom/candidate-clean-intervals.csv \
  --lat 29.898 --lon 102.030 --week 3 \
  --min-conf 0.25 \
  --photos \
  --spectrograms \
  --report both \
  --output 260115-002-birdidpv
  • Track analysed: 260115_002_Tr1.WAV only
  • Ranges: the 135 below-threshold intervals, 00:43:30 of the 03:02:24 take
  • Frame length: 3 seconds, fixed by the model
  • Minimum confidence: 0.25
  • Species filter: 29.898°N, 102.030°E, week 3 of 48
  • Runtime: about two minutes of analysis, plus 19 seconds for 126 spectrograms and about 25 seconds for 11 reference photographs
  • Detections: 128 frames across 11 species

Position filter

The coordinates come from the end of the GPX trace, at the camp on Yema Haizi. With them, BirdNET cuts its list of 6,522 species down to the 788 that could be there in mid-January. Without them, the model offers birds from the wrong continent, and it sounds just as sure when it does. A three-hour take gives it many chances. Even the shorter list is long enough to hold every mistake below.

Detections

Two corvids make up 99 of the 128 frames, and neither is in doubt: Red-billed Chough (82 frames, highest 0.9998) and Large-billed Crow (17 frames, highest 0.9118). Both have harmonic structure well above 2 kHz, and both sit 3–11 dB louder than everything else in the run.

The other nine species share 29 frames. All of them are deep-voiced waterbirds: Great Bittern, Ruddy Shelduck, Eurasian Coot, Graylag Goose, Eurasian Curlew, Eurasian Wigeon, Mallard, Common Shelduck, Gray Heron. The lake had frozen over. Great Bittern is the clearest error: twelve detections, highest 0.9064, arriving in long runs with eight of them inside seventy seconds. Not one of their spectrograms shows a call above the low-frequency bed. That bed is the melting-ice resonance described under Field details. Our guides call it long hou, and the model hears it as the birds whose calls are booms.

It is worth comparing the two by ear. The first page of xeno-canto for Botaurus stellaris has XC891071, XC1000766, XC832807 and XC741523. The booms really do sound like the dragon roar. They are close enough that the model's mistake looks fair. What the bird does not have is the electronic quality the ice carries. That is the one thing that tells them apart. It is easy to hear, and none of the spectrogram numbers we worked out found it.

False positive at 01:41:05

This one frame matters more than the pattern. The model calls the frame at 01:41:05 Ruddy Shelduck at 0.928, the second highest score any species reached in the run. There is no bird in it. It is Xiao Zhang calling across the lake to the recordist, who knew the voice as soon as he heard it.

We only got there after two wrong answers. The band measurements put the frame with the ice, but for the wrong reason. Then its spectrogram showed clear harmonic stacks between 1 and 3 kHz. Ice does not make those, so we read it as a real call and took the species off the doubtful list. Both readings were wrong, in opposite directions, and the picture that fixed the first one is what led us into the second. A voice looks like that too: a pitch contour sliding through its harmonics, formants near 1.2 and 2 kHz, four syllables in the last second.

Three things follow. First, confidence does not tell true from false on this take. At every threshold from 0.25 to 0.9, the false positives match or beat the real detections, so raising the bar only drops the harmless errors and keeps the dangerous ones. Second, anthropophony is not in a filtered species list. Human sound has no label of its own, so it has to come out as some bird. Third, a spectrogram tells you that something is there. It does not tell you what. Only listening settled this one.

Single-track limits

lowdom screened both tracks and kept the higher score of the two. This birdidpv run read Tr1 on its own. A call that came out better on the second microphone could be missed, and nothing here says anything about stereo or direction. The tool can read both tracks and keep the highest confidence per frame. That is the next thing to do.

birdidpv output

  • detections.csv has every 3-second frame above the minimum confidence, with its timecodes, species, and score. species.csv totals them by species.
  • summary.json records the source, the ranges analysed, the position and week used to filter, and every other setting of the run.
  • detection-spectrograms/ holds 126 WebP images, one per detected frame. FFmpeg made them from the source WAV, not from the lossy preview. Frames that start at the same time share an image, so 128 detections need only 126 files. Each one is a single call, here the Red-billed Chough frame at 00:40:35:
ffmpeg -ss 2435 -t 3 -i 260115_002_Tr1.WAV \
  -lavfi "[0:a:0]showspectrumpic=s=560x260:legend=1:scale=log:fscale=log:color=intensity" \
  -frames:v 1 frame-002435000.webp

fscale=log is the part that decides what you see. On a linear frequency axis the whole band below 500 Hz collapses into a line at the bottom of the image, and that band is where the ice sits and where most of these detections are argued about. scale=log does the same for amplitude, and legend=1 prints the frequency, time and dBFS axes. s=560x260 sets the spectrum area; the legend adds roughly 280x130 around it, so the files are 844x388. The four xeno-canto recordings above are rendered with the same call, over their full length instead of three seconds.

  • species-photos/ holds one reference photograph per species from iNaturalist, kept in the package instead of hot-linked, so the page still works if it is saved and no one else learns who read it. credits.json records each photographer, licence, and source. A photograph shows what a species looks like. It is not proof that the bird was there, and nine of these eleven go with rows we are asking you to doubt.
  • The multimedia report reuses lowdom's recording-preview-48k/ segments instead of copying 62 MB of audio. Click a detection to play its three seconds and see its spectrogram. Click a species to show only that species on the timeline.
  • As with lowdom, the site reports add navigation and styling. The bare HTML 4.01 pages the CLI writes are kept next to them as report-bare.html and report-multimedia-bare.html.

soundcite: citing a detection

The excerpt at the top of this test starts at 01:00:00. We chose that time from the clock, before we had looked at the recording. It shows what the output format is, not what is in the take.

After birdidpv there is a better time to cut. Sort detections.csv by time and the longest unbroken run in the take starts at 00:40:29: seven frames back to back, all Pyrrhocorax pyrrhocorax, scoring 0.9863 to 0.9998. The single highest frame in the whole take sits inside it, at 00:40:35. A run is better evidence than one frame, so cut from the start of the run rather than from its best frame. detections.csv stores that time as start_timecode in HH:MM:SS.mmm, which is one of the formats --start accepts, so the value can be copied straight across:

soundcite 260115_002_Tr1.WAV 260115_002_Tr2.WAV \
  --start 00:40:29.000 \
  --duration 21 \
  --pad-start 3 \
  --pad-end 4 \
  --title "Take 002 - Red-billed Chough at 00:40:29" \
  --recordist "Gewenxin Yu" \
  --recorded-at "2026-01-15T12:59:54+08:00" \
  --location "Yema Haizi, Kangding, Sichuan" \
  --license "CC BY-NC-SA 4.0" \
  --notes "Seven consecutive BirdNET frames identified Pyrrhocorax pyrrhocorax here, scoring 0.9863 to 0.9998. Recorded with two Primo EM273 omni-directional microphones (-37 dB +/- 3 dB at 1 kHz, 80 dB S/N ratio, 60 Hz ~ 20 kHz) connected to a Zoom F3." \
  --output 260115-002-chough-004029

--duration 21 covers the seven frames. The two --pad values keep three seconds before them and four after, because 00:40:29 is a frame boundary, not the moment the bird began. BirdNET only says the call falls somewhere inside those three seconds, so cutting exactly on the line can clip the first call.

The written excerpt therefore runs 00:40:26 to 00:40:54, starting at sample

  1. manifest.json keeps the two ranges apart: the top level describes the 28 seconds in the WAV, because that is what the checksums cover, while clip.cited records the 21 seconds being cited. The excerpt is lossless and links back to both source tracks.

The 28 seconds this section keeps pointing at: the 21 seconds being cited, plus three seconds before and four after. This player uses the lossy preview; the two 32-bit float WAV excerpts in the package are what the checksums and the spectrogram are taken from.

Set --duration to cover the last frame you want, then use --pad for listening room around it. --pad sets both ends at once; --pad-start and --pad-end override it when the two differ.

This step is manual. Before citing a time you have to listen to it, and the 0.93 that was a person shows why. A script that sent detections straight to soundcite would have built an evidence package for a shout.

Outcome

soundcite did its job on this recording: it turned a timestamp into a small package with enough information to trace the excerpt back to its sources. lowdom completed the analysis, and the provisional 0.95 threshold produced a useful listening split on this take. birdidpv then turned that split into a listening queue with names attached, and the names are where the caution has to move next.

The three tools now form a loop rather than a line. Three hours become 135 ranges worth hearing, those ranges become 128 frames worth checking, and the frames that hold up go back to soundcite as times worth citing. Each step cuts down what a person has to listen to. Only the first step is automatic: birdidpv reads the CSV lowdom writes. The step back to soundcite is manual, by choice. None of this removes the listening, and the 01:41:05 frame shows why.

Next, we need to report partial windows, repeat the listening test across other recordings, microphones, wind protection and environments, and run birdidpv across both tracks rather than one. Low-frequency screening remains more accurate than wind detection, and a machine identification remains a prompt to listen rather than a record.

References

  1. Kahl, S., Wood, C. M., Eibl, M., & Klinck, H. (2021). BirdNET: A deep learning solution for avian diversity monitoring. Ecological Informatics, 61, 101236.

  2. xeno-canto. Four Eurasian Bittern (Botaurus stellaris) recordings, used for the listening comparison in What came back: XC891071 (Christian Bøggild, Denmark), XC1000766 (Christian Bøggild, Denmark), XC832807 (Cedric Mroczko, Ukraine), XC741523 (Romuald Mikusek, Poland).

  3. iNaturalist. Eleven reference photographs, one per detected species, by Pavel Shukov, egorbirder, Joe Bourget, Tatyana Zarubo, Анна Голубева, Frans Vandewalle, Wei Li Jiang, zametnya, one anonymous, thegreatdodo and Frank Sengpiel. Licences and source URLs are in species-photos/credits.json.

  4. Wikidata. Taxon identifiers for the eleven species, matched on iNaturalist taxon ID and resolved to Avibase, eBird, GBIF, ITIS, IUCN, EOL and iNaturalist.