Skip to content

Commit

Permalink
Try audio byte to int16 slicing
Browse files Browse the repository at this point in the history
  • Loading branch information
sergystepanov committed Aug 31, 2023
1 parent 70c8439 commit 3f76047
Show file tree
Hide file tree
Showing 9 changed files with 69 additions and 49 deletions.
16 changes: 16 additions & 0 deletions pkg/worker/caged/app/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package app

type App interface {
AudioSampleRate() int
Init() error
ViewportSize() (int, int)
Start()
Close()

SetAudioCb(func(Audio))
}

type Audio struct {
Data []byte
Duration int64
}
25 changes: 9 additions & 16 deletions pkg/worker/caged/caged.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,12 @@ import (

"github.com/giongto35/cloud-game/v3/pkg/config"
"github.com/giongto35/cloud-game/v3/pkg/logger"
"github.com/giongto35/cloud-game/v3/pkg/worker/caged/app"
"github.com/giongto35/cloud-game/v3/pkg/worker/caged/libretro"
)

type App interface {
AudioSampleRate() int
Init() error
ViewportSize() (int, int)
Start()
Close()
}

type Manager struct {
list map[ModName]App
list map[ModName]app.App
log *logger.Logger
}

Expand All @@ -27,18 +20,18 @@ type ModName string
const Libretro ModName = "libretro"

func NewManager(log *logger.Logger) *Manager {
return &Manager{log: log, list: make(map[ModName]App)}
return &Manager{log: log, list: make(map[ModName]app.App)}
}

func (m *Manager) Get(name ModName) App { return m.list[name] }
func (m *Manager) Get(name ModName) app.App { return m.list[name] }

func (m *Manager) Load(name ModName, conf any) error {
if name == Libretro {
app, err := m.loadLibretro(conf)
caged, err := m.loadLibretro(conf)
if err != nil {
return err
}
m.list[name] = app
m.list[name] = caged
}
return nil
}
Expand All @@ -60,9 +53,9 @@ func (m *Manager) loadLibretro(conf any) (*libretro.Caged, error) {
Recording: r.Interface().(config.Recording),
}

app := libretro.Cage(c, m.log)
if err := app.Init(); err != nil {
caged := libretro.Cage(c, m.log)
if err := caged.Init(); err != nil {
return nil, err
}
return &app, nil
return &caged, nil
}
32 changes: 15 additions & 17 deletions pkg/worker/caged/libretro/frontend.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,22 @@ import (
"sync"
"sync/atomic"
"time"
"unsafe"

"github.com/giongto35/cloud-game/v3/pkg/config"
"github.com/giongto35/cloud-game/v3/pkg/logger"
"github.com/giongto35/cloud-game/v3/pkg/os"
"github.com/giongto35/cloud-game/v3/pkg/worker/caged/app"
"github.com/giongto35/cloud-game/v3/pkg/worker/caged/libretro/image"
"github.com/giongto35/cloud-game/v3/pkg/worker/caged/libretro/nanoarch"
)

