# Voice¶

This structure represents a single one of the ‘voices’ in the built-in sound chip called SKID in the kOS CPU. Please refer to the SKID chip documentation page if you have not already done so before reading this page. The things mentioned here are just ways to access the features of the SKID chip so they won’t be fully explained on this page, instead just referring to the SKID documentation with links.

Contents

## Functions¶

GETVOICE(num)
Returns: Voice

To access one of the voices of the SKID chip, you use the GetVoice(num) built-in function.

Where num is the number of the hardware voice you’re interested in accessing. (The numbering starts with the first voice being called 0).

STOPALLVOICES()
Returns: None

This will stop all voices. If the voice is scheduled to play additional notes, they will not be played. If the voice in the middle of playing a note, that note will be stopped.

Each voice is capable of playing one note at a time, or a series of notes from a song (a List of Note‘s), but what matters is that one voice can’t play two notes at once. To do that you need to use multiple voices. For simple one-voice situations, you probably only need to ever use voice 0.

## Structure¶

structure Voice
Members
Suffix Type Get/Set/Call Description
ATTACK scalar Get/Set The Attack setting of the SKID voice’s ADSR Envelope
DECAY scalar Get/Set The Decay setting of the SKID voice’s ADSR Envelope
SUSTAIN scalar Get/Set The Sustain setting of the SKID voice’s ADSR Envelope
RELEASE scalar Get/Set The Release setting of the SKID voice’s ADSR Envelope
VOLUME scalar Get/Set The default volume to play the notes on this voice.
WAVE string Get/Set The name for the waveform you want this voice to use.
PLAY(note_or_list) None Call The method that actually causes the voice to make some sound.
STOP() None Call Stop playing note on this voice instance.
LOOP boolean Get/Set Whether or not the voice should keep re-playing the song that was queued with PLAY().
ISPLAYING boolean Get/Set The playing status of voice.
TEMPO scalar Get/Set Stretches or shrinks the duration of the notes to speed up or slow down the song.
Voice:ATTACK
Access: Get/Set Scalar (seconds)

The Attack setting of the SKID voice’s ADSR Envelope. This value is in seconds (usually a fractional portion of a second).

Voice:DECAY
Access: Get/Set Scalar (seconds)

The Decay setting of the SKID voice’s ADSR Envelope. This value is in seconds (usually a fractional portion of a second).

Voice:SUSTAIN
Access: Get/Set Scalar in the range [0..1]

The Sustain setting of the SKID voice’s ADSR Envelope. Unlike the other values in the ASDR Envelope, this setting is NOT a measure of time. This is a coefficient to multiply the volume by during the sustain portion of the notes that are being played on this voice. (i.e. 0.5 would mean “sustain at half volume”).

Voice:RELEASE
Access: Get/Set Scalar (seconds)

The Release setting of the SKID voice’s ADSR Envelope. This value is in seconds (usually a fractional portion of a second). Note, that in order for this setting to have any real effect, the notes that are being played have to have their KeyDownLength set to be shorter than their Duration<Note:DURATION, otherwise the notes will still cut off before the Release has a chance to happen.

Voice:VOLUME
Access: Get/Set Scalar

The “peak” volume of the notes played on this voice, when they hit the top of their initial spike in the ADSR Envelope. While conceptually the max value is 1.0, in practice it can often go higher because the KSP game setting for User Interface volume is usually only at 50%, and in that scenario putting a 1.0 here would put the max at 50%, really. Setting this value to 0 will silence the voice.

Voice:WAVE
Access: Get/Set string

To select which of the SKID chip’s waveform generators you want this voice to use, set this to the string name of that waveform. If you use a string that isn’t one of the ones listed there (i.e. “triangle”, “noise”, “square”, etc) then the attempt to set this value will be ignored and it will remain at its previous value.

Voice:PLAY(note_or_list)
Access: Call (method) note_or_list – Either one Note or a List of Note‘s None

To cause the SKID chip to actually emit a sound, you need to use this suffix method. There are two ways it can be called:

Play just one note : To play a single note, you can call PLAY(), passing it one note object. Usually you construct the note object on the fly as you call Play, like so:

SET V0 to GetVoice(0).
V0:PLAY(NOTE(440,0.5)).


Play a list of notes : To play a full list of notes (which could even encode an entire song), you can call PLAY, passing it a List of Note‘s. It will recognize that it is receiving a list of notes, and begin playing through them one at a time, only playing the next note when the previous note’s DURATION is finished:

