This commit is contained in:
kris 2019-06-19 22:16:36 +01:00
commit e3cb6fe7d1
5 changed files with 27 additions and 39 deletions

View File

@ -15,11 +15,14 @@ class Audio:
# TODO: take into account that the available range is slightly offset # TODO: take into account that the available range is slightly offset
# as fraction of total cycle count? # as fraction of total cycle count?
self._tick_range = [4, 66] self._tick_range = [4, 66]
self.cycles_per_tick = 73 # type: int
# TODO: round to divisor of video frame rate # At 73 cycles/tick, true audio playback sample rate is
self.sample_rate = 14340 # int(1024. * 1024 / # roughly 1024*1024/73 = 14364 Hz (ignoring ACK slow path).
# self.cycles_per_tick) # Typical audio encoding is 44100Hz which is close to 14700*3
# Downscaling by 3x gives better results than trying to resample
# to a non-divisor. So we cheat a bit and play back the video a tiny
# bit (<2%) faster.
self.sample_rate = 14700. # type: float
self.normalization = ( self.normalization = (
normalization or self._normalization()) # type: float normalization or self._normalization()) # type: float

View File

@ -7,35 +7,18 @@ import numpy as np
import screen import screen
class CycleCounter:
"""Counts clock cycles."""
def __init__(self):
self.cycles = 0 # type:int
def tick(self, cycles: int) -> None:
"""Advance cycle counter by some number of clock ticks.
:param cycles: How many clock cycles to advance
"""
self.cycles += cycles
def reset(self) -> None:
"""Reset cycle counter to 0."""
self.cycles = 0
class Machine: class Machine:
"""Represents Apple II and player virtual machine state.""" """Represents Apple II and player virtual machine state."""
def __init__(self, cycle_counter: CycleCounter, def __init__(
memmap: screen.MemoryMap, update_priority: np.array): self,
memmap: screen.MemoryMap,
update_priority: np.array
):
self.page = 0x20 # type: int self.page = 0x20 # type: int
self.content = 0x7f # type: int self.content = 0x7f # type: int
self.memmap = memmap # type: screen.MemoryMap self.memmap = memmap # type: screen.MemoryMap
self.cycle_counter = cycle_counter # type: CycleCounter
self.update_priority = update_priority # type: np.array self.update_priority = update_priority # type: np.array
def emit(self, opcode: "Opcode") -> Iterator[int]: def emit(self, opcode: "Opcode") -> Iterator[int]:

View File

@ -33,19 +33,16 @@ class Movie:
filename, mode=video_mode, palette=self.palette) filename, mode=video_mode, palette=self.palette)
self.video = video.Video( self.video = video.Video(
self.frame_grabber, self.frame_grabber,
ticks_per_second=self.audio.sample_rate,
mode=video_mode, mode=video_mode,
palette=self.palette palette=self.palette
) # type: video.Video ) # type: video.Video
self.stream_pos = 0 # type: int self.stream_pos = 0 # type: int
# TODO: don't use this as well as cycle_counter, it's a relic of when self.ticks = 0 # type: int
# I relied on variable-duration opcodes for frame timings.
self.cycles = 0 # type: int
self.cycle_counter = machine.CycleCounter()
self.state = machine.Machine( self.state = machine.Machine(
self.cycle_counter,
self.video.memory_map, self.video.memory_map,
self.video.update_priority self.video.update_priority
) )
@ -64,9 +61,13 @@ class Movie:
yield opcodes.Header(mode=self.video_mode) yield opcodes.Header(mode=self.video_mode)
for au in self.audio.audio_stream(): for au in self.audio.audio_stream():
self.cycles += self.audio.cycles_per_tick self.ticks += 1
if self.video.tick(self.cycles): if self.video.tick(self.ticks):
try:
main, aux = next(video_frames) main, aux = next(video_frames)
except StopIteration:
break
if ((self.video.frame_number - 1) % self.every_n_video_frames if ((self.video.frame_number - 1) % self.every_n_video_frames
== 0): == 0):
print("Starting frame %d" % self.video.frame_number) print("Starting frame %d" % self.video.frame_number)

View File

@ -22,14 +22,15 @@ class Video:
def __init__( def __init__(
self, self,
frame_grabber: FrameGrabber, frame_grabber: FrameGrabber,
ticks_per_second: int,
mode: VideoMode = VideoMode.HGR, mode: VideoMode = VideoMode.HGR,
palette: Palette = Palette.NTSC, palette: Palette = Palette.NTSC
): ):
self.mode = mode # type: VideoMode self.mode = mode # type: VideoMode
self.frame_grabber = frame_grabber # type: FrameGrabber self.frame_grabber = frame_grabber # type: FrameGrabber
self.cycles_per_frame = ( self.ticks_per_second = float(ticks_per_second) # type: float
self.CLOCK_SPEED / frame_grabber.input_frame_rate self.ticks_per_frame = (
) # type: float self.ticks_per_second / frame_grabber.input_frame_rate) # type: float
self.frame_number = 0 # type: int self.frame_number = 0 # type: int
self.palette = palette # type: Palette self.palette = palette # type: Palette

View File

@ -12,7 +12,7 @@ import video_mode
class TestVideo(unittest.TestCase): class TestVideo(unittest.TestCase):
def test_diff_weights(self): def test_diff_weights(self):
fs = frame_grabber.FrameGrabber(mode=video_mode.VideoMode.DHGR) fs = frame_grabber.FrameGrabber(mode=video_mode.VideoMode.DHGR)
v = video.Video(fs, mode=video_mode.VideoMode.DHGR) v = video.Video(fs, ticks_per_second=10000, mode=video_mode.VideoMode.DHGR)
frame = screen.MemoryMap(screen_page=1) frame = screen.MemoryMap(screen_page=1)
frame.page_offset[0, 0] = 0b1111111 frame.page_offset[0, 0] = 0b1111111