television

package
v0.31.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 5, 2024 License: GPL-3.0 Imports: 11 Imported by: 3

Documentation

Overview

Package television implements the output device of the emulated VCS.

It is possible for instances of television to be embedded in other type structure, thereby extending the "features" of the television and allowing the extended type to be used wherever the Television interface is required. The digest package is a good example of this idea.

It is important to note that the television package does not render pixels or mix sound. Instead, the television interface exposes two functions, AddPixelRenderer() and AddAudioMixer(). These can be used to add as many renderers and mixers as required.

For audio outputs that are time sensitive, the AddRealtimeAudioMixer() function should be used. This requires a slightly different implementation (see RealtimeAudioMixer interface) and only one such implemenation can be registered at any one time.

There is also the FrameTrigger and PauseTrigger interfaces for applications that have limited need for a full pixel renderer.

The main means of communication is the Signal() function. This function accepts an instance of SignalAttributes which gives details of how the television should be behaving.

Note that the television implementation no longer attempts to report the same frame/scanline/clock information as Stella. Early versions of the implementation did because it facilitated A/B testing but since we're now confident that the TIA emulation is correct the need to keep in "sync" with Stella is no longer required.

The reference implementation also handles framerate limiting according to the current incoming TV signal. For debugging purposes, the framerate can also be set to a specific value

Framesize adaptation is also handled by the television package. Current information about the frame can be acquired with GetFrameInfo(). FrameInfo will also be sent to the PixelRenderers as appropriate.

Screen Rolling

Screen rolling is not handled by the television package. However, the synced argument of the NewFrame() function in the PixelRenderer and FrameTrigger interfaces can be used to implement it if required. Something like this:

  1. If Synced is false, note scanline of last plot (unsynedScanline)
  2. For every SetPixel() add unsyncedScanline to the Scanline value in the SignalAttributes struct (adjustedScanline)
  3. Bring adjustedScanline into range by modulo ScanlinesTotal of the current TV specification.

Recovery from a screen roll should also be emulated. A good way of doing this is to reduce unsyncedScanline by a percentage (80% say) on synced frames (or every other synced frame) after an unsynced frame.

A good additionl policy would be to only roll if several, consecutive unsynced frames are indicated.

Concurrency

None of the functions in the Television type are safe to be called from goroutines other than the one the type was created in.

Logging

The television does no logging. This is because the television can be used ephemerally and logging would be noisy. Callers of television package functions should decide whether it is appropriate to log.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Adj added in v0.15.0

type Adj int

Adj is used to specify adjustment scale for the ReqAdjust() function.

const (
	AdjFrame Adj = iota
	AdjScanline
	AdjCycle
	AdjClock
)

List of valid Adj values.

type AudioMixer

type AudioMixer interface {
	// for efficiency reasons, SetAudio() implementations can be sent
	// SignalAttributes values that do not have valid AudioData (ie.
	// AudioUpdate bit is zero). implemenations should therefore take care when
	// processing the sig slice
	//
	// the general algorithm for processing the sig slice is:
	//
	//	for _, s := range sig {
	//		if s&signal.AudioUpdate != signal.AudioUpdate {
	//			continue
	//		}
	//		d := uint8((s & signal.AudioData) >> signal.AudioDataShift)
	//
	//		...
	//	}
	SetAudio(sig []signal.SignalAttributes) error

	// some mixers may need to conclude and/or dispose of resources gently.
	// for simplicity, the AudioMixer should be considered unusable after
	// EndMixing() has been called
	EndMixing() error

	// Reset buffered audio and anything else that might need doing on, for
	// example, a cartridge change
	Reset()
}

AudioMixer implementations work with sound; most probably playing it. An example of an AudioMixer that does not play sound but otherwise works with it is the digest.Audio type

type FrameInfo added in v0.14.0

