Synchronize connection and transmission state

This commit is contained in:
Brandon McGinty (chatgpt)
2026-08-10 00:45:22 -04:00
committed by Brandon McGinty
parent 3efbd019ce
commit 55772fa459
4 changed files with 64 additions and 23 deletions
+28 -3
View File
@@ -31,6 +31,7 @@ type Barnard struct {
Tx bool Tx bool
AutoTransmit bool // auto-start transmission on connect AutoTransmit bool // auto-start transmission on connect
Connected bool Connected bool
stateMutex sync.RWMutex
Ui *uiterm.Ui Ui *uiterm.Ui
UiOutput uiterm.Textview UiOutput uiterm.Textview
@@ -149,10 +150,34 @@ func (b *Barnard) setSelectedUserValue(user *gumble.User) {
b.selectedUserMutex.Unlock() b.selectedUserMutex.Unlock()
} }
func (b *Barnard) isTransmitting() bool {
b.stateMutex.RLock()
defer b.stateMutex.RUnlock()
return b.Tx
}
func (b *Barnard) setTransmitting(transmitting bool) {
b.stateMutex.Lock()
b.Tx = transmitting
b.stateMutex.Unlock()
}
func (b *Barnard) isConnected() bool {
b.stateMutex.RLock()
defer b.stateMutex.RUnlock()
return b.Connected
}
func (b *Barnard) setConnected(connected bool) {
b.stateMutex.Lock()
b.Connected = connected
b.stateMutex.Unlock()
}
func (b *Barnard) StopTransmission() { func (b *Barnard) StopTransmission() {
if b.Tx { if b.isTransmitting() {
b.Notify("micdown", "me", "") b.Notify("micdown", "me", "")
b.Tx = false b.setTransmitting(false)
b.UpdateGeneralStatus(" Idle ", false) b.UpdateGeneralStatus(" Idle ", false)
if b.ToneTest { if b.ToneTest {
// Stop the tone generator. // Stop the tone generator.
@@ -209,7 +234,7 @@ func (b *Barnard) TreeItemKeyPress(ui *uiterm.Ui, tree *uiterm.Tree, item uiterm
// Update channel mute state // Update channel mute state
b.setChannelMuted(treeItem.Channel.ID, channelWillBeMuted) b.setChannelMuted(treeItem.Channel.ID, channelWillBeMuted)
if channelWillBeMuted && b.Client.Self.Channel.ID == treeItem.Channel.ID && b.Tx { if channelWillBeMuted && b.Client.Self.Channel.ID == treeItem.Channel.ID && b.isTransmitting() {
b.StopTransmission() b.StopTransmission()
} }
+7 -7
View File
@@ -70,11 +70,11 @@ func (b *Barnard) connect(reconnect bool) bool {
b.toneTestSaver = saver b.toneTestSaver = saver
b.toneTestSaverDetach = b.Client.Config.AttachAudio(saver) b.toneTestSaverDetach = b.Client.Config.AttachAudio(saver)
b.Connected = true b.setConnected(true)
if b.toneTestAutoTransmit() { if b.toneTestAutoTransmit() {
b.toneTestStop = make(chan struct{}) b.toneTestStop = make(chan struct{})
go StartToneGenerator(b.Client, b.toneTestStop) go StartToneGenerator(b.Client, b.toneTestStop)
b.Tx = true b.setTransmitting(true)
b.UpdateGeneralStatus(" Tx ", true) b.UpdateGeneralStatus(" Tx ", true)
b.AddOutputLine("Tone test transmission started") b.AddOutputLine("Tone test transmission started")
} }
@@ -112,7 +112,7 @@ func (b *Barnard) connect(reconnect bool) bool {
b.Stream.SetFilePlayer(b.FileStream) b.Stream.SetFilePlayer(b.FileStream)
b.FileStreamMutex.Unlock() b.FileStreamMutex.Unlock()
b.Connected = true b.setConnected(true)
// Dial delivers OnConnect before connect creates the OpenAL stream, so // Dial delivers OnConnect before connect creates the OpenAL stream, so
// start auto-transmit here as well for initial connections and reconnects. // start auto-transmit here as well for initial connections and reconnects.
b.startAutoTransmit() b.startAutoTransmit()
@@ -165,14 +165,14 @@ func (b *Barnard) OnConnect(e *gumble.ConnectEvent) {
} }
func (b *Barnard) startAutoTransmit() { func (b *Barnard) startAutoTransmit() {
if !b.AutoTransmit || b.Stream == nil || b.Tx { if !b.AutoTransmit || b.Stream == nil || b.isTransmitting() {
return return
} }
if err := b.Stream.StartSource(b.UserConfig.GetInputDevice()); err != nil { if err := b.Stream.StartSource(b.UserConfig.GetInputDevice()); err != nil {
b.AddOutputLine(fmt.Sprintf("auto-transmit failed: %s", err.Error())) b.AddOutputLine(fmt.Sprintf("auto-transmit failed: %s", err.Error()))
return return
} }
b.Tx = true b.setTransmitting(true)
b.UpdateGeneralStatus(" AutoTx ", true) b.UpdateGeneralStatus(" AutoTx ", true)
b.AddOutputLine("Auto-transmit started") b.AddOutputLine("Auto-transmit started")
} }
@@ -208,8 +208,8 @@ func (b *Barnard) OnDisconnect(e *gumble.DisconnectEvent) {
} else { } else {
b.AddOutputLine("Disconnected: " + reason) b.AddOutputLine("Disconnected: " + reason)
} }
b.Tx = false b.setTransmitting(false)
b.Connected = false b.setConnected(false)
b.postUI(func() { b.postUI(func() {
b.UiTree.Rebuild() b.UiTree.Rebuild()
b.Ui.Refresh() b.Ui.Refresh()
+16
View File
@@ -84,6 +84,22 @@ func TestServerAddressDefaultsPortWithoutBreakingIPv6(t *testing.T) {
} }
} }
func TestConcurrentConnectionStateAccess(t *testing.T) {
b := &Barnard{}
var wg sync.WaitGroup
for i := 0; i < 20; i++ {
wg.Add(1)
go func(value bool) {
defer wg.Done()
b.setConnected(value)
b.setTransmitting(value)
_ = b.isConnected()
_ = b.isTransmitting()
}(i%2 == 0)
}
wg.Wait()
}
func TestConcurrentSelectedUserAccess(t *testing.T) { func TestConcurrentSelectedUserAccess(t *testing.T) {
b := &Barnard{} b := &Barnard{}
user := &gumble.User{Session: 1} user := &gumble.User{Session: 1}
+13 -13
View File
@@ -236,7 +236,7 @@ func (b *Barnard) CommandPlayFile(ui *uiterm.Ui, cmd string) {
} }
} }
if !b.Connected { if !b.isConnected() {
b.AddOutputLine("Not connected to server") b.AddOutputLine("Not connected to server")
return return
} }
@@ -267,7 +267,7 @@ func (b *Barnard) CommandPlayFile(ui *uiterm.Ui, cmd string) {
b.Client.EnableStereoEncoder() b.Client.EnableStereoEncoder()
// Auto-start transmission if not already transmitting // Auto-start transmission if not already transmitting
if !b.Tx && b.Stream != nil { if !b.isTransmitting() && b.Stream != nil {
err := b.Stream.StartSource(b.UserConfig.GetInputDevice()) err := b.Stream.StartSource(b.UserConfig.GetInputDevice())
if err != nil { if err != nil {
b.AddOutputLine(fmt.Sprintf("Error starting transmission: %s", err.Error())) b.AddOutputLine(fmt.Sprintf("Error starting transmission: %s", err.Error()))
@@ -275,7 +275,7 @@ func (b *Barnard) CommandPlayFile(ui *uiterm.Ui, cmd string) {
b.Client.DisableStereoEncoder() b.Client.DisableStereoEncoder()
return return
} }
b.Tx = true b.setTransmitting(true)
b.UpdateGeneralStatus(" File ", true) b.UpdateGeneralStatus(" File ", true)
} }
@@ -312,15 +312,15 @@ func (b *Barnard) CommandStopFile(ui *uiterm.Ui, cmd string) {
} }
func (b *Barnard) setTransmit(ui *uiterm.Ui, val int) { func (b *Barnard) setTransmit(ui *uiterm.Ui, val int) {
if b.Tx && val == 1 { if b.isTransmitting() && val == 1 {
return return
} }
if b.Tx == false && val == 0 { if !b.isTransmitting() && val == 0 {
return return
} }
if b.Tx { if b.isTransmitting() {
b.Notify("micdown", "me", "") b.Notify("micdown", "me", "")
b.Tx = false b.setTransmitting(false)
b.UpdateGeneralStatus(" Idle ", false) b.UpdateGeneralStatus(" Idle ", false)
if b.ToneTest { if b.ToneTest {
if b.toneTestStop != nil { if b.toneTestStop != nil {
@@ -330,17 +330,17 @@ func (b *Barnard) setTransmit(ui *uiterm.Ui, val int) {
} else if b.Stream != nil { } else if b.Stream != nil {
b.Stream.StopSource() b.Stream.StopSource()
} }
} else if b.Connected == false { } else if !b.isConnected() {
b.Notify("error", "me", "no tx while disconnected") b.Notify("error", "me", "no tx while disconnected")
b.Tx = false b.setTransmitting(false)
b.UpdateGeneralStatus("no tx while disconnected", true) b.UpdateGeneralStatus("no tx while disconnected", true)
} else if b.isChannelMuted(b.Client.Self.Channel.ID) { } else if b.isChannelMuted(b.Client.Self.Channel.ID) {
// Check if current channel is muted // Check if current channel is muted
b.Notify("error", "me", "cannot transmit in muted channel") b.Notify("error", "me", "cannot transmit in muted channel")
b.Tx = false b.setTransmitting(false)
b.UpdateGeneralStatus("cannot transmit in muted channel", true) b.UpdateGeneralStatus("cannot transmit in muted channel", true)
} else { } else {
b.Tx = true b.setTransmitting(true)
if b.ToneTest { if b.ToneTest {
b.toneTestStop = make(chan struct{}) b.toneTestStop = make(chan struct{})
go StartToneGenerator(b.Client, b.toneTestStop) go StartToneGenerator(b.Client, b.toneTestStop)
@@ -349,7 +349,7 @@ func (b *Barnard) setTransmit(ui *uiterm.Ui, val int) {
} else { } else {
err := b.Stream.StartSource(b.UserConfig.GetInputDevice()) err := b.Stream.StartSource(b.UserConfig.GetInputDevice())
if err != nil { if err != nil {
b.Tx = false b.setTransmitting(false)
if fatalAudioOpenError(err) { if fatalAudioOpenError(err) {
// A missing capture device cannot recover through normal // A missing capture device cannot recover through normal
// transmission controls; exit so option 1 reports it on stderr. // transmission controls; exit so option 1 reports it on stderr.
@@ -405,7 +405,7 @@ func (b *Barnard) CommandExit(ui *uiterm.Ui, cmd string) {
} }
func (b *Barnard) CommandStatus(ui *uiterm.Ui, cmd string) { func (b *Barnard) CommandStatus(ui *uiterm.Ui, cmd string) {
if b.Tx { if b.isTransmitting() {
b.Notify("status", "me", "transmitting") b.Notify("status", "me", "transmitting")
} else { } else {
b.Notify("status", "me", "not transmitting") b.Notify("status", "me", "not transmitting")