SET V0 to GetVoice(0).
V0:PLAY(
LIST(
NOTE(440, 0.5),
NOTE(400, 0.2),
SLIDENOTE(410, 350, 0.3)
)
).


Notes play in the background: In either case, whether playing a single note or a list of notes, the PLAY() method will return immediately, before even the first note has begun playing. It queues the note(s) to play, rather than waiting for them to finish. This lets your main program continue doing its work without waiting for the sound to finish.

Calling PLAY() again on the same voice aborts the previous PLAY(): Because the notes play in the background, it’s possible to execute another PLAY() call while a previous one hasn’t finished its work yet. If you do this, then the previous thing that was playing will quit, to be replaced by the new thing.

But PLAY() can be called simultaneously on different voices: (In fact that’s the whole point of having different voices.). Calling PLAY() again on a different voice number will not abort the previous call to PLAY(). It only aborts the previous PLAY() when it’s being done on the same voice.

Voice:STOP()
Access: Call (method) None

Calling this method will tell the voice to stop playing notes. If there are any notes queued to be played, they will not be played. If a note is currently being played, that note will be stopped.

Voice:LOOP
Access: Get/Set boolean

If this is set to true, then the PLAY() method of this voice will keep on playing the same list of notes continually (starting over with the first note after the last note has finished). Note that for the purpose of this, a play command that was only given a single note to play still counts as a ‘song’ that is one note long (i.e. it will keep repeating the same note continually).

Voice:ISPLAYING
Access: Get/Set boolean

Get: If this voice is currently playing a note or list of notes that was previously passed in to the PLAY() method, then this returns true. Note that if LOOP is true, then this will never become false unless you set it to become false.

Set: If you set this value to FALSE, that will force the voice to stop playing whatever it was playing, and shut it up. (Setting it to true doesn’t really mean anything. It becomes true because the PLAY() method was called. You can’t restart a song just by setting this to true because when it becomes false, the voice “throws away” its memory of the song it was playing.)

Voice:TEMPO
Access: Get/Set scalar

When the voice is playing a Note or (more usefully) a List of Note‘s, it will stretch or shrink the durations of those notes by multiplying them by this scaling factor. At 1.0 (the default), that means that when a note says it lasts for 1 second, then it really does. But if this tempo was set to, say 1.5, then that would mean that each time a note claims it wants to play for 1 second, it would really end up playing for 1.5 seconds on this voice. (or if you set the tempo to 0.5, then all songs will play their notes at double speed (each note only lasting half as long as it “should”).)

In other words, setting this to a value less than 1.0 will speed up the song, and setting it to a value greater than 1.0 will slow it down (which might be the opposite of what you’d expect with it being called “tempo”, but what else should we have called it? “slowpo”?)

Changes to this value take effect as soon as the next note in the song starts. (You do not need to re-run the PLAY() method. It will change the speed in mid-song.)

Be aware that this only scales the timings of the Note‘s KEYDOWNLENGTH and DURATION timings. It does not affect the timings in the ADSR Envelope, as those represent what are meant to be physical properties of the “instrument” the voice is playing on. This means if you set the tempo too fast, it will start cutting off the full duration of the “envelope” of the notes, if you are playing the notes with settings that have a slow attack or decay.

## Example Song¶

Below is a more complex full example that demonstrates the chip a bit more. Type it in (or cut and paste it) to see the system at work:

brakes on.
set song to list().
song:add(note("b4", 0.5 , 0.45)). // lamb,
song:add(note("a4", 0.5 , 0.45)). // lamb
song:add(note("b4", 0.5 , 0.45)). // lamb

song:add(note("g4", 1   , 0.95)). // snow

set v0 to getvoice(0).

set v0:attack to 0.0333. // take 1/30 th of a second to max volume.
set v0:decay to 0.02.  // take 1/50th second to drop back down to sustain.
set v0:sustain to 0.80. // sustain at 80% of max vol.
set v0:release to 0.05. // takes 1/20th of a second to fall to zero volume at the end.

for wavename in LIST("square", "triangle", "sawtooth", "sine") { // Let's not do "noise" - it sounds dumb for music
set v0:wave to wavename.
v0:play(song).
print "Playing song in waveform : " + wavename.
wait until not v0:isplaying.
wait 1.
}