type FrameInfo struct {
	Spec specification.Spec

	FrameNum int

	// the top and bottom scanlines that are to be present visually to the
	// player. this is generally related to the state of VBLANK but in the case
	// of screens when the VBLANK is never set, the visible area is determined by
	// the extent of non-black output
	//
	// consumers of FrameInfo should use these values rather than deriving that
	// information from VBLANK
	//
	// see the Crop() function for the preferred way of using these values to
	// create the a rectangle of the visible screen area. in particular, note
	// how the VisibleBottom value is treated in that context
	VisibleTop    int
	VisibleBottom int

	// the number of scanlines considered to be in the frame. the number of
	// scanlines that are actually in the frame may actually be less or more.
	// this can happen when a the refresh rate is changing, for example.
	//
	// note therefore, that the refresh rate can change but the reported number
	// of total scanlines not changing at the same time. the practical
	// consequence of this is that it is possible for there to be more
	// scanlines in the signals slice sent to the PixelRenderer via the
	// SetPixels() function
	TotalScanlines int

	// the refresh rate. this value is derived from the number of scanlines in
	// the frame but note that that may not be equal to the TotalScanlines
	// field
	RefreshRate float32

	// has the refresh rate changed since the previous frame
	Jitter bool

	// a VSync frame is one which was generated from a valid VSYNC/VBLANK
	// sequence and which hasn't cause the update frequency of the television
	// to change.
	VSync bool

	// the number of scanlines in the VSync. value is not meaningful if VSync
	// is false
	VSyncScanlines int

	// Stable is true once the television frame has been consistent for N
	// frames after reset. This is useful for pixel renderers that don't want
	// to show the loose frames that often occur after VCS hard-reset.
	//
	// once Stable is true then the Specification will not change (except
	// manually). This is important for ROMs that allow the VCS to run without
	// VSYNC - we don't want those ROMs to change the specifciation after he
	// startup period. A good example of such a ROM is Andrew Davie's 3e+ chess
	// demos.
	Stable bool
}

FrameInfo records the current frame information, as opposed to the optimal values of the specification, a copy of which is provided as reference.

func NewFrameInfo added in v0.14.0

func NewFrameInfo(spec specification.Spec) FrameInfo

NewFrameInfo returns an initialised FrameInfo for the specification.

func (FrameInfo) Crop added in v0.29.0

func (info FrameInfo) Crop() image.Rectangle

Crop returns an image.Rectangle for the cropped region of the screen. Using this is preferrable than using the VisibleTop/Bottom fields to construct the rectangle

If the VisibleTop/Bottom fields are used in preference to this function for whatever reason, bear in mind that the VisibleBottom field should be adjusted by +1 in order to include the all visible scanlines in the rectangle

To prove the need for this, consider what would happen if the screen was one scanline tall. In that case both the top and bottom values would be the same:

r := image.Rect(0, 10, 100, 10)

The height of this rectangle will be zero, as shown by the Size() function

isZero := r.Size().Y == 0

func (FrameInfo) IsAtariSafe added in v0.14.0

func (info FrameInfo) IsAtariSafe() bool

IsAtariSafe returns true if the current frame matches the AtariSafe values of the current specification.

func (FrameInfo) IsDifferent added in v0.29.0

func (info FrameInfo) IsDifferent(cmp FrameInfo) bool

IsDifferent returns true if any of the pertinent display information is different between the two copies of FrameInfo

func (FrameInfo) OverscanClocks added in v0.19.3

func (info FrameInfo) OverscanClocks() int

OverscanClocks returns the number of clocks in the Overscan portion of the frame.

func (FrameInfo) ScreenClocks added in v0.19.3

func (info FrameInfo) ScreenClocks() int

ScreenClocks returns the number of clocks in the visible portion of the frame.

func (FrameInfo) String added in v0.14.0

func (info FrameInfo) String() string

func (FrameInfo) TotalClocks added in v0.18.0

func (info FrameInfo) TotalClocks() int

TotalClocks returns the total number of clocks required to generate the frame. The value returned assumes scanlines are complete - which may not be the case.

func (FrameInfo) VBLANKClocks added in v0.19.3

func (info FrameInfo) VBLANKClocks() int

VBLANKClocks returns the number of clocks in the VBLANK portion of the frame.

type FrameTrigger

type FrameTrigger interface {
	NewFrame(FrameInfo) error
}

FrameTrigger implementations listen for NewFrame events. FrameTrigger is a subset of PixelRenderer

type PauseTrigger added in v0.10.1

type PauseTrigger interface {
	Pause(pause bool) error
}

FrameTrigger implementations listen for Pause events

