Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ bin/
.env*
!.env.example

coverage.out
coverage.*
12 changes: 7 additions & 5 deletions cmd/client/chat.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ type channelRefreshTick struct{}

var errDirectoryKeyPending = fmt.Errorf("directory key pending")

var teaTick = tea.Tick

func newChatModel(api *APIClient, auth *AuthResponse, kp *crypto.KeyPair, keystorePassphrase string, width, height int, voiceIPCAddr string) chatModel {
input := textinput.New()
input.Placeholder = "type a message..."
Expand Down Expand Up @@ -1110,7 +1112,7 @@ func (m *chatModel) persistDirectoryKey() error {
}

func (m *chatModel) scheduleDirectoryTick() tea.Cmd {
return tea.Tick(10*time.Second, func(time.Time) tea.Msg {
return teaTick(10*time.Second, func(time.Time) tea.Msg {
return directoryTick{}
})
}
Expand Down Expand Up @@ -1890,13 +1892,13 @@ func (m *chatModel) refreshPresence() {
}

func (m *chatModel) schedulePresenceTick() tea.Cmd {
return tea.Tick(5*time.Second, func(time.Time) tea.Msg {
return teaTick(5*time.Second, func(time.Time) tea.Msg {
return presenceTick{}
})
}

func (m *chatModel) scheduleVoicePing() tea.Cmd {
return tea.Tick(15*time.Second, func(time.Time) tea.Msg {
return teaTick(15*time.Second, func(time.Time) tea.Msg {
return voicePingTick{}
})
}
Expand All @@ -1915,13 +1917,13 @@ func (m *chatModel) scheduleVoiceReconnect(attempt int) tea.Cmd {
}

func (m *chatModel) scheduleShareTick() tea.Cmd {
return tea.Tick(shareKeysInterval, func(time.Time) tea.Msg {
return teaTick(shareKeysInterval, func(time.Time) tea.Msg {
return shareTick{}
})
}

func (m *chatModel) scheduleChannelRefresh() tea.Cmd {
return tea.Tick(channelRefreshDelay, func(time.Time) tea.Msg {
return teaTick(channelRefreshDelay, func(time.Time) tea.Msg {
return channelRefreshTick{}
})
}
Expand Down
35 changes: 35 additions & 0 deletions cmd/client/chat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1361,17 +1361,52 @@ func TestChatModelSendCurrentMessageChannel(t *testing.T) {

func TestChatModelScheduleTicks(t *testing.T) {
m := newChatForTest(t, &APIClient{serverURL: "http://server", httpClient: http.DefaultClient})
originalTeaTick := teaTick
durations := make([]time.Duration, 0, 5)
teaTick = func(delay time.Duration, callback func(time.Time) tea.Msg) tea.Cmd {
durations = append(durations, delay)
return func() tea.Msg {
return callback(time.Now())
}
}
t.Cleanup(func() {
teaTick = originalTeaTick
})

if cmd := m.scheduleDirectoryTick(); cmd == nil {
t.Fatalf("expected directory tick cmd")
} else if _, ok := cmd().(directoryTick); !ok {
t.Fatalf("expected directoryTick message")
}
if cmd := m.schedulePresenceTick(); cmd == nil {
t.Fatalf("expected presence tick cmd")
} else if _, ok := cmd().(presenceTick); !ok {
t.Fatalf("expected presenceTick message")
}
if cmd := m.scheduleVoicePing(); cmd == nil {
t.Fatalf("expected voice ping cmd")
} else if _, ok := cmd().(voicePingTick); !ok {
t.Fatalf("expected voicePingTick message")
}
if cmd := m.scheduleShareTick(); cmd == nil {
t.Fatalf("expected share tick cmd")
} else if _, ok := cmd().(shareTick); !ok {
t.Fatalf("expected shareTick message")
}
if cmd := m.scheduleChannelRefresh(); cmd == nil {
t.Fatalf("expected channel refresh cmd")
} else if _, ok := cmd().(channelRefreshTick); !ok {
t.Fatalf("expected channelRefreshTick message")
}

expectedDurations := []time.Duration{10 * time.Second, 5 * time.Second, 15 * time.Second, shareKeysInterval, channelRefreshDelay}
if len(durations) != len(expectedDurations) {
t.Fatalf("captured durations length=%d want=%d", len(durations), len(expectedDurations))
}
for index := range expectedDurations {
if durations[index] != expectedDurations[index] {
t.Fatalf("duration[%d]=%s want=%s", index, durations[index], expectedDurations[index])
}
}
}

Expand Down
18 changes: 9 additions & 9 deletions internal/audio/capture.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ type Capture struct {

func StartCapture(ctx context.Context) (*Capture, <-chan []int16, error) {
config := malgo.ContextConfig{}
malgoCtx, err := malgo.InitContext(nil, config, nil)
malgoCtx, err := malgoInitContext(nil, config, nil)
if err != nil {
return nil, nil, fmt.Errorf("init malgo context: %w", err)
}

deviceConfig := malgo.DefaultDeviceConfig(malgo.Capture)
deviceConfig := malgoDefaultDeviceConfig(malgo.Capture)
deviceConfig.Capture.Format = malgo.FormatS16
deviceConfig.Capture.Channels = Channels
deviceConfig.SampleRate = SampleRate
Expand All @@ -52,14 +52,14 @@ func StartCapture(ctx context.Context) (*Capture, <-chan []int16, error) {
},
}

device, err := malgo.InitDevice(malgoCtx.Context, deviceConfig, callback)
device, err := malgoInitDevice(malgoCtx.Context, deviceConfig, callback)
if err != nil {
malgoCtx.Uninit()
malgoContextUninit(malgoCtx)
return nil, nil, fmt.Errorf("init capture device: %w", err)
}
if err := device.Start(); err != nil {
device.Uninit()
malgoCtx.Uninit()
if err := malgoDeviceStart(device); err != nil {
malgoDeviceUninit(device)
malgoContextUninit(malgoCtx)
return nil, nil, fmt.Errorf("start capture: %w", err)
}

Expand All @@ -78,11 +78,11 @@ func (c *Capture) Close() error {
}
c.closeOnce.Do(func() {
if c.device != nil {
c.device.Uninit()
malgoDeviceUninit(c.device)
c.device = nil
}
if c.ctx != nil {
c.ctx.Uninit()
malgoContextUninit(c.ctx)
c.ctx = nil
}
})
Expand Down
14 changes: 14 additions & 0 deletions internal/audio/malgo_hooks_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//go:build linux

package audio

import "github.com/gen2brain/malgo"

var (
malgoInitContext = malgo.InitContext
malgoDefaultDeviceConfig = malgo.DefaultDeviceConfig
malgoInitDevice = malgo.InitDevice
malgoContextUninit = (*malgo.AllocatedContext).Uninit
malgoDeviceStart = (*malgo.Device).Start
malgoDeviceUninit = (*malgo.Device).Uninit
)
18 changes: 9 additions & 9 deletions internal/audio/playback.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ type Playback struct {

func StartPlayback(ctx context.Context) (*Playback, error) {
config := malgo.ContextConfig{}
malgoCtx, err := malgo.InitContext(nil, config, nil)
malgoCtx, err := malgoInitContext(nil, config, nil)
if err != nil {
return nil, fmt.Errorf("init malgo context: %w", err)
}

deviceConfig := malgo.DefaultDeviceConfig(malgo.Playback)
deviceConfig := malgoDefaultDeviceConfig(malgo.Playback)
deviceConfig.Playback.Format = malgo.FormatS16
deviceConfig.Playback.Channels = Channels
deviceConfig.SampleRate = SampleRate
Expand All @@ -46,14 +46,14 @@ func StartPlayback(ctx context.Context) (*Playback, error) {
},
}

device, err := malgo.InitDevice(malgoCtx.Context, deviceConfig, callback)
device, err := malgoInitDevice(malgoCtx.Context, deviceConfig, callback)
if err != nil {
malgoCtx.Uninit()
malgoContextUninit(malgoCtx)
return nil, fmt.Errorf("init playback device: %w", err)
}
if err := device.Start(); err != nil {
device.Uninit()
malgoCtx.Uninit()
if err := malgoDeviceStart(device); err != nil {
malgoDeviceUninit(device)
malgoContextUninit(malgoCtx)
return nil, fmt.Errorf("start playback: %w", err)
}

Expand Down Expand Up @@ -118,11 +118,11 @@ func (p *Playback) Close() error {
}
p.closeOnce.Do(func() {
if p.device != nil {
p.device.Uninit()
malgoDeviceUninit(p.device)
p.device = nil
}
if p.ctx != nil {
p.ctx.Uninit()
malgoContextUninit(p.ctx)
p.ctx = nil
}
})
Expand Down
Loading
Loading