Mutara

Audio, timers and levels

The node mixes all audio at 48 kHz in 20 ms frames. The radio filters also work at 48 kHz. Audio changes to 8 kHz only at the edge to AllStar and the digital modes. This page gives the audio path, the rules of the mixer, the listener encoders, the level targets and the timers.

The audio path #

All audio inside the node is 48 kHz, 16-bit, mono PCM, in frames of 20 ms (960 samples). The rate is not a setting. Each port changes the audio to its own format at its edge.

Figure 1. The audio path of a node. Only the bridges to AllStar and to the digital modes use 8 kHz.
Text version of figure 1
  • The radio and its sound card work at 48 kHz. The radio filters also work at 48 kHz, in both directions.
  • The mixer works at 48 kHz, in 20 ms frames.
  • AllStar links and IAX users get 8 kHz μ-law. The change to 8 kHz is at the edge of each link.
  • YSF, DMR and M17 modules send 8 kHz audio to the transcoder server and get 8 kHz audio back.
  • The mixer makes a mix for each listener without the listener's own voice (mix-minus). Listeners who hear the same mix share one Opus encode.
  • Browsers and apps get Opus with a band tag and in-band error correction (FEC). Their microphones send Opus to the node.

Mix rules #

For each port, the node mixes the audio of all other ports that may go to it. These rules apply:

  • A monitor or localmonitor link gets no audio.
  • A localmonitor link is heard on local ports (the radio) only.
  • A listen-only port is never a source.
  • A network-only source (no_rf, or a browser user without rf) stays off local ports.
  • A port hears itself only when it is local and the radio mode is duplex repeater (duplex: 2).
  • When the timeout timer stops a radio or user source, its audio stops on all ports. Link-to-link audio continues, but not to local ports.

Codecs #

PortCodec
AllStar link, IAX2 user8 kHz μ-law. A low-pass filter, flat to 3.4 kHz and at least 70 dB down from 4.6 kHz, changes the sample rate.
YSF, DMR and M17 module8 kHz PCM to and from the transcoder server, with the same filter.
Browser, iPhone, iPad and Android apps48 kHz Opus: VoIP, 20 ms, constant bit rate, with in-band FEC. The band tag sets the bit rate. Read Audio to listeners.
Browser over plain HTTP, or without WebCodecs8 kHz μ-law, band tag narrow
Radio48 kHz from and to the sound card. All radio filters work at 48 kHz. Read Radio filters.
Local link48 kHz PCM, no codec

The node encodes and decodes audio for browsers and apps outside the 20 ms cycle of the mixer.

Radio filters #

The radio filters do the same work as the filters of chan_simpleusb in app_rpt. They work at 48 kHz, with no change of sample rate. Each filter keeps the gain at 1 kHz of the older 8 kHz filters. Thus radio.rx_gain_db and radio.tx_gain_db keep their meaning.

Figure 2. The order of the receive and transmit filters. All stages work at 48 kHz. The emphasis and PL stages work only when they are on.
Text version of figure 2

Receive, from the sound card to the mixer:

  1. RX gain (radio.rx_gain_db).
  2. De-emphasis, when radio.deemphasis is on.
  3. PL high-pass at 300 Hz, when radio.pl_filter is on.
  4. Low-pass at radio.rx_lowpass_hz.

Transmit, from the mixer to the sound card:

  1. Band-pass from 300 Hz to radio.tx_lowpass_hz.
  2. Pre-emphasis, when radio.preemphasis is on.
  3. Limiter at −1 dBFS. It also clips.
  4. Low-pass at radio.tx_lowpass_hz again.
  5. TX gain (radio.tx_gain_db).
  • De-emphasis is a one-pole low-pass with its corner at 300 Hz. Use it with flat (discriminator) audio from the radio.
  • PL high-pass is a 6th-order Chebyshev filter at 300 Hz. It removes CTCSS tones from 67 to 254.1 Hz at least as well as the 8 kHz filter of chan_simpleusb.
  • Receive low-pass is a soft 4th-order Butterworth filter. It removes hiss above the voice band. Set it to 3400, 5000 or 7000 Hz. The default is 5000 Hz.
  • Transmit band-pass removes sub-audible audio, which can interfere with the CTCSS of the radio. It also removes the high band of browser and app microphones, which can make the signal wider.
  • Pre-emphasis is a shelf from 150 Hz to 10 kHz. From 300 Hz to 3 kHz it rises about 6 dB for each octave. Use it with a radio that expects flat audio.
  • Limiter is always on. It stops one loud source from pushing the deviation too high.
  • Transmit low-pass is a 4th-order Butterworth filter at radio.tx_lowpass_hz, 3000 or 5000 Hz. It removes the harmonics that the limiter makes. The default is 5000 Hz.

