pnyx/cmd/client/main.go

347 lines
8.2 KiB
Go

2024-04-03 18:52:04 -06:00
package main
import (
2024-04-08 17:23:55 -06:00
"encoding/binary"
"fmt"
"os"
"slices"
"time"
"git.metznet.ca/MetzNet/pnyx"
2024-04-08 17:23:55 -06:00
"github.com/gen2brain/malgo"
2024-04-12 18:06:57 -06:00
"github.com/google/uuid"
2024-04-08 17:23:55 -06:00
"github.com/hraban/opus"
"seehuhn.de/go/ncurses"
2024-04-03 18:52:04 -06:00
)
2024-04-12 18:06:57 -06:00
var decoders = map[pnyx.PeerID]chan[]byte{}
var encoder *opus.Encoder
var sample_rate int = 0
var mic = make(chan []byte, 0)
2024-04-13 11:44:41 -06:00
var speaker = make(chan []int16, 0)
var audio_data = make(chan []int16, 0)
2024-04-12 18:06:57 -06:00
func set_sample_rate(new_sample_rate int) error {
sample_rate = new_sample_rate
var err error
encoder, err = opus.NewEncoder(new_sample_rate, 1, opus.AppVoIP)
if err != nil {
return err
}
for peer_id, decoder_chan := range(decoders) {
if decoder_chan != nil {
decoder_chan <- nil
}
new_chan := make(chan[]byte, 1000)
decoders[peer_id] = new_chan
go handle_peer_decode(peer_id, decoders[peer_id], sample_rate)
}
return nil
}
func handle_peer_decode(peer_id pnyx.PeerID, decode_chan chan[]byte, sample_rate int){
decoder, err := opus.NewDecoder(sample_rate, 1)
2024-04-08 17:23:55 -06:00
if err != nil {
panic(err)
}
2024-04-12 18:06:57 -06:00
running := true
2024-04-13 11:44:41 -06:00
missed := 0
2024-04-12 18:06:57 -06:00
for running {
select {
case <-time.After(20*time.Millisecond):
2024-04-13 11:44:41 -06:00
missed += 1
if missed > 100 {
decode_chan <- <- decode_chan
}
2024-04-12 18:06:57 -06:00
pcm := make([]int16, sample_rate/50)
err := decoder.DecodePLC(pcm)
if err != nil {
panic(err)
}
2024-04-13 11:44:41 -06:00
audio_data <- pcm
2024-04-12 18:06:57 -06:00
case data := <-decode_chan:
2024-04-13 11:44:41 -06:00
missed = 0
2024-04-12 18:06:57 -06:00
if data == nil {
running = false
} else {
pcm := make([]int16, sample_rate/50*2)
written, err := decoder.Decode(data, pcm)
if err != nil {
panic(err)
}
2024-04-13 11:44:41 -06:00
audio_data <- pcm[:written]
}
}
}
}
2024-04-12 18:06:57 -06:00
2024-04-13 11:44:41 -06:00
func mixer(data_chan chan []int16, speaker_chan chan []int16) {
var samples []int16 = nil
for true {
if samples == nil {
samples = <- data_chan
} else {
select {
case new_samples := <- data_chan:
for i, sample := range(new_samples) {
samples[i] += sample
2024-04-12 18:06:57 -06:00
}
2024-04-13 11:44:41 -06:00
case speaker_chan <- samples:
samples = nil
2024-04-12 18:06:57 -06:00
}
}
}
}
2024-04-13 11:44:41 -06:00
2024-04-12 18:06:57 -06:00
func main() {
ctx, err := malgo.InitContext(nil, malgo.ContextConfig{}, nil)
if err != nil {
panic(err)
}
2024-04-13 11:44:41 -06:00
go mixer(audio_data, speaker)
defer ctx.Free()
defer ctx.Uninit()
2024-04-08 11:32:39 -06:00
infos, err := ctx.Devices(malgo.Playback)
if err != nil {
panic(err)
}
2024-04-08 11:56:34 -06:00
var playback_device *malgo.DeviceInfo = nil
2024-04-08 11:46:43 -06:00
for _, info := range infos {
if info.IsDefault != 0 {
2024-04-08 11:56:34 -06:00
full, err := ctx.DeviceInfo(malgo.Playback, info.ID, malgo.Shared)
if err != nil {
panic(err)
}
playback_device = &full
2024-04-08 11:46:43 -06:00
}
2024-04-08 11:32:39 -06:00
}
2024-04-08 11:56:34 -06:00
if playback_device == nil {
panic("No default playback device")
}
2024-04-08 11:32:39 -06:00
infos, err = ctx.Devices(malgo.Capture)
if err != nil {
panic(err)
}
2024-04-08 11:56:34 -06:00
var capture_device *malgo.DeviceInfo = nil
2024-04-08 11:46:43 -06:00
for _, info := range infos {
if info.IsDefault != 0 {
2024-04-08 11:56:34 -06:00
full, err := ctx.DeviceInfo(malgo.Capture, info.ID, malgo.Shared)
if err != nil {
panic(err)
}
capture_device = &full
2024-04-08 11:46:43 -06:00
}
2024-04-08 11:32:39 -06:00
}
2024-04-08 11:56:34 -06:00
if capture_device == nil {
panic("No default capture device")
}
inDeviceConfig := malgo.DefaultDeviceConfig(malgo.Capture)
2024-04-08 17:23:55 -06:00
inDeviceConfig.Capture.Format = malgo.FormatS16
2024-04-08 12:08:36 -06:00
inDeviceConfig.Capture.Channels = 1
2024-04-08 11:47:56 -06:00
inDeviceConfig.Capture.DeviceID = capture_device.ID.Pointer()
2024-04-08 17:26:43 -06:00
inDeviceConfig.SampleRate = 48000
inDeviceConfig.PeriodSizeInFrames = 960
inDeviceConfig.Alsa.NoMMap = 1
inDeviceConfig.Capture.ShareMode = malgo.Shared
outDeviceConfig := malgo.DefaultDeviceConfig(malgo.Playback)
2024-04-08 17:23:55 -06:00
outDeviceConfig.Playback.Format = malgo.FormatS16
2024-04-08 12:08:36 -06:00
outDeviceConfig.Playback.Channels = 1
2024-04-08 11:47:56 -06:00
outDeviceConfig.Playback.DeviceID = playback_device.ID.Pointer()
2024-04-08 17:26:43 -06:00
outDeviceConfig.SampleRate = 48000
outDeviceConfig.PeriodSizeInFrames = 960
outDeviceConfig.Alsa.NoMMap = 1
outDeviceConfig.Playback.ShareMode = malgo.Shared
onSendFrames := func(output_samples []byte, input_samples []byte, framecount uint32) {
select {
2024-04-13 11:44:41 -06:00
case pcm := <- speaker:
for i := 0; i < sample_rate/50; i++ {
binary.LittleEndian.PutUint16(output_samples[i*2:], uint16(pcm[i]))
}
2024-04-08 11:59:36 -06:00
default:
}
}
playbackCallbacks := malgo.DeviceCallbacks{
Data: onSendFrames,
}
outDevice, err := malgo.InitDevice(ctx.Context, outDeviceConfig, playbackCallbacks)
if err != nil {
panic(err)
}
err = outDevice.Start()
if err != nil {
panic(err)
}
defer outDevice.Uninit()
defer outDevice.Stop()
onRecvFrames := func(output_samples []byte, input_samples []byte, framecount uint32) {
2024-04-12 18:06:57 -06:00
if encoder != nil {
pcm := make([]int16, len(input_samples)/2)
for i := 0; i < len(input_samples)/2; i++ {
pcm[i] = int16(binary.LittleEndian.Uint16(input_samples[2*i:]))
}
2024-04-12 18:06:57 -06:00
data := make([]byte, len(input_samples))
written, err := encoder.Encode(pcm, data)
if err != nil {
panic(err)
}
select {
case mic <- data[:written]:
default:
}
}
}
captureCallbacks := malgo.DeviceCallbacks{
Data: onRecvFrames,
}
inDevice, err := malgo.InitDevice(ctx.Context, inDeviceConfig, captureCallbacks)
if err != nil {
panic(err)
}
err = inDevice.Start()
if err != nil {
panic(err)
}
defer inDevice.Uninit()
defer inDevice.Stop()
2024-04-06 17:03:31 -06:00
client, err := pnyx.NewClient(nil, os.Args[1])
2024-04-03 18:52:04 -06:00
if err != nil {
panic(err)
}
2024-04-08 18:21:17 -06:00
go func() {
var buf [1024]byte
for true {
read, _, err := client.Connection.ReadFromUDP(buf[:])
if err != nil {
break
}
2024-04-11 21:05:50 -06:00
data, err := pnyx.ParseSessionData(&client.Session, buf[pnyx.COMMAND_LENGTH + pnyx.SESSION_ID_LENGTH:read])
if err != nil {
continue
}
packet, err := pnyx.ParsePacket(data)
if err != nil {
continue
}
switch packet := packet.(type) {
2024-04-12 18:06:57 -06:00
case pnyx.ChannelCommandPacket:
if packet.Channel == pnyx.ChannelID(0) {
2024-04-12 18:06:57 -06:00
if packet.Mode == pnyx.MODE_AUDIO {
if packet.Command == pnyx.AUDIO_SET_SAMPLE_RATE {
var new_sample_rate int
switch packet.Data[0] {
case byte(pnyx.SAMPLE_RATE_24KHZ):
new_sample_rate = 24000
case byte(pnyx.SAMPLE_RATE_48KHZ):
new_sample_rate = 48000
default:
continue
}
err := set_sample_rate(new_sample_rate)
if err != nil {
panic(err)
}
2024-04-12 18:06:57 -06:00
}
}
}
case pnyx.ChannelPeerPacket:
if packet.Channel == pnyx.ChannelID(0) {
decode_chan, exists := decoders[packet.Peer]
if exists == false {
if sample_rate != 0 {
decode_chan = make(chan[]byte, 1000)
decoders[packet.Peer] = decode_chan
go handle_peer_decode(packet.Peer, decoders[packet.Peer], sample_rate)
decode_chan <- packet.Data
} else {
decoders[packet.Peer] = nil
}
} else if decode_chan != nil {
decode_chan <- packet.Data
}
2024-04-08 17:23:55 -06:00
}
default:
2024-04-08 17:23:55 -06:00
}
}
}()
join_packet := pnyx.NewServerCommandPacket(uuid.New(), pnyx.SERVER_COMMAND_JOIN_CHANNEL, []byte{0x00})
err = client.Send(join_packet)
2024-04-08 17:23:55 -06:00
if err != nil {
panic(err)
}
2024-04-12 18:06:57 -06:00
get_sample_rate_packet := pnyx.NewChannelCommandPacket(uuid.New(), pnyx.ChannelID(0), pnyx.MODE_AUDIO, pnyx.AUDIO_SET_SAMPLE_RATE, []byte{byte(pnyx.SAMPLE_RATE_48KHZ)})
err = client.Send(get_sample_rate_packet)
if err != nil {
panic(err)
}
go func () {
for true {
data := <- mic
err = client.Send(pnyx.NewChannelDataPacket(pnyx.ChannelID(0), pnyx.MODE_AUDIO, data))
if err != nil {
panic(err)
}
}
}()
window := ncurses.Init()
defer ncurses.EndWin()
ncurses.ColorPair(1).Init(ncurses.ColorBlue, ncurses.ColorRed)
window.AddStr("pnyx client")
for true {
window.Refresh()
time.Sleep(200*time.Millisecond)
peers := make([]pnyx.PeerID, 0, len(decoders))
for peer_id := range(decoders) {
peers = append(peers, peer_id)
}
slices.SortFunc(peers, func(a, b pnyx.PeerID) int {
return slices.Compare(a[:], b[:])
})
for i, peer_id := range(peers) {
window.MvAddStr(i+1, 0, fmt.Sprintf("%x", peer_id))
}
}
2024-04-03 18:52:04 -06:00
}