diff --git a/src/python/boytacean/gb.py b/src/python/boytacean/gb.py index 50594902f7462e3715b44ae05a8d059e8cc3da28..89c61d2d7011e1bdabffe7757fcccf08b201f633 100644 --- a/src/python/boytacean/gb.py +++ b/src/python/boytacean/gb.py @@ -1,4 +1,7 @@ from enum import Enum +from os import remove +from glob import glob +from contextlib import contextmanager from PIL.Image import Image, frombytes @@ -14,6 +17,10 @@ class GameBoyMode(Enum): class GameBoy: + _frame_index: int = 0 + _start_frame: int | None + _frame_gap: int + def __init__( self, mode=GameBoyMode.DMG, @@ -25,6 +32,9 @@ class GameBoy: load=True, ): super().__init__() + self._frame_index = 0 + self._next_frame = None + self._frame_gap = 60 self._system = GameBoyRust(mode.value) self._system.set_ppu_enabled(ppu_enabled) self._system.set_apu_enabled(apu_enabled) @@ -64,7 +74,12 @@ This is a [Game Boy](https://en.wikipedia.org/wiki/Game_Boy) emulator built usin return self._system.clocks(count) def next_frame(self) -> int: - return self._system.next_frame() + cycles = self._system.next_frame() + self._frame_index += 1 + if self._next_frame != None and self._frame_index >= self._next_frame: + self._next_frame = self._next_frame + self._frame_gap + self.save_image(f"frame_{self._frame_index}.png") + return cycles def frame_buffer(self): return self._system.frame_buffer() @@ -87,6 +102,37 @@ This is a [Game Boy](https://en.wikipedia.org/wiki/Game_Boy) emulator built usin def set_palette_colors(self, colors_hex: str): self._system.set_palette_colors(colors_hex) + @contextmanager + def video_capture(self, fps=5): + self._start_capture(fps=fps) + try: + yield + finally: + self._stop_capture() + + def video(self): + import cv2 + + images = glob("*.png") + fourcc = cv2.VideoWriter_fourcc(*"H264") + + encoder = cv2.VideoWriter( + "output.mp4", + fourcc, + 60.0 / self._frame_gap, + (DISPLAY_WIDTH, DISPLAY_HEIGHT), + ) + + for image_file in sorted(images): + img = cv2.imread(image_file) + encoder.write(img) + + encoder.release() + + from IPython.display import Video + + return Video("output.mp4", embed=True, html_attributes="controls loop autoplay") + @property def ppu_enabled(self) -> bool: return self._system.ppu_enabled() @@ -129,3 +175,12 @@ This is a [Game Boy](https://en.wikipedia.org/wiki/Game_Boy) emulator built usin @property def clock_freq_s(self) -> str: return self._system.clock_freq_s() + + def _start_capture(self, fps=5): + self._next_frame = self._frame_index + self._frame_gap + self._frame_gap = int(60.0 / fps) # @TODO: This is not accurate!!! + + def _stop_capture(self): + self._next_frame = None + for file in glob("*.png"): + remove(file)