diff --git a/README.md b/README.md index 0aaf580..517c326 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ reasonably shouldn't, let me know (or fix it and then let me know!) - [x] handling everyone's state - [x] any of the actual audio stuff - [x] noise suppression with rnnoise -- [ ] chat with more than a singular other person, probably +- [x] chat with more than a singular other person, probably ## building hubbub requires go 1.23 to build. it might run on earlier versions, but i think diff --git a/audio/audio.go b/audio/audio.go index 909a9a2..5cb38c5 100644 --- a/audio/audio.go +++ b/audio/audio.go @@ -29,17 +29,9 @@ const sampleRate = 24000 const channels = 1 type Audio struct { - Muted bool - Deafened bool - OutBuffer chan string -} - -func NewAudio() Audio { - return Audio{ - Muted: false, - Deafened: false, - OutBuffer: make(chan string), - } + Muted bool + Deafened bool + Buffers map[string]chan string } func (au *Audio) ProcessInput(conn *ircevent.Connection, channel string) error { @@ -72,23 +64,36 @@ func (au *Audio) ProcessInput(conn *ircevent.Connection, channel string) error { } } - empty := make([]float32, len(out)) - select { - case str := <-au.OutBuffer: - if !au.Deafened { - raw, err := base64.StdEncoding.DecodeString(str) - if err != nil { - break + // mix all outputs together + mix := make([]float32, len(out)) + voices := 0 + for _, buffer := range au.Buffers { + select { + case str := <-buffer: + if !au.Deafened { + raw, err := base64.StdEncoding.DecodeString(str) + if err != nil { + break + } + decoded := make([]float32, len(out)) + if _, err = dec.DecodeFloat32(raw, decoded); err != nil { + break + } + for i := range decoded { + mix[i] += decoded[i] + } + voices++ } - if _, err = dec.DecodeFloat32(raw, out); err != nil { - break - } - } else { - copy(out, empty) + default: } - default: - copy(out, empty) } + + if voices > 0 { + for i := range mix { + mix[i] /= float32(voices) + } + } + copy(out, mix) }) if err != nil { log.Printf("error opening input stream: %v", err) diff --git a/main.go b/main.go index 954c05e..3ad28d1 100644 --- a/main.go +++ b/main.go @@ -119,6 +119,7 @@ func (m model) Init() tea.Cmd { u, ok := m.users[e.Nick()] if !ok { m.users[e.Nick()] = user{} + m.au.Buffers[e.Nick()] = make(chan string) } switch message { @@ -133,7 +134,7 @@ func (m model) Init() tea.Cmd { u.isMuted = false default: u.lastSpoke = time.Now() - m.au.OutBuffer <- message + m.au.Buffers[e.Nick()] <- message } m.users[e.Nick()] = u @@ -141,6 +142,9 @@ func (m model) Init() tea.Cmd { m.conn.AddCallback("JOIN", func(e ircmsg.Message) { m.users[e.Nick()] = user{} + if e.Nick() != m.nick { + m.au.Buffers[e.Nick()] = make(chan string) + } // advertise your muted and deafened statuses on someone joining if m.deafen { @@ -157,10 +161,12 @@ func (m model) Init() tea.Cmd { m.conn.AddCallback("PART", func(e ircmsg.Message) { delete(m.users, e.Nick()) + delete(m.au.Buffers, e.Nick()) }) m.conn.AddCallback("QUIT", func(e ircmsg.Message) { delete(m.users, e.Nick()) + delete(m.au.Buffers, e.Nick()) }) m.conn.Join(m.channel) @@ -215,8 +221,9 @@ func (m model) View() (s string) { } s += fmt.Sprintf("%d user%s connected:\n", numUsers, plural) - for _, nick := range slices.Sorted(maps.Keys(m.users)) { - user := m.users[nick] + users := maps.Clone(m.users) + for _, nick := range slices.Sorted(maps.Keys(users)) { + user := users[nick] status := " " nickStyled := styleInactive.Render(nick) if user.isDeafened { @@ -282,7 +289,9 @@ func start(c *cli.Context) error { return err } - au := audio.NewAudio() + au := audio.Audio{ + Buffers: map[string](chan string){}, + } go func() { if err := au.ProcessInput(&conn.Connection, config.Channel); err != nil { os.Exit(1)