type PixelRenderer

type PixelRenderer interface {
	// NewFrame is called at the start of a new frame
	//
	// Renderers should be prepared to resize the rendering display to either a
	// smaller or larger frame size
	NewFrame(FrameInfo) error

	// NewScanline is called at the start of a new scanline
	NewScanline(scanline int) error

	// SetPixels is used to Render a series of signals. The number of signals
	// will always be television.MaxSignalHistory
	//
	// Producing a 2d image from the signals sent by SetPixels() can easily be
	// done by first allocating a bitmap of width specification.ClksScanline
	// and height specification.AbsoluateMaxScanlines. This bitmap will have
	// television.MaxSignalHistory entries
	//
	// Every signal from SetPixels() therefore corresponds to a pixel in the
	// bitmap - the first entry always referes to the top-left pixel
	//
	// Setting the color of a pixel can be done by extracting the ColorSignal
	// from the SignalAttributes (see signal package)
	//
	// The last parameter indicates the index of the array entry that was most
	// recently set. This is useful to know when showing televison images when
	// the emulation is paused. All entries upto and including last are from
	// teh *current* frame. All entries afterwards are from the *previous*
	// frame
	//
	// If the entry contains signal.NoSignal then that screen pixel has not
	// been written to recently
	//
	// For renderers that are producing an accurate visual image, the pixel
	// should always be set to video black if VBLANK is on. Some renderers
	// however may find it useful to set the pixel to the RGB value regardless
	// of VBLANK
	//
	// A very important note is that some ROMs use VBLANK to control pixel
	// color within the visible display area. For example:
	//
	//	* Custer's Revenge
	//	* Ladybug
	//	* ET (turns VBLANK off late on scanline 40)
	//
	// In other words, the PixelRenderer should not simply assume VBLANK is
	// restricted to the "off-screen" areas as defined by the FrameInfo sent to
	// Resize()
	SetPixels(sig []signal.SignalAttributes, last int) error

	// Reset all pixels. Called when TV is reset.
	//
	// Note that a Reset event does not imply a Resize() event. Implementations
	// should not call the Resize() function as a byproduct of a Reset(). The
	// television will send an explicit Resize() request if it is appropriate
	Reset()

	// Some renderers may need to conclude and/or dispose of resources gently
	EndRendering() error
}

PixelRenderer implementations displays, or otherwise works with, visual information from a television. For example digest.Video

PixelRenderer implementations often find it convenient to maintain a reference to the parent Television implementation and maybe even embed the Television interface. ie.

type ExampleTV struct {
	television.Television
	...
}

The most useful source of information though is the FrameInfo type supplied to the PixelRenderer through the Resize() and NewFrame() functions. A current copy of this information is also available from the television type GetFrameInfo() function

type PixelRendererFPSCap added in v0.23.0

type PixelRendererFPSCap interface {
	SetFPSCap(limit bool)
}

PixelRendererFPSCap is an extension to the PixelRenderer interface. Pixel renderers that implement this interface will be notified when the television's frame capping policy is changed. Not all pixel renderers need to worry about frame rate.

type PixelRendererRotation added in v0.26.0

type PixelRendererRotation interface {
	SetRotation(specification.Rotation)
}

PixelRendererRotation is an extension to the PixelRenderer interface. Pixel renderes that implement this interface can show the television image in a rotated aspect. Not all pixel renderers need to worry about rotation.

type RealtimeAudioMixer added in v0.15.0

type RealtimeAudioMixer interface {
	AudioMixer
	MoreAudio() bool
}

RealtimeAudioMixer is an extension for the AudioMixer interface. Implementations of this interface expect to be given more audio data on demand

MoreAudio() is called periodically (every scanline) and the implementation should return true if more audio data is required immediately

type ScanlineTrigger added in v0.19.3

type ScanlineTrigger interface {
	NewScanline(FrameInfo) error
}

ScanlineTrigger implementations listen for NewScanline events. It is a subset of PixelRenderer

type State

type State struct {
	// contains filtered or unexported fields
}

State encapsulates the television values that can change from moment to moment. Used by the rewind system when recording the current television state.

func (*State) GetCoords added in v0.15.0

func (s *State) GetCoords() coords.TelevisionCoords

