Synchronize connection-owned stream access
This commit is contained in:
committed by
Brandon McGinty
parent
f0aa5ff35a
commit
e9ba7f580a
+17
-9
@@ -97,6 +97,14 @@ type Barnard struct {
|
|||||||
// reconnect replaces them. It is intentionally idempotent for repeated
|
// reconnect replaces them. It is intentionally idempotent for repeated
|
||||||
// disconnect notifications.
|
// disconnect notifications.
|
||||||
func (b *Barnard) cleanupConnectionAudio() {
|
func (b *Barnard) cleanupConnectionAudio() {
|
||||||
|
// Connection audio operations that use both resources take FileStreamMutex
|
||||||
|
// before connectionMutex, so cleanup follows that order as well.
|
||||||
|
b.FileStreamMutex.Lock()
|
||||||
|
if b.FileStream != nil {
|
||||||
|
_ = b.FileStream.Stop()
|
||||||
|
b.FileStream = nil
|
||||||
|
}
|
||||||
|
b.FileStreamMutex.Unlock()
|
||||||
b.connectionMutex.Lock()
|
b.connectionMutex.Lock()
|
||||||
if b.Stream != nil {
|
if b.Stream != nil {
|
||||||
stream := b.Stream
|
stream := b.Stream
|
||||||
@@ -104,12 +112,6 @@ func (b *Barnard) cleanupConnectionAudio() {
|
|||||||
stream.Destroy()
|
stream.Destroy()
|
||||||
}
|
}
|
||||||
b.connectionMutex.Unlock()
|
b.connectionMutex.Unlock()
|
||||||
b.FileStreamMutex.Lock()
|
|
||||||
if b.FileStream != nil {
|
|
||||||
_ = b.FileStream.Stop()
|
|
||||||
b.FileStream = nil
|
|
||||||
}
|
|
||||||
b.FileStreamMutex.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Barnard) cleanupToneTestAudio() {
|
func (b *Barnard) cleanupToneTestAudio() {
|
||||||
@@ -266,9 +268,13 @@ func (b *Barnard) TreeItemKeyPress(ui *uiterm.Ui, tree *uiterm.Tree, item uiterm
|
|||||||
for _, u := range users {
|
for _, u := range users {
|
||||||
// Explicitly set user mute state to match channel state
|
// Explicitly set user mute state to match channel state
|
||||||
if channelWillBeMuted && !u.LocallyMuted() {
|
if channelWillBeMuted && !u.LocallyMuted() {
|
||||||
b.UserConfig.ToggleMute(u)
|
if err := b.UserConfig.ToggleMute(u); err != nil {
|
||||||
|
b.AddOutputLine("Mute: could not save setting: " + err.Error())
|
||||||
|
}
|
||||||
} else if !channelWillBeMuted && u.LocallyMuted() {
|
} else if !channelWillBeMuted && u.LocallyMuted() {
|
||||||
b.UserConfig.ToggleMute(u)
|
if err := b.UserConfig.ToggleMute(u); err != nil {
|
||||||
|
b.AddOutputLine("Mute: could not save setting: " + err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
b.updateUserGain(u)
|
b.updateUserGain(u)
|
||||||
@@ -297,7 +303,9 @@ func (b *Barnard) TreeItemKeyPress(ui *uiterm.Ui, tree *uiterm.Tree, item uiterm
|
|||||||
if treeItem.User != nil {
|
if treeItem.User != nil {
|
||||||
if key == *b.Hotkeys.MuteToggle {
|
if key == *b.Hotkeys.MuteToggle {
|
||||||
// Toggle mute for single user
|
// Toggle mute for single user
|
||||||
b.UserConfig.ToggleMute(treeItem.User)
|
if err := b.UserConfig.ToggleMute(treeItem.User); err != nil {
|
||||||
|
b.AddOutputLine("Mute: could not save setting: " + err.Error())
|
||||||
|
}
|
||||||
b.updateUserGain(treeItem.User)
|
b.updateUserGain(treeItem.User)
|
||||||
b.RebuildUserChannelTreePreservingSelection()
|
b.RebuildUserChannelTreePreservingSelection()
|
||||||
b.Ui.Refresh()
|
b.Ui.Refresh()
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ func (b *Barnard) exitWithError(err error) {
|
|||||||
|
|
||||||
func (b *Barnard) connect(reconnect bool) bool {
|
func (b *Barnard) connect(reconnect bool) bool {
|
||||||
var err error
|
var err error
|
||||||
_, err = gumble.DialWithDialer(new(net.Dialer), b.Config, &b.TLSConfig)
|
_, err = gumble.DialWithDialer(&net.Dialer{Timeout: 15 * time.Second}, b.Config, &b.TLSConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if reconnect {
|
if reconnect {
|
||||||
b.Log(err.Error())
|
b.Log(err.Error())
|
||||||
@@ -295,11 +295,11 @@ func (b *Barnard) OnUserChange(e *gumble.UserChangeEvent) {
|
|||||||
if b.isChannelMuted(e.User.Channel.ID) {
|
if b.isChannelMuted(e.User.Channel.ID) {
|
||||||
// Only mute if not already muted
|
// Only mute if not already muted
|
||||||
if !e.User.LocallyMuted() {
|
if !e.User.LocallyMuted() {
|
||||||
b.UserConfig.ToggleMute(e.User)
|
if err := b.UserConfig.ToggleMute(e.User); err != nil {
|
||||||
}
|
b.AddOutputLine("Mute: could not save setting: " + err.Error())
|
||||||
if b.Stream != nil {
|
}
|
||||||
b.Stream.UpdateUserGain(e.User)
|
|
||||||
}
|
}
|
||||||
|
b.updateUserGain(e.User)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-1
@@ -116,10 +116,28 @@ func DialWithDialer(dialer *net.Dialer, config *Config, tlsConfig *tls.Config) (
|
|||||||
}
|
}
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
conn, err := tls.DialWithDialer(dialer, "tcp", config.Address, tlsConfig)
|
rawConn, err := dialer.Dial("tcp", config.Address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
conn := tls.Client(rawConn, tlsConfig)
|
||||||
|
// net.Dialer.Timeout covers only the TCP dial. Apply the same bounded
|
||||||
|
// deadline to TLS negotiation so a peer that accepts but never responds
|
||||||
|
// cannot block startup indefinitely.
|
||||||
|
if dialer.Timeout > 0 {
|
||||||
|
if err := conn.SetDeadline(start.Add(dialer.Timeout)); err != nil {
|
||||||
|
rawConn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := conn.Handshake(); err != nil {
|
||||||
|
rawConn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := conn.SetDeadline(time.Time{}); err != nil {
|
||||||
|
rawConn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
client := &Client{
|
client := &Client{
|
||||||
Conn: NewConn(conn),
|
Conn: NewConn(conn),
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.stormux.org/storm/barnard/gumble/gumble"
|
"git.stormux.org/storm/barnard/gumble/gumble"
|
||||||
|
"git.stormux.org/storm/barnard/gumble/gumbleopenal"
|
||||||
"git.stormux.org/storm/barnard/recording"
|
"git.stormux.org/storm/barnard/recording"
|
||||||
"git.stormux.org/storm/barnard/uiterm"
|
"git.stormux.org/storm/barnard/uiterm"
|
||||||
)
|
)
|
||||||
@@ -179,10 +180,10 @@ func (b *Barnard) finishRecordingStart() {
|
|||||||
}
|
}
|
||||||
b.Recorder = recorder
|
b.Recorder = recorder
|
||||||
b.recordingStarting = false
|
b.recordingStarting = false
|
||||||
|
// Recorder operations take RecordingMutex before connectionMutex. This
|
||||||
|
// prevents disconnect cleanup from destroying a stream during attachment.
|
||||||
|
b.withStream(func(stream *gumbleopenal.Stream) { stream.SetRecorder(recorder) })
|
||||||
b.RecordingMutex.Unlock()
|
b.RecordingMutex.Unlock()
|
||||||
if b.Stream != nil {
|
|
||||||
b.Stream.SetRecorder(recorder)
|
|
||||||
}
|
|
||||||
b.AddOutputLine(fmt.Sprintf("Recording started: %s", recorder.Path()))
|
b.AddOutputLine(fmt.Sprintf("Recording started: %s", recorder.Path()))
|
||||||
b.Notify("recordstart", "me", recorder.Path())
|
b.Notify("recordstart", "me", recorder.Path())
|
||||||
b.renderGeneralStatus()
|
b.renderGeneralStatus()
|
||||||
@@ -205,9 +206,7 @@ func (b *Barnard) detachRecorder() (*recording.Recorder, string, bool) {
|
|||||||
}
|
}
|
||||||
b.Recorder = nil
|
b.Recorder = nil
|
||||||
b.recordingStarting = false
|
b.recordingStarting = false
|
||||||
if b.Stream != nil {
|
b.withStream(func(stream *gumbleopenal.Stream) { stream.SetRecorder(nil) })
|
||||||
b.Stream.SetRecorder(nil)
|
|
||||||
}
|
|
||||||
return recorder, path, wasPending
|
return recorder, path, wasPending
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -129,7 +129,9 @@ func (b *Barnard) OnTimestampToggle(ui *uiterm.Ui, key uiterm.Key) {
|
|||||||
|
|
||||||
func (b *Barnard) OnNoiseSuppressionToggle(ui *uiterm.Ui, key uiterm.Key) {
|
func (b *Barnard) OnNoiseSuppressionToggle(ui *uiterm.Ui, key uiterm.Key) {
|
||||||
enabled := !b.UserConfig.GetNoiseSuppressionEnabled()
|
enabled := !b.UserConfig.GetNoiseSuppressionEnabled()
|
||||||
b.UserConfig.SetNoiseSuppressionEnabled(enabled)
|
if err := b.UserConfig.SetNoiseSuppressionEnabled(enabled); err != nil {
|
||||||
|
b.AddOutputLine("Noise suppression: could not save setting: " + err.Error())
|
||||||
|
}
|
||||||
b.NoiseSuppressor.SetEnabled(enabled)
|
b.NoiseSuppressor.SetEnabled(enabled)
|
||||||
|
|
||||||
if enabled {
|
if enabled {
|
||||||
@@ -198,7 +200,9 @@ func (b *Barnard) CommandMicDown(ui *uiterm.Ui, cmd string) {
|
|||||||
|
|
||||||
func (b *Barnard) CommandNoiseSuppressionToggle(ui *uiterm.Ui, cmd string) {
|
func (b *Barnard) CommandNoiseSuppressionToggle(ui *uiterm.Ui, cmd string) {
|
||||||
enabled := !b.UserConfig.GetNoiseSuppressionEnabled()
|
enabled := !b.UserConfig.GetNoiseSuppressionEnabled()
|
||||||
b.UserConfig.SetNoiseSuppressionEnabled(enabled)
|
if err := b.UserConfig.SetNoiseSuppressionEnabled(enabled); err != nil {
|
||||||
|
b.AddOutputLine("Noise suppression: could not save setting: " + err.Error())
|
||||||
|
}
|
||||||
b.NoiseSuppressor.SetEnabled(enabled)
|
b.NoiseSuppressor.SetEnabled(enabled)
|
||||||
|
|
||||||
if enabled {
|
if enabled {
|
||||||
@@ -266,11 +270,18 @@ func (b *Barnard) CommandPlayFile(ui *uiterm.Ui, cmd string) {
|
|||||||
// Enable stereo encoder for file playback
|
// Enable stereo encoder for file playback
|
||||||
b.Client.EnableStereoEncoder()
|
b.Client.EnableStereoEncoder()
|
||||||
|
|
||||||
// Auto-start transmission if not already transmitting
|
// Auto-start transmission if not already transmitting. FileStreamMutex is
|
||||||
if !b.isTransmitting() && b.Stream != nil {
|
// held here, before withStream's connection mutex, matching cleanup.
|
||||||
err := b.Stream.StartSource(b.UserConfig.GetInputDevice())
|
if !b.isTransmitting() {
|
||||||
if err != nil {
|
var startErr error
|
||||||
b.AddOutputLine(fmt.Sprintf("Error starting transmission: %s", err.Error()))
|
started := b.withStream(func(stream *gumbleopenal.Stream) {
|
||||||
|
startErr = stream.StartSource(b.UserConfig.GetInputDevice())
|
||||||
|
})
|
||||||
|
if !started {
|
||||||
|
startErr = errors.New("audio unavailable while reconnecting")
|
||||||
|
}
|
||||||
|
if startErr != nil {
|
||||||
|
b.AddOutputLine(fmt.Sprintf("Error starting transmission: %s", startErr))
|
||||||
b.FileStream.Stop()
|
b.FileStream.Stop()
|
||||||
b.Client.DisableStereoEncoder()
|
b.Client.DisableStereoEncoder()
|
||||||
return
|
return
|
||||||
@@ -327,8 +338,8 @@ func (b *Barnard) setTransmit(ui *uiterm.Ui, val int) {
|
|||||||
close(b.toneTestStop)
|
close(b.toneTestStop)
|
||||||
b.toneTestStop = nil
|
b.toneTestStop = nil
|
||||||
}
|
}
|
||||||
} else if b.Stream != nil {
|
} else {
|
||||||
b.Stream.StopSource()
|
b.withStream(func(stream *gumbleopenal.Stream) { _ = stream.StopSource() })
|
||||||
}
|
}
|
||||||
} else if !b.isConnected() {
|
} else if !b.isConnected() {
|
||||||
b.Notify("error", "me", "no tx while disconnected")
|
b.Notify("error", "me", "no tx while disconnected")
|
||||||
@@ -347,20 +358,26 @@ func (b *Barnard) setTransmit(ui *uiterm.Ui, val int) {
|
|||||||
b.Notify("micup", "me", "")
|
b.Notify("micup", "me", "")
|
||||||
b.UpdateGeneralStatus(" Tx ", true)
|
b.UpdateGeneralStatus(" Tx ", true)
|
||||||
} else {
|
} else {
|
||||||
err := b.Stream.StartSource(b.UserConfig.GetInputDevice())
|
started := b.withStream(func(stream *gumbleopenal.Stream) {
|
||||||
if err != nil {
|
err := stream.StartSource(b.UserConfig.GetInputDevice())
|
||||||
b.setTransmitting(false)
|
if err != nil {
|
||||||
if fatalAudioOpenError(err) {
|
b.setTransmitting(false)
|
||||||
// A missing capture device cannot recover through normal
|
if fatalAudioOpenError(err) {
|
||||||
// transmission controls; exit so option 1 reports it on stderr.
|
// A missing capture device cannot recover through normal
|
||||||
b.exitWithError(fmt.Errorf("audio device initialization failed: %w", err))
|
// transmission controls; exit so option 1 reports it on stderr.
|
||||||
|
b.exitWithError(fmt.Errorf("audio device initialization failed: %w", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b.Notify("error", "me", err.Error())
|
||||||
|
b.UpdateGeneralStatus(err.Error(), true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
b.Notify("error", "me", err.Error())
|
|
||||||
b.UpdateGeneralStatus(err.Error(), true)
|
|
||||||
} else {
|
|
||||||
b.Notify("micup", "me", "")
|
b.Notify("micup", "me", "")
|
||||||
b.UpdateGeneralStatus(" Tx ", true)
|
b.UpdateGeneralStatus(" Tx ", true)
|
||||||
|
})
|
||||||
|
if !started {
|
||||||
|
b.setTransmitting(false)
|
||||||
|
b.UpdateGeneralStatus("audio unavailable while reconnecting", true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -371,25 +388,29 @@ func fatalAudioOpenError(err error) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Barnard) OnMicVolumeDown(ui *uiterm.Ui, key uiterm.Key) {
|
func (b *Barnard) OnMicVolumeDown(ui *uiterm.Ui, key uiterm.Key) {
|
||||||
if b.ToneTest || b.Stream == nil {
|
if b.ToneTest {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
b.Stream.SetMicVolume(-0.1, true)
|
b.withStream(func(stream *gumbleopenal.Stream) {
|
||||||
b.UserConfig.SetMicVolume(b.Stream.GetMicVolume())
|
stream.SetMicVolume(-0.1, true)
|
||||||
if err := b.UserConfig.SaveConfig(); err != nil {
|
b.UserConfig.SetMicVolume(stream.GetMicVolume())
|
||||||
b.AddOutputLine("Microphone: could not save volume: " + err.Error())
|
if err := b.UserConfig.SaveConfig(); err != nil {
|
||||||
}
|
b.AddOutputLine("Microphone: could not save volume: " + err.Error())
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Barnard) OnMicVolumeUp(ui *uiterm.Ui, key uiterm.Key) {
|
func (b *Barnard) OnMicVolumeUp(ui *uiterm.Ui, key uiterm.Key) {
|
||||||
if b.ToneTest || b.Stream == nil {
|
if b.ToneTest {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
b.Stream.SetMicVolume(0.1, true)
|
b.withStream(func(stream *gumbleopenal.Stream) {
|
||||||
b.UserConfig.SetMicVolume(b.Stream.GetMicVolume())
|
stream.SetMicVolume(0.1, true)
|
||||||
if err := b.UserConfig.SaveConfig(); err != nil {
|
b.UserConfig.SetMicVolume(stream.GetMicVolume())
|
||||||
b.AddOutputLine("Microphone: could not save volume: " + err.Error())
|
if err := b.UserConfig.SaveConfig(); err != nil {
|
||||||
}
|
b.AddOutputLine("Microphone: could not save volume: " + err.Error())
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Barnard) OnQuitPress(ui *uiterm.Ui, key uiterm.Key) {
|
func (b *Barnard) OnQuitPress(ui *uiterm.Ui, key uiterm.Key) {
|
||||||
|
|||||||
+35
-32
@@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.stormux.org/storm/barnard/gumble/gumble"
|
"git.stormux.org/storm/barnard/gumble/gumble"
|
||||||
|
"git.stormux.org/storm/barnard/gumble/gumbleopenal"
|
||||||
"git.stormux.org/storm/barnard/uiterm"
|
"git.stormux.org/storm/barnard/uiterm"
|
||||||
"sort"
|
"sort"
|
||||||
)
|
)
|
||||||
@@ -37,44 +38,46 @@ func (ti TreeItem) TreeItemStyle(fg, bg uiterm.Attribute, active bool) (uiterm.A
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Barnard) changeVolume(users []*gumble.User, change float32) {
|
func (b *Barnard) changeVolume(users []*gumble.User, change float32) {
|
||||||
for _, u := range users {
|
b.withStream(func(stream *gumbleopenal.Stream) {
|
||||||
if b.Stream == nil {
|
for _, u := range users {
|
||||||
continue
|
var boost uint16
|
||||||
|
var ng float32
|
||||||
|
curboost := float32((u.Boost() - 1)) / 10
|
||||||
|
ng = u.Volume() + curboost + change
|
||||||
|
boost = uint16(1)
|
||||||
|
if ng > 1.0 {
|
||||||
|
perc := uint16((ng * 10)) - 10
|
||||||
|
perc += 1
|
||||||
|
boost = perc
|
||||||
|
ng = 1.0
|
||||||
|
}
|
||||||
|
if ng < 0 {
|
||||||
|
ng = 0.0
|
||||||
|
}
|
||||||
|
u.SetBoost(boost)
|
||||||
|
u.SetVolume(ng)
|
||||||
|
stream.UpdateUserGain(u)
|
||||||
|
b.UserConfig.UpdateConfig(u)
|
||||||
}
|
}
|
||||||
var boost uint16
|
if err := b.UserConfig.SaveConfig(); err != nil {
|
||||||
var ng float32
|
b.AddOutputLine("Volume: could not save setting: " + err.Error())
|
||||||
curboost := float32((u.Boost() - 1)) / 10
|
|
||||||
ng = u.Volume() + curboost + change
|
|
||||||
boost = uint16(1)
|
|
||||||
if ng > 1.0 {
|
|
||||||
perc := uint16((ng * 10)) - 10
|
|
||||||
perc += 1
|
|
||||||
boost = perc
|
|
||||||
ng = 1.0
|
|
||||||
}
|
}
|
||||||
if ng < 0 {
|
})
|
||||||
ng = 0.0
|
|
||||||
}
|
|
||||||
u.SetBoost(boost)
|
|
||||||
u.SetVolume(ng)
|
|
||||||
b.Stream.UpdateUserGain(u)
|
|
||||||
b.UserConfig.UpdateConfig(u)
|
|
||||||
}
|
|
||||||
b.UserConfig.SaveConfig()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Barnard) resetVolume(users []*gumble.User) {
|
func (b *Barnard) resetVolume(users []*gumble.User) {
|
||||||
for _, u := range users {
|
b.withStream(func(stream *gumbleopenal.Stream) {
|
||||||
if b.Stream == nil {
|
for _, u := range users {
|
||||||
continue
|
// Reset to original volume (1.0) and boost (1)
|
||||||
|
u.SetBoost(uint16(1))
|
||||||
|
u.SetVolume(1.0)
|
||||||
|
stream.UpdateUserGain(u)
|
||||||
|
b.UserConfig.UpdateConfig(u)
|
||||||
}
|
}
|
||||||
// Reset to original volume (1.0) and boost (1)
|
if err := b.UserConfig.SaveConfig(); err != nil {
|
||||||
u.SetBoost(uint16(1))
|
b.AddOutputLine("Volume: could not save setting: " + err.Error())
|
||||||
u.SetVolume(1.0)
|
}
|
||||||
b.Stream.UpdateUserGain(u)
|
})
|
||||||
b.UserConfig.UpdateConfig(u)
|
|
||||||
}
|
|
||||||
b.UserConfig.SaveConfig()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeUsersArray(users gumble.Users) []*gumble.User {
|
func makeUsersArray(users gumble.Users) []*gumble.User {
|
||||||
|
|||||||
Reference in New Issue
Block a user