feat: While recording a QSO the count is clickable to start the recording again
This commit is contained in:
@@ -4237,6 +4237,18 @@ func (a *App) QSOAudioRestart() bool {
|
|||||||
return a.qsoRec.Active()
|
return a.qsoRec.Active()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QSOAudioResetClock restarts the in-progress recording from zero, dropping
|
||||||
|
// everything captured so far (pre-roll included). Lets the operator click the
|
||||||
|
// REC timer to record only their own exchange when the station was already in a
|
||||||
|
// long QSO before they entered the call. Returns whether a recording is active.
|
||||||
|
func (a *App) QSOAudioResetClock() bool {
|
||||||
|
if a.qsoRec == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
a.qsoRec.ResetQSOClock()
|
||||||
|
return a.qsoRec.Active()
|
||||||
|
}
|
||||||
|
|
||||||
// QSOAudioCancel drops the in-progress recording (callsign cleared, QSO
|
// QSOAudioCancel drops the in-progress recording (callsign cleared, QSO
|
||||||
// abandoned without logging).
|
// abandoned without logging).
|
||||||
func (a *App) QSOAudioCancel() {
|
func (a *App) QSOAudioCancel() {
|
||||||
|
|||||||
+15
-3
@@ -32,7 +32,7 @@ import {
|
|||||||
GetDVKMessages, GetDVKStatus, DVKPlay, DVKStop,
|
GetDVKMessages, GetDVKStatus, DVKPlay, DVKStop,
|
||||||
StartCWDecoder, StopCWDecoder, SetCWDecoderPitch,
|
StartCWDecoder, StopCWDecoder, SetCWDecoderPitch,
|
||||||
ChatAvailable, GetChatHistory, SendChatMessage, GetOnlineOperators,
|
ChatAvailable, GetChatHistory, SendChatMessage, GetOnlineOperators,
|
||||||
QSOAudioBegin, QSOAudioCancel, QSOAudioRestart,
|
QSOAudioBegin, QSOAudioCancel, QSOAudioRestart, QSOAudioResetClock,
|
||||||
GetAwardDefs,
|
GetAwardDefs,
|
||||||
GetUIPref,
|
GetUIPref,
|
||||||
ReportLiveActivity,
|
ReportLiveActivity,
|
||||||
@@ -507,6 +507,12 @@ export default function App() {
|
|||||||
if (forCall !== undefined) recordingCallRef.current = forCall.trim().toUpperCase();
|
if (forCall !== undefined) recordingCallRef.current = forCall.trim().toUpperCase();
|
||||||
QSOAudioRestart().then((active) => { setRecording(active); setRecTick((t) => t + 1); }).catch(() => {});
|
QSOAudioRestart().then((active) => { setRecording(active); setRecTick((t) => t + 1); }).catch(() => {});
|
||||||
};
|
};
|
||||||
|
// Reset the recording to zero (drop everything so far, pre-roll included) —
|
||||||
|
// bound to clicking the REC timer. Use when the station was already in a long
|
||||||
|
// QSO and you only want your own exchange in the file.
|
||||||
|
const resetRecordingClock = () => {
|
||||||
|
QSOAudioResetClock().then((active) => { setRecording(active); setRecTick((t) => t + 1); }).catch(() => {});
|
||||||
|
};
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
const [filterCallsign, setFilterCallsign] = useState('');
|
const [filterCallsign, setFilterCallsign] = useState('');
|
||||||
// Advanced filter builder (replaces the old band/mode dropdowns).
|
// Advanced filter builder (replaces the old band/mode dropdowns).
|
||||||
@@ -2247,10 +2253,16 @@ export default function App() {
|
|||||||
</Label>
|
</Label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
{recording && RECORDABLE_MODES.has(mode.toUpperCase()) && (
|
{recording && RECORDABLE_MODES.has(mode.toUpperCase()) && (
|
||||||
<span className="absolute right-2 top-1/2 -translate-y-1/2 z-10 inline-flex items-center gap-1 text-[10px] font-semibold tabular-nums text-red-600 whitespace-nowrap pointer-events-none">
|
<button
|
||||||
|
type="button"
|
||||||
|
onMouseDown={(e) => e.preventDefault()}
|
||||||
|
onClick={resetRecordingClock}
|
||||||
|
title="Click to restart the recording from 0 — drops everything captured so far (incl. pre-roll) so the file holds only your exchange"
|
||||||
|
className="absolute right-2 top-1/2 -translate-y-1/2 z-10 inline-flex items-center gap-1 text-[10px] font-semibold tabular-nums text-red-600 whitespace-nowrap cursor-pointer rounded px-1 hover:bg-red-50"
|
||||||
|
>
|
||||||
<span className="size-2 rounded-full bg-red-600 animate-pulse" />
|
<span className="size-2 rounded-full bg-red-600 animate-pulse" />
|
||||||
{String(Math.floor(recSeconds / 60)).padStart(2, '0')}:{String(recSeconds % 60).padStart(2, '0')}
|
{String(Math.floor(recSeconds / 60)).padStart(2, '0')}:{String(recSeconds % 60).padStart(2, '0')}
|
||||||
</span>
|
</button>
|
||||||
)}
|
)}
|
||||||
<Input
|
<Input
|
||||||
ref={callsignRef}
|
ref={callsignRef}
|
||||||
|
|||||||
Vendored
+2
@@ -429,6 +429,8 @@ export function QSOAudioBegin():Promise<boolean>;
|
|||||||
|
|
||||||
export function QSOAudioCancel():Promise<void>;
|
export function QSOAudioCancel():Promise<void>;
|
||||||
|
|
||||||
|
export function QSOAudioResetClock():Promise<boolean>;
|
||||||
|
|
||||||
export function QSOAudioRestart():Promise<boolean>;
|
export function QSOAudioRestart():Promise<boolean>;
|
||||||
|
|
||||||
export function QuitApp():Promise<void>;
|
export function QuitApp():Promise<void>;
|
||||||
|
|||||||
@@ -826,6 +826,10 @@ export function QSOAudioCancel() {
|
|||||||
return window['go']['main']['App']['QSOAudioCancel']();
|
return window['go']['main']['App']['QSOAudioCancel']();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function QSOAudioResetClock() {
|
||||||
|
return window['go']['main']['App']['QSOAudioResetClock']();
|
||||||
|
}
|
||||||
|
|
||||||
export function QSOAudioRestart() {
|
export function QSOAudioRestart() {
|
||||||
return window['go']['main']['App']['QSOAudioRestart']();
|
return window['go']['main']['App']['QSOAudioRestart']();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -239,6 +239,22 @@ func (r *Recorder) RestartQSO() {
|
|||||||
r.active = true
|
r.active = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResetQSOClock restarts the active accumulation from ZERO — discarding
|
||||||
|
// everything captured so far INCLUDING the pre-roll. Unlike RestartQSO (which
|
||||||
|
// re-seeds from the pre-roll ring), this keeps nothing: the saved file will
|
||||||
|
// contain only audio from this moment onward. Used when the contact you entered
|
||||||
|
// was already in a long QSO and you want to record just your own exchange.
|
||||||
|
// No-op if not running; if no take is active it begins one (empty).
|
||||||
|
func (r *Recorder) ResetQSOClock() {
|
||||||
|
r.mu.Lock()
|
||||||
|
defer r.mu.Unlock()
|
||||||
|
if !r.running {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.acc = nil
|
||||||
|
r.active = true
|
||||||
|
}
|
||||||
|
|
||||||
// TakeQSO snapshots the accumulated recording as raw 16 kHz mono PCM bytes and
|
// TakeQSO snapshots the accumulated recording as raw 16 kHz mono PCM bytes and
|
||||||
// stops accumulating — fast, no encoding. The next BeginQSO can safely start a
|
// stops accumulating — fast, no encoding. The next BeginQSO can safely start a
|
||||||
// new take immediately. Pair with WritePCM to encode/write off the hot path so
|
// new take immediately. Pair with WritePCM to encode/write off the hot path so
|
||||||
|
|||||||
Reference in New Issue
Block a user