GetCoords returns an instance of coords.TelevisionCoords.

func (*State) GetFrameInfo added in v0.25.0

func (s *State) GetFrameInfo() FrameInfo

GetFrameInfo returns the television's current frame information.

func (*State) GetLastSignal added in v0.25.0

func (s *State) GetLastSignal() signal.SignalAttributes

GetLastSignal returns a copy of the most SignalAttributes sent to the TV (via the Signal() function).

func (*State) Snapshot

func (s *State) Snapshot() *State

Snapshot makes a copy of the television state.

func (*State) String added in v0.10.1

func (s *State) String() string

type Television

type Television struct {
	// contains filtered or unexported fields
}

Television is a Television implementation of the Television interface. In all honesty, it's most likely the only implementation required.

func NewTelevision

func NewTelevision(spec string) (*Television, error)

NewTelevision creates a new instance of the television type, satisfying the Television interface.

func (*Television) AddAudioMixer

func (tv *Television) AddAudioMixer(m AudioMixer)

AddAudioMixer adds an implementation of AudioMixer.

func (*Television) AddFrameTrigger

func (tv *Television) AddFrameTrigger(f FrameTrigger)

AddFrameTrigger adds an implementation of FrameTrigger.

func (*Television) AddPixelRenderer

func (tv *Television) AddPixelRenderer(r PixelRenderer)

AddPixelRenderer adds an implementation of PixelRenderer.

func (*Television) AddRealtimeAudioMixer added in v0.15.0

func (tv *Television) AddRealtimeAudioMixer(m RealtimeAudioMixer)

AddRealtimeAudioMixer adds a RealtimeAudioMixer. Any previous assignment is lost.

func (*Television) AddScanlineTrigger added in v0.19.3

func (tv *Television) AddScanlineTrigger(f ScanlineTrigger)

AddScanlineTrigger adds an implementation of ScanlineTrigger.

func (*Television) AdjCoords added in v0.15.0

func (tv *Television) AdjCoords(adj Adj, amount int) coords.TelevisionCoords

AdjCoords returns a coords.TelevisionCoords with the current coords adjusted by the specified amount.

func (*Television) AttachVCS added in v0.8.0

func (tv *Television) AttachVCS(env *environment.Environment, vcs VCSReturnChannel)

AttachVCS attaches an implementation of the VCSReturnChannel.

func (*Television) End

func (tv *Television) End() error

some televisions may need to conclude and/or dispose of resources gently. implementations of End() should call EndRendering() and EndMixing() on each PixelRenderer and AudioMixer that has been added.

for simplicity, the Television should be considered unusable after EndRendering() has been called.

func (*Television) GetActualFPS

func (tv *Television) GetActualFPS() (float32, float32)

GetActualFPS returns the current number of frames per second and the detected frequency of the TV signal.

Note that FPS measurement still works even when frame capping is disabled.

IS goroutine safe.

func (*Television) GetCoords added in v0.15.0

func (tv *Television) GetCoords() coords.TelevisionCoords

GetCoords returns an instance of coords.TelevisionCoords.

Like all Television functions this function is not safe to call from goroutines other than the one that created the Television.

func (*Television) GetFrameInfo added in v0.14.0

func (tv *Television) GetFrameInfo() FrameInfo

GetFrameInfo returns the television's current frame information.

func (*Television) GetLastSignal

func (tv *Television) GetLastSignal() signal.SignalAttributes

GetLastSignal returns a copy of the most SignalAttributes sent to the TV (via the Signal() function).

func (*Television) GetReqFPS

func (tv *Television) GetReqFPS() float32

GetReqFPS returns the requested number of frames per second. Compare with GetActualFPS() to check for accuracy.

IS goroutine safe.

func (*Television) GetReqSpecID

func (tv *Television) GetReqSpecID() string

GetReqSpecID returns the specification that was requested on creation.

func (*Television) GetSpecID added in v0.26.0

func (tv *Television) GetSpecID() string

GetSpecID returns the current specification.

func (*Television) NudgeFPSCap added in v0.23.0

func (tv *Television) NudgeFPSCap(frames int)

NudgeFPSCap stops the FPS limiter for the specified number of frames. A value of zero (or less) will stop any existing nudge