type Emulator interface {
// SetAudio sets the audio callback
SetAudio(func(*GameAudio))
// SetAudioCb sets the audio callback
SetAudioCb(func(app.Audio))
// SetVideo sets the video callback
SetVideo(func(*GameFrame))
Audio() func(*GameAudio)
Audio() func(app.Audio)
Video() func(*GameFrame)
LoadCore(name string)
LoadGame(path string) error
Expand Down Expand Up @@ -54,7 +56,7 @@ type Emulator interface {

type Frontend struct {
onVideo func(*GameFrame)
onAudio func(*GameAudio)
onAudio func(app.Audio)

input InputState

Expand Down Expand Up @@ -88,10 +90,6 @@ type (
Data *image.Frame
Duration time.Duration
}
GameAudio struct {
Data *[]int16
Duration time.Duration
}
InputEvent struct {
RawState []byte
}
Expand Down Expand Up @@ -121,7 +119,7 @@ var (
)

var (
noAudio = func(*GameAudio) {}
noAudio = func(app.Audio) {}
noVideo = func(*GameFrame) {}
videoPool sync.Pool
)
Expand Down Expand Up @@ -203,13 +201,13 @@ func (f *Frontend) handleAudio(data []int16, samples int) {
// 1600 = x / 1000 * 48000 * 2
estimate := float64(samples) / float64(sampleRate<<1) * 1000000000

fr, _ := audioPool.Get().(*GameAudio)
fr, _ := audioPool.Get().(*app.Audio)
if fr == nil {
fr = &GameAudio{}
fr = &app.Audio{}
}
fr.Data = &xx
fr.Duration = time.Duration(estimate) // used in recordings
f.onAudio(fr)
fr.Data = unsafe.Slice((*byte)(unsafe.Pointer(unsafe.SliceData(xx))), samples*2)
fr.Duration = int64(time.Duration(estimate)) // used in recordings
f.onAudio(*fr)
audioPool.Put(fr)
audioCopyPool.Put(dst)
}
Expand Down Expand Up @@ -237,7 +235,7 @@ func (f *Frontend) Shutdown() {
f.mu.Lock()
f.nano.Shutdown()
f.Canvas.Clear()
f.SetAudio(noAudio)
f.SetAudioCb(noAudio)
f.SetVideo(noVideo)
f.mu.Unlock()
f.log.Debug().Msgf("run loop finished")
Expand Down Expand Up @@ -290,7 +288,7 @@ func (f *Frontend) Start() {
}

func (f *Frontend) FrameSize() (int, int) { return f.nano.GeometryBase() }
func (f *Frontend) Audio() func(*GameAudio) { return f.onAudio }
func (f *Frontend) Audio() func(app.Audio) { return f.onAudio }
func (f *Frontend) Video() func(*GameFrame) { return f.onVideo }
func (f *Frontend) FPS() int { return f.nano.VideoFramerate() }
func (f *Frontend) HashPath() string { return f.storage.GetSavePath() }
Expand All @@ -304,7 +302,7 @@ func (f *Frontend) RestoreGameState() error { return f.Load() }
func (f *Frontend) IsPortrait() bool { return f.nano.IsPortrait() }
func (f *Frontend) SaveGameState() error { return f.Save() }
func (f *Frontend) Scale(factor int) { w, h := f.ViewportSize(); f.SetViewport(w, h, factor) }
func (f *Frontend) SetAudio(ff func(*GameAudio)) { f.onAudio = ff }
func (f *Frontend) SetAudioCb(cb func(app.Audio)) { f.onAudio = cb }
func (f *Frontend) SetSessionId(name string) { f.storage.SetMainSaveName(name) }
func (f *Frontend) SetViewport(width int, height int, scale int) {
f.mu.Lock()
Expand Down
5 changes: 3 additions & 2 deletions pkg/worker/caged/libretro/frontend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (

"github.com/giongto35/cloud-game/v3/pkg/config"
"github.com/giongto35/cloud-game/v3/pkg/logger"
"github.com/giongto35/cloud-game/v3/pkg/worker/caged/app"
"github.com/giongto35/cloud-game/v3/pkg/worker/caged/libretro/nanoarch"
)

Expand Down Expand Up @@ -101,7 +102,7 @@ func GetDefaultFrontend(room string, system string, rom string) *EmulatorMock {
mock := GetEmulatorMock(room, system)
mock.loadRom(rom)
mock.SetVideo(func(_ *GameFrame) {})
mock.SetAudio(func(_ *GameAudio) {})
mock.SetAudioCb(func(app.Audio) {})

return mock
}
Expand Down Expand Up @@ -358,7 +359,7 @@ func TestStateConcurrency(t *testing.T) {
t.Errorf("It seems that rom video frame was empty, which is strange!")
}
})
mock.SetAudio(func(_ *GameAudio) {})
mock.SetAudioCb(func(app.Audio) {})

t.Logf("Random seed is [%v]\n", test.seed)
t.Logf("Save path is [%v]\n", mock.paths.save)
Expand Down
14 changes: 11 additions & 3 deletions pkg/worker/caged/libretro/recording.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package libretro

import (
"time"
"unsafe"

"github.com/giongto35/cloud-game/v3/pkg/config"
"github.com/giongto35/cloud-game/v3/pkg/logger"
"github.com/giongto35/cloud-game/v3/pkg/worker/caged/app"
"github.com/giongto35/cloud-game/v3/pkg/worker/recorder"
)

Expand All @@ -27,10 +31,14 @@ func WithRecording(fe Emulator, rec bool, user string, game string, conf config.
return rr
}

func (r *RecordingFrontend) SetAudio(fn func(*GameAudio)) {
r.Emulator.SetAudio(func(audio *GameAudio) {
func unwrapAudio(a []byte) []int16 {
return unsafe.Slice((*int16)(unsafe.Pointer(&a[0])), len(a)>>2)
}

func (r *RecordingFrontend) SetAudioCb(fn func(app.Audio)) {
r.Emulator.SetAudioCb(func(audio app.Audio) {
if r.IsRecording() {
r.rec.WriteAudio(recorder.Audio{Samples: audio.Data, Duration: audio.Duration})
r.rec.WriteAudio(recorder.Audio{Samples: unwrapAudio(audio.Data), Duration: time.Duration(audio.Duration)})
}
fn(audio)
})
Expand Down
4 changes: 4 additions & 0 deletions pkg/worker/coordinatorhandlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package worker

import (
"encoding/base64"
"unsafe"

"github.com/giongto35/cloud-game/v3/pkg/api"
"github.com/giongto35/cloud-game/v3/pkg/com"
Expand Down Expand Up @@ -31,6 +32,9 @@ func emulator(wtf any) *libretro.Caged { return wtf.(*libretro.Caged) }
func recorder(wtf any) *libretro.RecordingFrontend {
return (emulator(wtf).Emulator).(*libretro.RecordingFrontend)
}
func unwrapAudio(a []byte) []int16 {
return unsafe.Slice((*int16)(unsafe.Pointer(unsafe.SliceData(a))), len(a)/2)
}

func (c *coordinator) HandleWebrtcInit(rq api.WebrtcInitRequest[com.Uid], w *Worker, connApi *webrtc.ApiFactory) api.Out {
peer := webrtc.New(c.log, connApi)
Expand Down
2 changes: 1 addition & 1 deletion pkg/worker/recorder/recorder.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ type videoStream interface {

type (
Audio struct {
Samples *[]int16
Samples []int16
Duration time.Duration
}
Video struct {
Expand Down
2 changes: 1 addition & 1 deletion pkg/worker/recorder/wavstream.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func (w *wavStream) Close() (err error) {
}

func (w *wavStream) Write(data Audio) {
pcm := *data.Samples
pcm := data.Samples
bs := make([]byte, len(pcm)*2)
// int & 0xFF + (int >> 8) & 0xFF
for i, ln := 0, len(pcm); i < ln; i++ {
Expand Down
18 changes: 9 additions & 9 deletions pkg/worker/room.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ import (
"github.com/giongto35/cloud-game/v3/pkg/config"
"github.com/giongto35/cloud-game/v3/pkg/logger"
"github.com/giongto35/cloud-game/v3/pkg/network/webrtc"
"github.com/giongto35/cloud-game/v3/pkg/worker/caged"
"github.com/giongto35/cloud-game/v3/pkg/worker/caged/app"
"github.com/giongto35/cloud-game/v3/pkg/worker/caged/libretro"
"github.com/giongto35/cloud-game/v3/pkg/worker/encoder"
"github.com/giongto35/cloud-game/v3/pkg/worker/encoder/h264"
"github.com/giongto35/cloud-game/v3/pkg/worker/encoder/vpx"
)

type AppRoom interface {
App() caged.App
App() app.App
Close()
Id() string
StartApp()
Expand All @@ -26,7 +26,7 @@ type AppRoom interface {
}

type Room struct {
app caged.App
app app.App
id string
log *logger.Logger
users com.NetMap[Session]
Expand All @@ -36,17 +36,17 @@ type Room struct {
HandleClose func(self *Room)
}

func NewRoom(id string, app caged.App, conf config.WorkerConfig, log *logger.Logger) *Room {
func NewRoom(id string, app app.App, conf config.WorkerConfig, log *logger.Logger) *Room {
room := &Room{id: id, app: app, users: com.NewNetMap[Session](), log: log}
w, h := app.ViewportSize()
room.initVideo(w, h, conf.Encoder.Video)
room.initAudio(app.AudioSampleRate(), conf.Encoder.Audio)
return room
}

func (r *Room) App() caged.App { return r.app }
func (r *Room) Id() string { return r.id }
func (r *Room) StartApp() { r.app.Start() }
func (r *Room) App() app.App { return r.app }
func (r *Room) Id() string { return r.id }
func (r *Room) StartApp() { r.app.Start() }

func (r *Room) OnUserConnect(user Session) bool {
r.log.Debug().Str("user", user.Id().String()).Msg("User has joined the room")
Expand Down Expand Up @@ -92,8 +92,8 @@ func (r *Room) initAudio(srcHz int, conf config.Audio) {
}
frameDur := time.Duration(conf.Frame) * time.Millisecond

emulator(r.app).SetAudio(func(raw *libretro.GameAudio) {
buf.write(*raw.Data, func(pcm samples) {
emulator(r.app).SetAudioCb(func(raw app.Audio) {
buf.write(unwrapAudio(raw.Data), func(pcm samples) {
data, err := opus_.Encode(pcm)
audioPool.Put((*[]int16)(&pcm))
if err != nil {
Expand Down

0 comments on commit 3f76047

Please sign in to comment.