Simplify the accounting of audio ticks in video framing, and fix an off-by-one that was causing audio and video to become desynchronized.

Switch from 14340Hz to 14700Hz, which is 44100/3 i.e. a divisor of the
most common audio input sample rate.  This gives better quality
(and/or faster to encode) audio quality at the cost of producing <2%
faster playback.
This commit is contained in:
kris 2019-06-19 21:47:49 +01:00
parent 32bb4347ad
commit bd0aa6ad6b
3 changed files with 21 additions and 13 deletions

View File

@ -15,11 +15,14 @@ class Audio:
# TODO: take into account that the available range is slightly offset
# as fraction of total cycle count?
self._tick_range = [4, 66]
self.cycles_per_tick = 73 # type: int
# TODO: round to divisor of video frame rate
self.sample_rate = 14340 # int(1024. * 1024 /
# self.cycles_per_tick)
# At 73 cycles/tick, true audio playback sample rate is
# roughly 1024*1024/73 = 14364 Hz (ignoring ACK slow path).
# 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 = (
normalization or self._normalization()) # type: float

View File

@ -23,13 +23,16 @@ class Movie:
self.audio = audio.Audio(
filename, normalization=audio_normalization) # type: audio.Audio
self.video = video.Video(filename, mode=video_mode) # type: video.Video
self.video = video.Video(
filename, mode=video_mode,
ticks_per_second=self.audio.sample_rate
) # type: video.Video
self.stream_pos = 0 # type: int
# TODO: don't use this as well as cycle_counter, it's a relic of when
# I relied on variable-duration opcodes for frame timings.
self.cycles = 0 # type: int
self.ticks = 0 # type: int
self.cycle_counter = machine.CycleCounter()
self.state = machine.Machine(
@ -52,8 +55,8 @@ class Movie:
yield opcodes.Header(mode=self.video_mode)
for au in self.audio.audio_stream():
self.cycles += self.audio.cycles_per_tick
if self.video.tick(self.cycles):
self.ticks += 1
if self.video.tick(self.ticks):
main, aux = next(video_frames)
if ((self.video.frame_number - 1) % self.every_n_video_frames
== 0):

View File

@ -32,10 +32,12 @@ class Video:
def __init__(
self,
filename: str,
mode: Mode = Mode.HGR
ticks_per_second: float,
mode: Mode = Mode.HGR,
):
self.filename = filename # type: str
self.mode = mode # type: Mode
self.ticks_per_second = ticks_per_second # type: float
self._reader = skvideo.io.FFmpegReader(filename)
@ -46,8 +48,8 @@ class Video:
self.input_frame_rate = float(
rate_data[0]) / float(rate_data[1]) # type: float
self.cycles_per_frame = (
self.CLOCK_SPEED / self.input_frame_rate) # type: float
self.ticks_per_frame = (
self.ticks_per_second / self.input_frame_rate) # type: float
self.frame_number = 0 # type: int
# Initialize empty screen
@ -62,8 +64,8 @@ class Video:
if self.mode == mode.DHGR:
self.aux_update_priority = np.zeros((32, 256), dtype=np.int64)
def tick(self, cycles: int) -> bool:
if cycles > (self.cycles_per_frame * self.frame_number):
def tick(self, ticks: int) -> bool:
if ticks >= (self.ticks_per_frame * self.frame_number):
self.frame_number += 1
return True
return False