func (*Television) Plumb

func (tv *Television) Plumb(vcs VCSReturnChannel, state *State)

Plumb attaches an existing television state.

func (*Television) RemoveAudioMixer added in v0.16.0

func (tv *Television) RemoveAudioMixer(m AudioMixer)

RemoveAudioMixer removes a single AudioMixer implementation from the list of mixers. Order is not maintained.

func (*Television) RemoveFrameTrigger added in v0.16.0

func (tv *Television) RemoveFrameTrigger(f FrameTrigger)

RemoveFrameTrigger removes a single FrameTrigger implementation from the list of triggers. Order is not maintained.

func (*Television) RemovePixelRenderer added in v0.16.0

func (tv *Television) RemovePixelRenderer(r PixelRenderer)

RemovePixelRenderer removes a single PixelRenderer implementation from the list of renderers. Order is not maintained.

func (*Television) RemoveRealtimeAudioMixer added in v0.16.0

func (tv *Television) RemoveRealtimeAudioMixer(m RealtimeAudioMixer)

RemoveRealtimeAudioMixer removes a RealtimeAudioMixer implementation from the Television.

func (*Television) RemoveScanlineTrigger added in v0.19.3

func (tv *Television) RemoveScanlineTrigger(f ScanlineTrigger)

RemoveScanlineTrigger removes a single ScanlineTrigger implementation from the list of triggers. Order is not maintained.

func (*Television) Reset

func (tv *Television) Reset(keepFrameNum bool) error

Reset the television to an initial state.

func (*Television) SetEmulationState added in v0.15.0

func (tv *Television) SetEmulationState(state govern.State) error

SetEmulationState is called by emulation whenever state changes. How we handle incoming signals depends on the current state.

func (*Television) SetFPS

func (tv *Television) SetFPS(fps float32)

SetFPS requests the number frames per second. This overrides the frame rate of the specification. A negative value restores frame rate to the ideal value (the frequency of the incoming signal).

func (*Television) SetFPSCap

func (tv *Television) SetFPSCap(limit bool) bool

SetFPSCap whether the emulation should wait for FPS limiter. Returns the setting as it was previously.

func (*Television) SetRotation added in v0.26.0

func (tv *Television) SetRotation(rotation specification.Rotation)

SetRotation instructs the television to a different orientation. In truth, the television just forwards the request to the pixel renderers.

func (*Television) SetSpec

func (tv *Television) SetSpec(spec string) error

SetSpec sets the television's specification. Will return an error if specification is not recognised.

Currently supported NTSC, PAL, PAL60 and AUTO. The empty string behaves like "AUTO".

func (*Television) SetSpecConditional added in v0.12.1

func (tv *Television) SetSpecConditional(spec string) error

SetSpecConditional sets the television's specification if the original specification (not the current spec, the original) is "AUTO".

This is used when attaching a cartridge to the VCS and also when processing setup entries (see setup package, particularly the TV type).

func (*Television) SetVisible added in v0.29.0

func (tv *Television) SetVisible(info FrameInfo)

SetVisible uses the supplied FrameInfo to set the visible boundaries for the television. This is useful if the visible area of the ROM is known ahead of time and can help prevent ugly resizing in a pixel renderer

func (*Television) Signal

func (tv *Television) Signal(sig signal.SignalAttributes)

Signal updates the current state of the television.

func (*Television) Snapshot

func (tv *Television) Snapshot() *State

Snapshot makes a copy of the television state.

func (*Television) String

func (tv *Television) String() string

type VCSReturnChannel added in v0.8.0

type VCSReturnChannel interface {
	SetClockSpeed(specification.Spec)
}

VCSReturnChannel is used to send information from the TV back to the parent console. Named because I think of it as being similar to the Audio Return Channel (ARC) present in modern TVs

Directories

Path Synopsis
Package coords represents and can work with television coorindates
Package coords represents and can work with television coorindates
Package signal exposes the interface between the VCS and the television implementation.
Package signal exposes the interface between the VCS and the television implementation.
Package specification contains the definitions, including colour, of the PAL and NTSC television protocols supported by the emulation.
Package specification contains the definitions, including colour, of the PAL and NTSC television protocols supported by the emulation.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL