diff --git a/src/py.rs b/src/py.rs index 4b18b1a7aaa88640e99678cc4d9877c7759f6472..22570d3d2560675b065f15d26d970ad266bca8c5 100644 --- a/src/py.rs +++ b/src/py.rs @@ -117,5 +117,7 @@ fn boytacean(_py: Python, module: &PyModule) -> PyResult<()> { module.add("DISPLAY_WIDTH", DISPLAY_WIDTH)?; module.add("DISPLAY_HEIGHT", DISPLAY_HEIGHT)?; module.add("CPU_FREQ", GameBoyBase::CPU_FREQ)?; + module.add("VISUAL_FREQ", GameBoyBase::VISUAL_FREQ)?; + module.add("LCD_CYCLES", GameBoyBase::LCD_CYCLES)?; Ok(()) } diff --git a/src/python/boytacean/__init__.py b/src/python/boytacean/__init__.py index 1b4517ad7755dda1ef43ee65e4b43fa9675eddc5..033b1adcc958d327f9839bf726449cf930b7d3d6 100644 --- a/src/python/boytacean/__init__.py +++ b/src/python/boytacean/__init__.py @@ -2,6 +2,8 @@ from .gb import ( DISPLAY_WIDTH, DISPLAY_HEIGHT, CPU_FREQ, + VISUAL_FREQ, + LCD_CYCLES, GameBoy, GameBoyMode, GameBoyRust, diff --git a/src/python/boytacean/gb.py b/src/python/boytacean/gb.py index 89c61d2d7011e1bdabffe7757fcccf08b201f633..23b82118287d49fab67d5ff9524388044650786b 100644 --- a/src/python/boytacean/gb.py +++ b/src/python/boytacean/gb.py @@ -7,7 +7,14 @@ from PIL.Image import Image, frombytes from .palettes import PALETTES -from .boytacean import DISPLAY_WIDTH, DISPLAY_HEIGHT, CPU_FREQ, GameBoy as GameBoyRust +from .boytacean import ( + DISPLAY_WIDTH, + DISPLAY_HEIGHT, + CPU_FREQ, + VISUAL_FREQ, + LCD_CYCLES, + GameBoy as GameBoyRust, +) class GameBoyMode(Enum): @@ -34,7 +41,7 @@ class GameBoy: super().__init__() self._frame_index = 0 self._next_frame = None - self._frame_gap = 60 + self._frame_gap = VISUAL_FREQ self._system = GameBoyRust(mode.value) self._system.set_ppu_enabled(ppu_enabled) self._system.set_apu_enabled(apu_enabled) @@ -93,45 +100,36 @@ This is a [Game Boy](https://en.wikipedia.org/wiki/Game_Boy) emulator built usin image = self.image() image.save(filename, format=format) - def set_palette(self, name: str): - if not name in PALETTES: - raise ValueError(f"Unknown palette: {name}") - palette = PALETTES[name] - self.set_palette_colors(palette) - - 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 + def video(self, encoder="H264"): + from cv2 import VideoWriter, VideoWriter_fourcc, imread + from IPython.display import Video images = glob("*.png") - fourcc = cv2.VideoWriter_fourcc(*"H264") - encoder = cv2.VideoWriter( + encoder = VideoWriter( "output.mp4", - fourcc, - 60.0 / self._frame_gap, + VideoWriter_fourcc(*encoder), + VISUAL_FREQ / self._frame_gap, (DISPLAY_WIDTH, DISPLAY_HEIGHT), ) - for image_file in sorted(images): - img = cv2.imread(image_file) - encoder.write(img) + try: + for image_file in sorted(images): + img = imread(image_file) + encoder.write(img) + finally: + encoder.release() - encoder.release() + return Video("output.mp4", embed=True, html_attributes="controls loop autoplay") - from IPython.display import Video + def set_palette(self, name: str): + if not name in PALETTES: + raise ValueError(f"Unknown palette: {name}") + palette = PALETTES[name] + self.set_palette_colors(palette) - return Video("output.mp4", embed=True, html_attributes="controls loop autoplay") + def set_palette_colors(self, colors_hex: str): + self._system.set_palette_colors(colors_hex) @property def ppu_enabled(self) -> bool: @@ -176,9 +174,17 @@ This is a [Game Boy](https://en.wikipedia.org/wiki/Game_Boy) emulator built usin def clock_freq_s(self) -> str: return self._system.clock_freq_s() + @contextmanager + def video_capture(self, fps=5): + self._start_capture(fps=fps) + try: + yield + finally: + self._stop_capture() + 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!!! + self._frame_gap = int(VISUAL_FREQ / fps) def _stop_capture(self): self._next_frame = None