fix(icom): only forward-moving audio packets reach the sink
Every audio packet was delivered as if it were the next 20 ms, duplicates and late retransmits included. The monitor's capped ring quietly threw the surplus away, so the speakers sounded fine — but the recorder keeps every sample it is given, and the file came out longer than the QSO, slowed and stuttering, each lost-then-resent packet heard twice. trackRxSeq now says whether a packet advances the stream, and only those are handed on.
This commit is contained in:
+4
-2
@@ -16,7 +16,8 @@
|
|||||||
"Icom network audio: toggling Listening off and on no longer chops the sound — the monitor restarts as network-fed instead of also opening a USB capture.",
|
"Icom network audio: toggling Listening off and on no longer chops the sound — the monitor restarts as network-fed instead of also opening a USB capture.",
|
||||||
"QSO recorder: starting a fresh manual take resets the counter for good — it used to flash 0 and jump back to the previous take’s elapsed time.",
|
"QSO recorder: starting a fresh manual take resets the counter for good — it used to flash 0 and jump back to the previous take’s elapsed time.",
|
||||||
"MY_RIG follows the radio actually connected: the entry form now asks the active radio first, before the per-band default — an IC-7760 on the air no longer logs as the Flex the band plan names.",
|
"MY_RIG follows the radio actually connected: the entry form now asks the active radio first, before the per-band default — an IC-7760 on the air no longer logs as the Flex the band plan names.",
|
||||||
"QSO recorder: a manual take on a network Icom records the network RX stream, not the “From radio” sound card (which captured another radio’s DAX on a mixed station)."
|
"QSO recorder: a manual take on a network Icom records the network RX stream, not the “From radio” sound card (which captured another radio’s DAX on a mixed station).",
|
||||||
|
"Icom network audio: duplicate and late-retransmitted packets no longer reach the recorder — recordings came out longer than the QSO, slowed and stuttering, while the speakers played fine."
|
||||||
],
|
],
|
||||||
"fr": [
|
"fr": [
|
||||||
"Console Elecraft : le S-mètre est calibré sur un vrai K3 — S9 et les +dB correspondent désormais à l’affichage de la radio (il lisait environ deux points S trop bas).",
|
"Console Elecraft : le S-mètre est calibré sur un vrai K3 — S9 et les +dB correspondent désormais à l’affichage de la radio (il lisait environ deux points S trop bas).",
|
||||||
@@ -32,7 +33,8 @@
|
|||||||
"Audio réseau Icom : couper puis relancer Listening ne hache plus le son — le moniteur redémarre alimenté par le réseau au lieu d’ouvrir en plus une capture USB.",
|
"Audio réseau Icom : couper puis relancer Listening ne hache plus le son — le moniteur redémarre alimenté par le réseau au lieu d’ouvrir en plus une capture USB.",
|
||||||
"Enregistreur de QSO : démarrer une nouvelle prise manuelle remet le compteur à zéro pour de bon — il affichait 0 puis resautait au temps de la prise précédente.",
|
"Enregistreur de QSO : démarrer une nouvelle prise manuelle remet le compteur à zéro pour de bon — il affichait 0 puis resautait au temps de la prise précédente.",
|
||||||
"MY_RIG suit la radio réellement connectée : le formulaire interroge d’abord la radio active, avant le défaut par bande — un IC-7760 à l’antenne ne se logue plus comme le Flex prévu par le plan de bande.",
|
"MY_RIG suit la radio réellement connectée : le formulaire interroge d’abord la radio active, avant le défaut par bande — un IC-7760 à l’antenne ne se logue plus comme le Flex prévu par le plan de bande.",
|
||||||
"Enregistreur de QSO : une prise manuelle sur un Icom réseau enregistre le flux RX réseau, pas la carte son « From Radio » (qui capturait le DAX d’une autre radio sur une station mixte)."
|
"Enregistreur de QSO : une prise manuelle sur un Icom réseau enregistre le flux RX réseau, pas la carte son « From Radio » (qui capturait le DAX d’une autre radio sur une station mixte).",
|
||||||
|
"Audio réseau Icom : les paquets dupliqués ou retransmis en retard n’atteignent plus l’enregistreur — les enregistrements sortaient plus longs que le QSO, ralentis et hachés, alors que les haut-parleurs jouaient bien."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -134,12 +134,19 @@ func (a *icomAudio) audioPump() {
|
|||||||
case typ == 0x05: // rig-initiated disconnect
|
case typ == 0x05: // rig-initiated disconnect
|
||||||
debugLog.Printf("icom audio: rig sent DISCONNECT — audio stream dropped by the rig")
|
debugLog.Printf("icom audio: rig sent DISCONNECT — audio stream dropped by the rig")
|
||||||
case typ == 0x00 && k > icaAudioOffset: // audio data packet
|
case typ == 0x00 && k > icaAudioOffset: // audio data packet
|
||||||
a.trackRxSeq(icnLE.Uint16(buf[6:]))
|
fresh := a.trackRxSeq(icnLE.Uint16(buf[6:]))
|
||||||
if a.dumped < icaDumpFirst {
|
if a.dumped < icaDumpFirst {
|
||||||
a.dumped++
|
a.dumped++
|
||||||
debugLog.Printf("icom audio raw #%d: len=%d head=% X", a.dumped, k, buf[:min(icaAudioOffset+8, k)])
|
debugLog.Printf("icom audio raw #%d: len=%d head=% X", a.dumped, k, buf[:min(icaAudioOffset+8, k)])
|
||||||
}
|
}
|
||||||
if a.sink != nil {
|
// Only a packet that ADVANCES the sequence reaches the sink. A
|
||||||
|
// duplicate or a late retransmit used to be delivered as if it
|
||||||
|
// were the next 20 ms of audio: the monitor's capped ring threw
|
||||||
|
// the surplus away (the speakers stayed clean), but the recorder
|
||||||
|
// keeps every sample it is given — the file grew longer than the
|
||||||
|
// QSO and played back slowed and stuttering, each lost-then-
|
||||||
|
// resent packet heard twice.
|
||||||
|
if fresh && a.sink != nil {
|
||||||
payload := append([]byte(nil), buf[icaAudioOffset:k]...)
|
payload := append([]byte(nil), buf[icaAudioOffset:k]...)
|
||||||
a.sink(payload)
|
a.sink(payload)
|
||||||
}
|
}
|
||||||
@@ -159,26 +166,36 @@ func (a *icomAudio) audioPump() {
|
|||||||
// trackRxSeq / sendRetransmitReq mirror icomNet's receive-side retransmit exactly
|
// trackRxSeq / sendRetransmitReq mirror icomNet's receive-side retransmit exactly
|
||||||
// (audio is as loss-sensitive as the scope stream). Duplicated deliberately so
|
// (audio is as loss-sensitive as the scope stream). Duplicated deliberately so
|
||||||
// the audio stream owns its own seq state with no shared locking.
|
// the audio stream owns its own seq state with no shared locking.
|
||||||
func (a *icomAudio) trackRxSeq(seq uint16) {
|
//
|
||||||
|
// The return value says whether this packet moves the stream FORWARD — the
|
||||||
|
// only kind the sink may hear. A duplicate is the same 20 ms again; a late
|
||||||
|
// retransmit would play old audio in the middle of new. Both are accounted
|
||||||
|
// for here and dropped by the caller.
|
||||||
|
func (a *icomAudio) trackRxSeq(seq uint16) bool {
|
||||||
if !a.rxHaveSeq {
|
if !a.rxHaveSeq {
|
||||||
a.rxHaveSeq = true
|
a.rxHaveSeq = true
|
||||||
a.rxLastSeq = seq
|
a.rxLastSeq = seq
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
switch d := int16(seq - a.rxLastSeq); {
|
switch d := int16(seq - a.rxLastSeq); {
|
||||||
case d == 0:
|
case d == 0:
|
||||||
|
return false
|
||||||
case d < 0:
|
case d < 0:
|
||||||
delete(a.rxMissing, seq)
|
delete(a.rxMissing, seq)
|
||||||
|
return false
|
||||||
case d == 1:
|
case d == 1:
|
||||||
a.rxLastSeq = seq
|
a.rxLastSeq = seq
|
||||||
|
return true
|
||||||
case int(d) <= icnMaxMissing:
|
case int(d) <= icnMaxMissing:
|
||||||
for f := a.rxLastSeq + 1; f != seq; f++ {
|
for f := a.rxLastSeq + 1; f != seq; f++ {
|
||||||
a.rxMissing[f] = 0
|
a.rxMissing[f] = 0
|
||||||
}
|
}
|
||||||
a.rxLastSeq = seq
|
a.rxLastSeq = seq
|
||||||
|
return true
|
||||||
default:
|
default:
|
||||||
a.rxMissing = make(map[uint16]int)
|
a.rxMissing = make(map[uint16]int)
|
||||||
a.rxLastSeq = seq
|
a.rxLastSeq = seq
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user