Play a recording on phone, and record it in a window

A phone function key names a wav file instead of words, which is what N1MM's
own SSB file holds: `F1 CQ,{WAVDIR}\Cq.wav`. `{WAVDIR}` is the folder named
after whoever is at the microphone, a name is taken relative to `Wav`, and
commas play several files in a row. An SSB `.mc` file written for N1MM plays
here.

Window ▸ Voice Keyer lists every recording the messages name, says which of
them are there for the operator picked at the top, plays one, and records over
one. The recording is written beside the wanted file and moved over it at the
end, so a recorder that will not start takes nothing away.

`CommandSoundCard` runs `aplay` and `arecord`, and both commands are settings:
the audio path belongs to whatever the station runs, and a library would pick
one of them for the operator. A recorder is stopped with an interrupt so it
closes its wav file, and killed only if it ignores that. PTT is cwdaemon's
`<ESC>a` for as long as the recording plays, off for a station on VOX.

Two things follow. The QTC window sends on SSB: N1MM's four recordings on the
Ready, Agn, Cfm and Close buttons, with the lines still read out by the
operator. And alternating CQ duels on phone, because the voice keyer says when
a recording has finished — which it had to, since the CQ message is picked by
the radio's mode and would otherwise have been keyed as Morse.

Nothing here has been played on a radio: this machine has no sound card, so
what is tested is the program being run, the file filled in, the exit code and
the interrupt, with shell commands standing in for the two.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0157i7PiZyqBkquxS9A6FTdQ
This commit is contained in:
2026-09-11 07:51:58 +00:00
co-authored by Claude Opus 5
parent dae1f4f197
commit 082fa75a40
28 changed files with 1564 additions and 86 deletions
+13
View File
@@ -1,6 +1,7 @@
using System.Reflection;
using System.Text.Json;
using Nonemm.Core;
using Nonemm.Keying;
using Nonemm.Network;
using Nonemm.Session;
@@ -171,6 +172,18 @@ public sealed record Settings
/// 100 ms, because an SO2R box works relays.
public int AlternatingCqGapMs { get; init; } = 100;
/// What the voice keyer plays and records with. `{file}` in each command
/// stands for the file. Empty means the program has nothing to play or
/// record with, and the voice keyer window says so.
public string VoicePlayCommand { get; init; } = CommandSoundCard.DefaultPlay;
public string VoiceRecordCommand { get; init; } = CommandSoundCard.DefaultRecord;
/// Whether the voice keyer keys the transmitter itself, through the same
/// cwdaemon that keys CW. Turned off at a station whose radio hears the
/// audio and keys itself on VOX.
public bool VoiceKeysPtt { get; init; } = true;
/// How the WAE QTC window behaves. The names and the defaults are N1MM's:
/// it skips the confirm and ready steps out of the box, clears the header
/// when the header is asked for again, and leaves a line alone when the