The FM signal stays a voice-band signal. If the deviation or the occupied bandwidth is too high, set radio.tx_lowpass_hz to 3000. A wider receive band carries more energy from the same signal. If received audio is now too loud, lower radio.rx_gain_db a little. Read Set the audio band.

Audio to listeners #

Browsers and apps get one Opus packet of 20 ms in each WebSocket message. Each stream has a band tag. The tag tells what band the audio has. It sets the bit rate and the widest band of the encoder.

The band tags
TagAudio bandOpus bit rateUsed by
narrowto 4 kHz12 kb/sAllStar links, IAX users, and the YSF, DMR and M17 modes; a radio with radio.rx_lowpass_hz 3400. μ-law browsers always get this tag. μ-law itself is 64 kb/s.
mediumto 6 kHz16 kb/sA radio with radio.rx_lowpass_hz 5000 (the default)
wideto 8 kHz24 kb/sBrowser and app microphones on a node; announcements, IDs and clips; OpusRef; a radio with radio.rx_lowpass_hz 7000; room audio in the scan
superto 12 kHz32 kb/sRoom audio to listeners; microphones in a room
fullto 20 kHz40 kb/sNot used by default

On a node, the band of a listener's stream follows the talkers. It is the widest band of the ports that talk in that listener's mix. The listener's own voice does not count. For example:

  • Only the radio talks: the band of the radio, medium by default.
  • Only an AllStar link or a digital mode talks: narrow.
  • A browser or an app talks, also together with the radio: wide.
  • An announcement, an ID or a clip plays: at least wide.
  • Only a courtesy tone plays, or no one talks: the band does not change.

A room is always super. In the scan, each node module has the band of its talkers, and a room has wide.

  • The node sends the tag as a text message on the audio socket, for example {"type":"band","band":"wide"}. It sends the message before the first frame of each talkspurt (after 200 ms with no audio) and when the band changes. The dashboard shows it, for example audio to 8 kHz. A client that does not know the message ignores it.
  • Each live encoder turns on in-band FEC for 10 % packet loss. Then a packet also carries a small copy of the frame before it. A listener who loses a packet can get most of that frame back. A decoder that does not use FEC decodes the stream as usual.
  • The bit rate is constant. The band and the bit rate do not change with the encoder level.

One encode for all listeners

The mixer makes a mix for each port without the port's own voice. All listeners who do not talk hear the same mix. Thus the node encodes each frame one time for each source (a node or a room) and each band. It sends the same packet to all these listeners. The cost of encoding does not grow with each listener who only listens.

  • A listener who talked in the last second uses its own encoder, because its mix does not have its own voice.
  • A frame that is different from the shared frame, or that comes late, also goes to the listener's own encoder.
  • The node has a shared encoder for each band, and each listener has its own encoder for each band that its stream had. Thus the packets of a listener's own encoder stay the same bytes as the shared packets, also after a change of band.
  • A shared encoder closes after 30 seconds with no listeners.
  • The shared packets are the same bytes that the listener's own encoder would make. Clients need no change.

Listener encoder levels #

The Opus encoders that send audio to browsers and apps use the most CPU of all audio work. On a Raspberry Pi 4, one encode at the full profile takes about 1.86 ms of each 20 ms frame, about 9 % of one core. On a slow board with listeners, the encoders can fall behind, and listeners hear stutter. The encoder level sets how much work each encoder does.

The encoder levels
LevelComplexityFECWhat it gives
45OnThe full profile, as OpusRef. The best sound.
33OnAbout half the CPU of level 4. A small loss of sound quality.
21OnAbout half the CPU of level 3.
10OnAbout half the CPU of level 2. The last level with FEC.
00OffThe least CPU. A lost packet is concealed, not recovered.

The level changes only the work of the encoder. The band and the bit rate stay the same, so a listener hears no gap when the level changes. FEC goes off last, because app listeners on a mobile network need it most. web.encoder_level is auto (the default) or a fixed level from 0 to 4. Clients do not need a change for any level.

What auto does

