ChiptuneSfxPlayer
Extends AudioStreamPlayer. Read from the plugin's source on every build.
A sound the game fires, like a jump, a hit or a coin. An AudioStreamPlayer
that plays a .sfx.tres from the SFX editor.
Set document and it plays. What a game uses, by need:
Fire it. play, or play_at for a pitch per shot: a
coin that climbs a step with each pickup.
Repeats that differ. variants takes that many
variations in turn, and pitch_variance rolls the pitch of each play.
The engine picks both on every play, from autoplay or an animation track
too.
The same variety every run. seed picks which variations.
One node’s own copy. unique gives this node its own copy of
the document, so a script’s edit to it is heard here alone.
ChiptuneSfxPlayer2D is this node placed in 2D. A .music.tres plays on
ChiptuneSongPlayer. For 3D, or any other slot, put a ChiptuneAudioStream
on the player: Godot’s own AudioStreamPlayer3D, say.
stream is built from document, and is neither shown nor
saved.
play() and stop() are AudioStreamPlayer’s.
Properties
document
@export_custom(PROPERTY_HINT_RESOURCE_TYPE, "ChiptuneSfxSpec") var document: ResourceThe .sfx.tres to play.
Hinted to ChiptuneSfxSpec alone so the picker and its Quick Load search
offer sounds and not every resource in the project. A song belongs on a
ChiptuneSongPlayer, which has the loop. The hint is an editor filter and
not the contract: anything answering render() still plays when it is
assigned from code or written into a scene by hand, a song included. See
ChiptuneAudioStream.
unique
@export var unique := falseWhether this node plays its own copy of document, apart from the
file every other node of it shares.
Off, a script’s edit to document is heard on every node holding
the same file. On, the copy is made when the document is assigned, so
document.instrument.decay = 0.1 changes this node alone; the
scene still stores the file. A save of the file while the game runs copies
it again, and the node’s own edits go with it. In the editor the node holds
the file, so the inspector shows what the scene stores.
Declared after document, so the file comes first in the inspector.
A scene the editor saves sets it after the document has rendered, and the
copy it makes then sounds the same, so it is held without a second render.
pitch_variance
@export_range(0, 12) var pitch_variance := 0How far each play may wander from the pitch it was asked for, in semitones either way, rolled per play. Zero plays it where it is.
Nothing is rendered for it, so it is free; a sound played higher is also shorter, as any sample is.
variants
@export_range(1, 8) var variants := 1How many versions of the sound this node takes in turn, one per play: at
one, the document as it is; above one, that many variations on it, each a
ChiptuneSfx.nudge of the document on the document’s note,
rendered once when it loads and again when it changes.
Each variant keeps the wave, the arpeggio and the note and nudges every other number a little, so a hit is not the same hit twice.
seed
@export var seed := 0Which variations variants makes: the same seed is the same set of
sounds, taken in the same order.
Methods
play_at
func play_at(pitch: int, from_position := 0.0) -> voidPlays the sound at the MIDI note pitch, sixty being middle C, by
setting AudioStreamPlayer.pitch_scale from how far it is from the
document’s own ChiptuneSfxSpec.pitch. Nothing is rendered, so a
coin that climbs a step per pickup costs what play does.
The pitch_scale goes back to what it was when the sound ends, or at the
next play() made through this node’s script. A
play() the engine takes directly while the sound is still
sounding, from a variable typed ChiptuneSfxPlayer or autoplay, plays at
the note asked for here.
play
func play(from_position := 0.0) -> voidAudioStreamPlayer.play, back at the document’s pitch after a
play_at. Only that: the variants and the spread are picked by the
engine, on every play.
rebuild
func rebuild() -> voidRenders document now, in this frame, and leaves a playing node
playing from where it had got to.
A change to the document is heard without this: the stream follows the
document’s changed and renders once on the next idle frame, however many
fields moved. This is for the frame the change was made in, a pitch set
and the sound played on the same line.
The audio is written into the stream the node already has, so anything holding this node’s stream keeps the resource it was handed. It is a full render on the main thread: see “What it costs” in the module README before calling it every frame.
Signals
document_changed
signal document_changedEmitted after new audio for document lands in this node’s stream,
whatever asked for it: the document changing, rebuild, a save.
JamChip, by GlitterSnack. MIT licence. Built from the source on every merge. Its artwork and screenshots are made with JamChip.