Record a message with Ctrl+Shift+F, and open the editor with Alt+K
N1MM records a phone message from the entry window: Ctrl+Shift+F1 to Ctrl+Shift+F12 records into the file that key plays, and the same pair again stops and saves it. Escape and sending anything stop one too — there is one microphone, and it cannot be recorded and transmitted at the same time. The status line says what is being recorded and what was saved, which is where N1MM says it. The recording being made is the session's rather than the window's, for the same reason: the voice keyer window and these keys record into the same microphone, and now each can see what the other started. Alt+K opens the function key editor for the mode the radio is on, which is N1MM's shortcut for what right-clicking a key button already did. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0157i7PiZyqBkquxS9A6FTdQ
This commit is contained in:
@@ -21,11 +21,6 @@ public sealed partial class VoiceKeyerWindow : RefreshableWindow
|
||||
{
|
||||
private readonly AppSession session;
|
||||
|
||||
/// The line being recorded, or null while nothing is. One at a time: two
|
||||
/// recorders on one microphone would each get half of what was said.
|
||||
private CancellationTokenSource? recording;
|
||||
private string? recordingName;
|
||||
|
||||
public VoiceKeyerWindow(AppSession session)
|
||||
{
|
||||
this.session = session;
|
||||
@@ -115,12 +110,15 @@ public sealed partial class VoiceKeyerWindow : RefreshableWindow
|
||||
{
|
||||
Button play = new() { Content = "Play", IsEnabled = has, Margin = new Thickness(0, 1, 4, 1) };
|
||||
play.Click += (_, _) => _ = PlayAsync(line);
|
||||
bool isRecording = recordingName == line.Name;
|
||||
bool isRecording = session.Recording == line.Name;
|
||||
Button record = new()
|
||||
{
|
||||
Content = isRecording ? "Stop" : has ? "Record again" : "Record",
|
||||
// one microphone, so the other rows wait for this one to finish
|
||||
IsEnabled = recording is null || isRecording,
|
||||
// one microphone, so the other rows wait for this one to finish.
|
||||
// The entry window's Ctrl+Shift+F keys record into the same one,
|
||||
// which is why the state is the session's rather than this
|
||||
// window's
|
||||
IsEnabled = session.Recording is null || isRecording,
|
||||
Margin = new Thickness(0, 1, 0, 1),
|
||||
};
|
||||
record.Click += (_, _) => _ = RecordAsync(line);
|
||||
@@ -150,9 +148,9 @@ public sealed partial class VoiceKeyerWindow : RefreshableWindow
|
||||
/// with it, so a stop that fails leaves the old recording playable.
|
||||
private async Task RecordAsync(VoiceLine line)
|
||||
{
|
||||
if (recording is { } running)
|
||||
if (session.Recording is not null)
|
||||
{
|
||||
running.Cancel();
|
||||
session.StopRecording();
|
||||
return;
|
||||
}
|
||||
if (!session.Sound.CanRecord)
|
||||
@@ -160,28 +158,8 @@ public sealed partial class VoiceKeyerWindow : RefreshableWindow
|
||||
Status("there is no recorder — name one in the box below");
|
||||
return;
|
||||
}
|
||||
recording = new CancellationTokenSource();
|
||||
recordingName = line.Name;
|
||||
Refresh();
|
||||
Status($"recording {line.Name} — press Stop when you have finished speaking");
|
||||
string outcome;
|
||||
try
|
||||
{
|
||||
await session.Sound.RecordAsync(
|
||||
session.Recordings.PathOf(line.Name),
|
||||
recording.Token);
|
||||
outcome = $"recorded {line.Name}";
|
||||
}
|
||||
catch (Exception failure) when (failure is InvalidOperationException or IOException)
|
||||
{
|
||||
outcome = failure.Message;
|
||||
}
|
||||
finally
|
||||
{
|
||||
recording.Dispose();
|
||||
recording = null;
|
||||
recordingName = null;
|
||||
}
|
||||
string outcome = await session.RecordAsync(line.Name);
|
||||
// the redraw comes first: it writes the tally over the status line, and
|
||||
// what happened to this recording is worth more than the tally
|
||||
Refresh();
|
||||
|
||||
Reference in New Issue
Block a user