Figure 3. How auto sets the encoder level. A step up that fails makes the next try wait longer.
Text version of figure 3
  1. At the start, the node encodes 25 test frames at level 4 and picks the first level from the mean time.
  2. Each 2 seconds, the node measures the encoders and the CPU.
  3. If the encoders are too slow or the CPU is too busy, the level goes down one step.
  4. After 30 seconds of calm with listeners, the level goes up one step.
  5. After each change, the level holds for 10 seconds.
  6. If a step up comes down again within 10 minutes, the next step up waits twice as long, up to 30 minutes.
  • Start. The node encodes 25 test frames at level 4. A mean under 5 ms for each frame gives level 4. Under 8 ms gives level 3. Under 12 ms gives level 2. A slower board starts at level 1. On a Raspberry Pi 3 Model B, the test gave 6.4 to 6.8 ms, so the node started at level 3.
  • Window. Each 2 seconds, the node adds up the encode time of all encoders. On Linux, it also reads the CPU use of the whole system from /proc/stat.
  • Step down at once, one level, when one of these is true in a window:
    • Encoding took more than half of the whole CPU (all cores).
    • At least 3 frames, and at least 5 % of the frames, took more than 20 ms to encode.
    • The system CPU was above 90 %, and encoding took more than 5 % of the CPU.
    One slow frame does not lower the level, because each listener has a queue of 25 frames.
  • Step up one level after 30 seconds of calm with listeners. Calm is: at least 50 frames in each window, no frame over 20 ms, encoding under 12 % of the CPU, a mean frame under 5 ms, and the system CPU under 60 %.
  • Idle. When nobody listens, the level does not change.
  • Hold. After each change, the level holds for 10 seconds.
  • Back-off. A step up that comes down within 10 minutes doubles the calm time for the next try, up to 30 minutes. A step up that holds for 10 minutes sets the calm time back to 30 seconds. Thus a board at its limit does not go up and down all the time. A failed try costs only some CPU for a few seconds.

Read the Encoder stat

The top card of the dashboard shows Encoder, for example 3 of 4. Point to it to read the complexity, the FEC and the reason for the last change. The bottom of each page also shows the level, for example encoder 3 of 4 (auto). A fixed level shows no (auto).

The reasons for the last change in auto
ReasonWhat it means
the start benchmarkThe start test picked this level.
an admin set autoAn admin changed the setting to auto. The start test ran again.
encoding took most of the CPUThe encoders used more than half of the whole CPU.
the encoders did not keep upToo many frames took more than 20 ms to encode.
the CPU is busyThe system CPU was above 90 %.
the CPU has roomThe level went up after a calm time.

Each change also writes the log line listener encoder level, with the old and new level, the reason, and the numbers of the window: the CPU share of the encoders, the frames, the slow frames, the mean and longest frame, the system CPU and the time to the next step up. At the start, the line listener encoder gives the first level and the time of the test.

When to set a fixed level

Keep auto on most nodes. Set a fixed level in these cases:

  • Short stutters on a slow board. A Raspberry Pi 3 or a Pi Zero 2 W with listeners can be near its limit at the level that auto keeps. Set level 2. If the stutter continues, set level 1. In a test on a Pi 3 Model B with one browser listener, there was one very small stutter in 3 minutes at level 3, and no stutter at level 2.
  • A fast computer or a cloud server. Auto starts at level 4 there. Set level 4 to keep the full profile also when other work makes the CPU busy for a time. If the encoders then fall behind, listeners hear stutter, because a fixed level does not step down.
  • Level 0 is for a board that cannot keep up at level 1. Listeners on a weak connection lose the FEC.

To set the level, an admin opens Settings, clicks Access, and sets Listener encoder level under Listener audio. The change applies at once, with no restart. A change to auto runs the start test again. Read Listener audio.

How levels are measured #

Each port measures the active-speech average (RMS) of each transmission. Frames below −50 dBFS do not count. The meter uses the 8 kHz voice band with a 300 Hz high-pass filter. Thus CTCSS and bass do not count, and 48 kHz and 8 kHz sources read the same. The meter reads the audio before AGC.

The ratings of an average level
RatingAverage (dBFS)
Too quiet−59 to −39
Low−38 to −33
Good−32 to −21
High−20 to −18
Too loud−17 and higher

These are the bands of the AllStarLink parrot node. A rating comes from the average only. The peaks and the clipped samples show as numbers.

