diff --git a/chip8/beep.wav b/chip8/beep.wav new file mode 100644 index 0000000..8343a63 Binary files /dev/null and b/chip8/beep.wav differ diff --git a/chip8/chip8.go b/chip8/chip8.go index fe0c6bc..afec0a8 100644 --- a/chip8/chip8.go +++ b/chip8/chip8.go @@ -4,10 +4,11 @@ import ( _ "embed" "encoding/binary" "image" + "math" ) -//go:embed sound.wav -var Sound []byte +//go:embed beep.wav +var Beep []byte const ( MemorySize = 4096 // 4KB of memory @@ -19,13 +20,14 @@ const ( ) const ( - Width = 64 - Height = 32 - FPS = 60 + Width = 64 + Height = 32 + FPS = 60 + SampleRate = 44100 ) type KeyPressed func(key uint8) bool -type PlaySound func(sound []byte) func() +type SoundPlayer func(sound []byte) (func(), func()) type ROM struct { Name string @@ -43,14 +45,14 @@ type Emulator struct { ROM ROM // game rom Display *image.RGBA // display buffer KeyPressed KeyPressed // input function - PlaySound PlaySound - StopSound func() + PlaySound func() // play sound effect + StopSound func() // stop sound effect } -func NewEmulator(keyPressed KeyPressed, playSound PlaySound) *Emulator { +func NewEmulator(keyPressed KeyPressed, soundPlayer SoundPlayer) *Emulator { emulator := new(Emulator) emulator.KeyPressed = keyPressed - emulator.PlaySound = playSound + emulator.PlaySound, emulator.StopSound = soundPlayer(Beep) emulator.Stack = NewStack() emulator.Display = image.NewRGBA(image.Rect(0, 0, Width, Height)) emulator.Reset() @@ -97,20 +99,13 @@ func (emulator *Emulator) LoadFont() { } func (emulator *Emulator) UpdateTimers() { - if emulator.DT > 0 { - emulator.DT -= 1 - } - if emulator.ST > 0 { - emulator.ST -= 1 - if emulator.StopSound == nil { - emulator.StopSound = emulator.PlaySound(Sound) - } + if emulator.ST != 0 { + emulator.PlaySound() } else { - if emulator.StopSound != nil { - emulator.StopSound() - emulator.StopSound = nil - } + emulator.StopSound() } + emulator.ST = uint8(math.Max(0, float64(emulator.ST)-1)) + emulator.DT = uint8(math.Max(0, float64(emulator.DT)-1)) } func (emulator *Emulator) Cycle() { diff --git a/chip8/chip8_test.go b/chip8/chip8_test.go index 9c7921c..2972ca2 100644 --- a/chip8/chip8_test.go +++ b/chip8/chip8_test.go @@ -8,7 +8,9 @@ import ( ) func TestEmulator_Reset(t *testing.T) { - emulator := chip8.NewEmulator(nil, nil) + soundPlayer := func(sound []byte) (func(), func()) { return nil, nil } + + emulator := chip8.NewEmulator(nil, soundPlayer) emulator.V[0x03] = 0xFF emulator.V[0x0F] = 0xBB diff --git a/chip8/sound.wav b/chip8/sound.wav deleted file mode 100644 index 657aa25..0000000 Binary files a/chip8/sound.wav and /dev/null differ diff --git a/main.go b/main.go index 68951eb..1004071 100644 --- a/main.go +++ b/main.go @@ -50,71 +50,81 @@ var KeyMapping = []ebiten.Key{ ebiten.KeyV, } -type UI struct { - Emulator *chip8.Emulator - State State - AudioContext *audio.Context +type GUI struct { + State State + Emulator *chip8.Emulator } -func (ui *UI) Run() { +func (gui *GUI) Run() { for { - ui.Emulator.Cycle() + gui.Emulator.Cycle() time.Sleep(time.Millisecond * 2) } } -func (ui *UI) Update() error { +func (gui *GUI) Update() error { if ebiten.IsKeyPressed(ebiten.KeyEscape) { - ui.Emulator.Reset() + gui.Emulator.Reset() } - - switch ui.State { + switch gui.State { case LoadingState: - go ui.Run() - ui.State = RunningState + go gui.Run() + gui.State = RunningState case RunningState: - ui.Emulator.UpdateTimers() + gui.Emulator.UpdateTimers() } return nil } -func (ui *UI) Draw(screen *ebiten.Image) { - frame := ebiten.NewImageFromImage(ui.Emulator.Display) - +func (gui *GUI) Draw(screen *ebiten.Image) { + frame := ebiten.NewImageFromImage(gui.Emulator.Display) operation := new(ebiten.DrawImageOptions) operation.GeoM.Scale(ScreenScale, ScreenScale) - screen.DrawImage(frame, operation) } -func (ui *UI) Layout(int, int) (int, int) { +func (gui *GUI) Layout(int, int) (int, int) { return Width, Height } -func (ui *UI) KeyPressed(key uint8) bool { +func KeyPressed(key uint8) bool { return ebiten.IsKeyPressed(KeyMapping[key]) } -func (ui *UI) PlaySound(sound []byte) func() { - player := ui.AudioContext.NewPlayerFromBytes(sound) +func SoundPlayer(sound []byte) (func(), func()) { + player := audio.NewContext(chip8.SampleRate).NewPlayerFromBytes(sound) player.SetVolume(0.3) - player.Play() - return func() { _ = player.Close() } + return PlaySound(player), StopSound(player) +} + +func PlaySound(player *audio.Player) func() { + return func() { + if player.IsPlaying() { + return + } + player.Play() + } +} + +func StopSound(player *audio.Player) func() { + return func() { + player.Pause() + player.Seek(0) + } } func main() { rom := LoadROM() - ui := UI{} - ui.Emulator = chip8.NewEmulator(ui.KeyPressed, ui.PlaySound) - ui.State = LoadingState - ui.AudioContext = audio.NewContext(44100) - ui.Emulator.LoadROM(rom) + gui := GUI{} + gui.State = LoadingState + gui.Emulator = chip8.NewEmulator(KeyPressed, SoundPlayer) + gui.Emulator.LoadROM(rom) ebiten.SetWindowSize(Width, Height) ebiten.SetWindowTitle("CHIP-8 : " + rom.Name) - if err := ebiten.RunGame(&ui); err != nil { + if err := ebiten.RunGame(&gui); err != nil { log.Fatal(err) } }