AGC and limiter #

  • AGC brings speech to −25 dBFS average, measured on the 8 kHz stream that the network gets, with its bass. The voice band above 300 Hz only tells speech from a pause, so a CTCSS tone or hum alone does not count. It is a slow leveler (attack 30 ms, release 800 ms) that adds at most 12 dB and holds below −50 dBFS. A 2:1 compressor above −15 dBFS stops loud starts.
  • AGC is always on for browser microphones. For the radio, radio.agc turns it on.
  • On a Mutara connection, the node levels the microphone of the apps. On an IAX2 connection, the app levels its own microphone, because an ASL node does not.
  • Voice boost. The dashboard and the apps can make the node audio louder for the listener only, from 0 to +20 dB, +10 dB by default. A 3 ms look-ahead peak limiter at −1 dBFS stops clipping. 0 dB changes nothing. The boost applies to live audio, the scan and clips. Nothing that goes to the node changes.
  • Listening buffer. The Android and iOS apps keep node audio in a playout buffer before it plays. In Auto, the target is the 99th percentile of the packet delay in the last 30 s, plus a margin, from 60 to 300 ms. The app keeps a target for Wi-Fi and one for cellular (80 and 150 ms at the start). A network stall makes the target bigger at once: the queue runs dry, then at least half of the late frames come in a bunch. When the frames come again at an even pace, the node paused (for example between speech and a courtesy tone); the app logs a pause, and the target does not change. The queue holds up to 3 s: when the network gives seconds of held audio at once, the app keeps all of it and catches up by removing silent frames and joining frames. After each transmission with no gaps, the target gets smaller by half of its excess over the measured delay (at least 10 ms), so a rare stall does not keep the delay high. To make the delay smaller, the app removes a silent frame, or joins two frames. A late frame gets Opus packet loss concealment; IAX2 audio fades out. The first frame plays at once, with no silence before it. In Manual, the buffer is fixed from 40 to 500 ms, and the app shows the value that Auto would use. At the end of each transmission, the app logs one line: buffer over: net=wifi mode=auto buffer=80 ms ... gaps=0 concealed=0 late=0 (Android tag Mutara, iOS NSLog). When a stall made the target bigger, the line ends with stalls= and how late the frames were. Android also writes the line to its audio focus log file, with the screen state. The dashboard logs the frames that it drops from a slow listener.
  • Limiter. Each AllStar μ-law stream goes through a 3 ms look-ahead limiter at −6 dBFS, with a release of 80 ms.
  • Tones and IDs play at telemetry_db, −18 dBFS peak. Speech prompts are 9 dB below that on average. No AGC changes them.

Keying #

  • The radio keys on COS or CTCSS from a HID input, or on VOX. PTT goes out on HID GPIO3, as chan_simpleusb does.
  • The node sets PTT on each HID poll, so a lost signal cannot leave the transmitter keyed.
  • The sound card clock drives the radio loop. Network audio waits in a 60 ms buffer. If the buffer is full, the oldest frame is dropped.
  • An AllStar link unkeys on RADIO_UNKEY or after a 500 ms gap.
  • A browser keys with the talk button. The node unkeys a browser after web.talk_limit, 90 seconds by default.

Timers #

TimerKeyDefaultWhat it does
Hang timenode.hang_time2sThe transmitter stays on after the last talker. Not in simplex quiet mode.
Key-up delaynode.keyup_delay400msSilence before a tone, ID or announcement, so that receivers open.
Timeoutnode.timeout3mStops a talker who transmits longer.
Timeout resetnode.timeout_reset2sThe pause that starts the timeout timer again.
ID intervalnode.id_interval10mThe station ID while the node is in use.
Ack delaynode.ack_delay400msFrom the end of a local transmission to the ack tone.
Ack hangnode.ack_hang0The transmitter stays on after the ack tone.
Link inactivityallstar.link_inactivity0 (off)Drops quiet links that are not permanent.

Tones #

The tone builder has 40 preset tones and your own tones in the app_rpt form |t(f1,f2,ms,amp). node.courtesy_sound is the tone after network audio, and node.ack_sound is the tone after local audio.

Recorded clips #

A clip starts when someone talks and ends when they stop or a different talker starts. The node records the audio of talkers before it adds its own sounds: the radio, links, IAX2 users, browsers and reflectors. The ID, announcements, tones, voice messages and parrot are never in a clip. Clips are Ogg Opus (RFC 7845, mono, 20 kb/s). One hour of talk uses about 9 MB.