xnano.audio
xnano.audio
¶
xnano.audio
xnano-audio based components and
utilities for sound playback and composition. Needs the optional audio extra:
pip install "xnano[audio]".
Classes:
-
Audio–A sound you can play with
Canvas.playor -
TerminalAudioEffect–An immutable audio effect description.
-
TerminalAudioError–Raised when an audio device or stream fails.
-
TerminalAudioUnavailableError–Raised when an audio device is missing, or this build has no device support.
-
TerminalMelody–Notes on a beat timeline with a tempo map and a time signature.
-
TerminalNote–One note on a beat timeline.
-
TerminalSound–A rendered mono sound: raw samples plus their sample rate.
-
AudioDevice–The canvas's sound: notes, melodies and sounds to play, a musical clock, and the
Functions:
-
instrument_names–Names of every built-in instrument, in catalog order.
Attributes:
-
InstrumentName(TypeAlias) –One of the built-in instruments.
-
NoteName(TypeAlias) –A note by name: a letter, an optional sharp or flat, and an octave (
'C4'is middle C). -
Pitch(TypeAlias) –A pitch: a note name, or a MIDI number from 0 to 127 (60 is middle C).
-
PlaySource(TypeAlias) –Anything
playandCanvas.playaccept: a
InstrumentName
module-attribute
¶
InstrumentName: TypeAlias = Literal['piano', 'electric_piano', 'organ', 'bell', 'marimba', 'guitar', 'electric_guitar', 'pluck', 'bass', 'synth', 'pad', 'strings', 'sine', 'square', 'saw', 'triangle', 'kick', 'snare', 'hihat', 'open_hat', 'clap', 'tom', 'cowbell']
One of the built-in instruments.
NoteName
module-attribute
¶
NoteName: TypeAlias = Literal['C0', 'C#0', 'Db0', 'D0', 'D#0', 'Eb0', 'E0', 'F0', 'F#0', 'Gb0', 'G0', 'G#0', 'Ab0', 'A0', 'A#0', 'Bb0', 'B0', 'C1', 'C#1', 'Db1', 'D1', 'D#1', 'Eb1', 'E1', 'F1', 'F#1', 'Gb1', 'G1', 'G#1', 'Ab1', 'A1', 'A#1', 'Bb1', 'B1', 'C2', 'C#2', 'Db2', 'D2', 'D#2', 'Eb2', 'E2', 'F2', 'F#2', 'Gb2', 'G2', 'G#2', 'Ab2', 'A2', 'A#2', 'Bb2', 'B2', 'C3', 'C#3', 'Db3', 'D3', 'D#3', 'Eb3', 'E3', 'F3', 'F#3', 'Gb3', 'G3', 'G#3', 'Ab3', 'A3', 'A#3', 'Bb3', 'B3', 'C4', 'C#4', 'Db4', 'D4', 'D#4', 'Eb4', 'E4', 'F4', 'F#4', 'Gb4', 'G4', 'G#4', 'Ab4', 'A4', 'A#4', 'Bb4', 'B4', 'C5', 'C#5', 'Db5', 'D5', 'D#5', 'Eb5', 'E5', 'F5', 'F#5', 'Gb5', 'G5', 'G#5', 'Ab5', 'A5', 'A#5', 'Bb5', 'B5', 'C6', 'C#6', 'Db6', 'D6', 'D#6', 'Eb6', 'E6', 'F6', 'F#6', 'Gb6', 'G6', 'G#6', 'Ab6', 'A6', 'A#6', 'Bb6', 'B6', 'C7', 'C#7', 'Db7', 'D7', 'D#7', 'Eb7', 'E7', 'F7', 'F#7', 'Gb7', 'G7', 'G#7', 'Ab7', 'A7', 'A#7', 'Bb7', 'B7', 'C8', 'C#8', 'Db8', 'D8', 'D#8', 'Eb8', 'E8', 'F8', 'F#8', 'Gb8', 'G8', 'G#8', 'Ab8', 'A8', 'A#8', 'Bb8', 'B8']
A note by name: a letter, an optional sharp or flat, and an octave ('C4' is middle C).
Pitch
module-attribute
¶
A pitch: a note name, or a MIDI number from 0 to 127 (60 is middle C).
PlaySource
module-attribute
¶
PlaySource: TypeAlias = 'Playable | int | float | TerminalSound | TerminalNote | TerminalMelody'
Anything play and Canvas.play accept: a
Playable (a Note, an Audio, a symbol
name, a note name such as 'C#4', melody text, or a sequence of those), a MIDI note number
such as 60, a frequency in hertz, or a rendered sound, note or melody from xnano-audio.
Audio
dataclass
¶
A sound you can play with Canvas.play or
AudioDevice.play.
Make one from the path of a WAV file, the bytes of one, or a
TerminalSound.
Attributes:
-
sound(TerminalSound) –The rendered sound.
-
symbol(str | None) –The name the sound is registered under in
symbols, orNone.play('kick')plays the sound registered as'kick'.
Examples:
import xnano
from xnano_audio import TerminalSound
audio = xnano.Audio(TerminalSound.from_samples([0.0, 0.5, -0.5, 0.0]))
assert audio.sound.sample_rate == 44100
Load a sound.
Parameters:
-
source(TerminalSound | PathLike[str] | str | bytes) –The path of a WAV file, the bytes of one, or a sound.
-
symbol(str | None, default:None) –A name to register the sound under in
symbols, soplay(name)plays it.
Raises:
-
ImportError–xnano-audiois not installed. Install it withpip install "xnano[audio]". -
TypeError–sourceis not a path, bytes or a sound. -
FileNotFoundError–sourceis a path to a file that does not exist. -
ValueError–The file is not a PCM WAV file that xnano can read, or
symbolcannot be a symbol name, or another kind of value already has it.
Source code in xnano/_media.py
TerminalAudioEffect
¶
An immutable audio effect description.
Build one with a static constructor and pass it, in order, to
render_note,
TerminalSound.with_effects or
TerminalAudioPlayer.play_note.
Effects hold no state, so one instance can be reused for any number of
sounds.
Every constructor raises ValueError when an argument is out of
range.
Attributes:
Methods:
-
bit_crush–Lo-fi: quantize to
bitsand hold everydownsample-th sample. -
distortion–Soft-clip (tanh) distortion.
-
low_pass–Remove frequencies above
cutoff. -
high_pass–Remove frequencies below
cutoff. -
delay–Echoes.
-
reverb–Room reverb (Freeverb-style).
-
tremolo–Amplitude wobble.
-
__or__–Chain this effect with another effect or sequence of effects.
bit_crush
staticmethod
¶
bit_crush(*, bits: int = 8, downsample: int = 4) -> TerminalAudioEffect
distortion
staticmethod
¶
distortion(*, drive: float = 4.0) -> TerminalAudioEffect
Soft-clip (tanh) distortion.
Parameters:
-
drive(float, default:4.0) –Input gain before clipping, 0.1 to 100. Around 1 is nearly clean; higher is harsher. The output is normalized.
Source code in packages/xnano-audio/src/xnano_audio/_xnano_audio/__init__.pyi
low_pass
staticmethod
¶
low_pass(*, cutoff: float = 1200.0, resonance: float = 0.707) -> TerminalAudioEffect
high_pass
staticmethod
¶
high_pass(*, cutoff: float = 400.0, resonance: float = 0.707) -> TerminalAudioEffect
delay
staticmethod
¶
delay(*, time_milliseconds: float = 250.0, feedback: float = 0.4, mix: float = 0.35) -> TerminalAudioEffect
Echoes.
The result is longer than the input so the echoes ring out.
Parameters:
-
time_milliseconds(float, default:250.0) –Time between echoes, 1 to 2000.
-
feedback(float, default:0.4) –How much of each echo is fed back, 0.0 to 0.95.
-
mix(float, default:0.35) –Level of the echoes against the dry sound, 0.0 to 1.0.
Source code in packages/xnano-audio/src/xnano_audio/_xnano_audio/__init__.pyi
reverb
staticmethod
¶
reverb(*, room: float = 0.5, damping: float = 0.5, mix: float = 0.25) -> TerminalAudioEffect
Room reverb (Freeverb-style).
The result is longer than the input so the tail rings out.
Parameters:
-
room(float, default:0.5) –Room size, 0.0 to 1.0. Larger rooms ring longer.
-
damping(float, default:0.5) –How quickly high frequencies fade, 0.0 to 1.0.
-
mix(float, default:0.25) –Wet level against the dry sound, 0.0 to 1.0.
Source code in packages/xnano-audio/src/xnano_audio/_xnano_audio/__init__.pyi
tremolo
staticmethod
¶
tremolo(*, rate_hertz: float = 5.0, depth: float = 0.5) -> TerminalAudioEffect
__or__
¶
__or__(other: AudioEffectSource) -> list[TerminalAudioEffect]
Chain this effect with another effect or sequence of effects.
Examples:
chain = TerminalAudioEffect.low_pass(cutoff=800.0) | TerminalAudioEffect.reverb(room=0.5)
assert [effect.name for effect in chain] == ["low_pass", "reverb"]
Source code in packages/xnano-audio/src/xnano_audio/_xnano_audio/__init__.pyi
TerminalAudioError
¶
Bases: RuntimeError
Raised when an audio device or stream fails.
Treat this (and a failed import of the package) as "audio is not available": SSH sessions, containers and CI usually have no audio device.
TerminalAudioUnavailableError
¶
Bases: TerminalAudioError
Raised when an audio device is missing, or this build has no device support.
The message says which of the two it is and how to fix it.
TerminalMelody
¶
TerminalMelody(notes: list[TerminalNote] | None = None, *, tempo_map: TerminalTempoMap | None = None, time_signature: TerminalTimeSignature | None = None, length_beats: float = 0.0)
Notes on a beat timeline with a tempo map and a time signature.
Immutable: every method that changes the melody returns a new one. Notes can start
together to form chords. Ask which notes are due between two readings of any clock with
scheduled_between, or render the
melody to a TerminalSound with
render. len(melody) is the number of notes
and iterating yields the notes in start order.
Attributes:
-
notes(list[TerminalNote]) –The notes in start order.
-
tempo_map(TerminalTempoMap) –The tempo map.
-
time_signature(TerminalTimeSignature) –The time signature.
-
length_beats(float) –The end of the last note, or the explicit length when that is longer, in beats.
-
duration_seconds(float) –The melody's length in seconds.
Examples:
melody = (
TerminalMelody(tempo_map=TerminalTempoMap(100))
.appended_note("C4")
.appended_chord(["E4", "G4"], duration_beats=2)
.appended_rest(1)
)
sound = melody.render("piano")
due = melody.scheduled_between(0.0, 1.0)
Create a melody.
Parameters:
-
notes(list[TerminalNote] | None, default:None) –The notes, in any order.
-
tempo_map(TerminalTempoMap | None, default:None) –Defaults to a constant 120 beats per minute.
-
time_signature(TerminalTimeSignature | None, default:None) –Defaults to 4/4.
-
length_beats(float, default:0.0) –A minimum length that adds trailing silence after the last note.
Raises:
-
ValueError–If there are more than 100000 notes or
length_beatsis negative.
Methods:
-
with_notes–Return the melody with
notesadded. -
with_tempo_map–Return the melody with another tempo map.
-
with_time_signature–Return the melody with another time signature.
-
with_length_beats–Return the melody lasting at least
length_beats, which adds trailing silence. -
appended_note–Return the melody with one note appended at its end.
-
appended_chord–Return the melody with notes that sound together appended at its end.
-
appended_rest–Return the melody with silence appended at its end.
-
transposed–Return the melody with every pitch moved by
semitones. -
shifted–Return the melody moved later by
beats, or earlier when negative. -
stretched–Return the melody with every start, duration and tempo change multiplied by
factor. -
repeated–Return the melody played
counttimes in a row. -
followed_by–Return the melody followed by
other, keeping this melody's tempo map. -
layered_with–Return the notes of both melodies played together, keeping this tempo map.
-
__add__–Play something after the melody, or raise it by semitones.
-
__sub__–Lower every note by semitones.
-
__mul__–Play the melody several times in a row.
-
__or__–Play two melodies together.
-
__rshift__–Start the melody later by beats.
-
__lshift__–Start the melody earlier by beats.
-
notes_between_beats–Return the notes that start in
[from_beats, to_beats). -
notes_between_seconds–Return the notes that start in
[from_seconds, to_seconds)of melody time. -
scheduled_between–Return the notes due on an external clock between two readings.
-
render–Render the melody, or the notes starting inside a window, to a sound.
Source code in packages/xnano-audio/src/xnano_audio/_xnano_audio/__init__.pyi
length_beats
property
¶
length_beats: float
The end of the last note, or the explicit length when that is longer, in beats.
with_notes
¶
with_notes(notes: list[TerminalNote]) -> TerminalMelody
with_tempo_map
¶
with_tempo_map(tempo_map: TerminalTempoMap) -> TerminalMelody
with_time_signature
¶
with_time_signature(time_signature: TerminalTimeSignature) -> TerminalMelody
with_length_beats
¶
with_length_beats(length_beats: float) -> TerminalMelody
Return the melody lasting at least length_beats, which adds trailing silence.
appended_note
¶
appended_note(pitch: Pitch, *, duration_beats: float = 1.0, velocity: float = 0.8) -> TerminalMelody
Return the melody with one note appended at its end.
appended_chord
¶
appended_chord(pitches: Sequence[Pitch], *, duration_beats: float = 1.0, velocity: float = 0.8) -> TerminalMelody
Return the melody with notes that sound together appended at its end.
Source code in packages/xnano-audio/src/xnano_audio/_xnano_audio/__init__.pyi
appended_rest
¶
appended_rest(duration_beats: float = 1.0) -> TerminalMelody
transposed
¶
transposed(semitones: int) -> TerminalMelody
Return the melody with every pitch moved by semitones.
Raises:
-
ValueError–If a pitch would leave the MIDI range 0 to 127.
shifted
¶
shifted(beats: float) -> TerminalMelody
Return the melody moved later by beats, or earlier when negative.
Raises:
-
ValueError–If a note would start before beat zero.
stretched
¶
stretched(factor: float) -> TerminalMelody
Return the melody with every start, duration and tempo change multiplied by factor.
The tempo values are unchanged, so 2.0 makes the melody twice as long in time.
Source code in packages/xnano-audio/src/xnano_audio/_xnano_audio/__init__.pyi
repeated
¶
repeated(count: int) -> TerminalMelody
Return the melody played count times in a row.
Raises:
-
ValueError–If
countis zero or the result would hold too many notes.
followed_by
¶
followed_by(other: TerminalMelody) -> TerminalMelody
layered_with
¶
layered_with(other: TerminalMelody) -> TerminalMelody
Return the notes of both melodies played together, keeping this tempo map.
__add__
¶
__add__(other: int) -> TerminalMelody
__add__(other: TerminalMelody | TerminalNote) -> TerminalMelody
__add__(other: int | TerminalMelody | TerminalNote) -> TerminalMelody
Play something after the melody, or raise it by semitones.
Examples:
phrase = TerminalNote("C4") + TerminalNote("E4")
assert len((phrase + TerminalNote("G4")).notes) == 3
assert [note.name for note in phrase + 2] == ["D4", "F#4"]
Source code in packages/xnano-audio/src/xnano_audio/_xnano_audio/__init__.pyi
__sub__
¶
__sub__(other: int) -> TerminalMelody
__mul__
¶
__mul__(other: int) -> TerminalMelody
Play the melody several times in a row.
Examples:
Source code in packages/xnano-audio/src/xnano_audio/_xnano_audio/__init__.pyi
__or__
¶
__or__(other: TerminalMelody | TerminalNote) -> TerminalMelody
__rshift__
¶
__rshift__(other: float) -> TerminalMelody
__lshift__
¶
__lshift__(other: float) -> TerminalMelody
notes_between_beats
¶
notes_between_beats(from_beats: float, to_beats: float) -> list[TerminalNote]
Return the notes that start in [from_beats, to_beats).
notes_between_seconds
¶
notes_between_seconds(from_seconds: float, to_seconds: float) -> list[TerminalNote]
Return the notes that start in [from_seconds, to_seconds) of melody time.
scheduled_between
¶
scheduled_between(from_elapsed_seconds: float, to_elapsed_seconds: float, *, start_seconds: float = 0.0, should_loop: bool = False) -> list[TerminalScheduledNote]
Return the notes due on an external clock between two readings.
The melody begins when the clock reads start_seconds. Notes that start in
[from_elapsed_seconds, to_elapsed_seconds) are returned with their times on
that same clock, so consecutive windows never repeat or skip a note. Call it every
frame with the previous and current reading of a manual or monotonic clock.
Parameters:
-
from_elapsed_seconds(float) –The previous clock reading (inclusive).
-
to_elapsed_seconds(float) –The current clock reading (exclusive).
-
start_seconds(float, default:0.0) –The clock reading at which the melody begins.
-
should_loop(bool, default:False) –Restart the melody every time it reaches its length.
Raises:
-
ValueError–If a reading is not finite or
to_elapsed_secondsis earlier thanfrom_elapsed_seconds.
Source code in packages/xnano-audio/src/xnano_audio/_xnano_audio/__init__.pyi
render
¶
render(instrument: InstrumentName = 'piano', *, sample_rate: int = 44100, effects: list[TerminalAudioEffect] | None = None, from_seconds: float = 0.0, to_seconds: float | None = None) -> TerminalSound
Render the melody, or the notes starting inside a window, to a sound.
Notes are placed sample-accurately from the tempo map and mixed; the result is
limited to stay below clipping. The first sample is the instant from_seconds.
Notes ring past the window with their natural tail.
Parameters:
-
instrument(InstrumentName, default:'piano') –One of
instrument_names. -
sample_rate(int, default:44100) –Output rate in hertz, 8000 to 192000.
-
effects(list[TerminalAudioEffect] | None, default:None) –Effects applied in order to the mixed result (at most 32).
-
from_seconds(float, default:0.0) –Melody time of the first sample.
-
to_seconds(float | None, default:None) –Melody time at which the window ends; defaults to the melody's end.
Raises:
-
ValueError–If the instrument is unknown, an argument is out of range or the window is longer than 600 seconds.
Source code in packages/xnano-audio/src/xnano_audio/_xnano_audio/__init__.pyi
TerminalNote
¶
TerminalNote(pitch: Pitch, *, start_beats: float = 0.0, duration_beats: float = 1.0, velocity: float = 0.8)
One note on a beat timeline.
Immutable. Pitches are MIDI numbers (60 is middle C); a name such as "C#4"
is accepted wherever a pitch is. Times are measured in quarter-note beats from the
start of the melody. Every with_* method returns a new note.
Attributes:
-
pitch(int) –The MIDI pitch, 0 to 127.
-
name(NoteName) –The sharp-spelled note name, such as
"C#4". -
frequency_hertz(float) –The pitch in hertz.
-
velocity(float) –The strength, 0.0 to 1.0.
-
start_beats(float) –Where the note starts, in beats.
-
duration_beats(float) –How long the key is held, in beats.
-
end_beats(float) –Where the note ends, in beats.
Examples:
note = TerminalNote("C4", start_beats=1.0, duration_beats=0.5)
assert note.pitch == 60 and note.end_beats == 1.5
Create a note.
Parameters:
-
pitch(Pitch) –A
NoteNamesuch as"C4"or"Db5", or a MIDI number from 0 to 127. Lower case and a missing octave ("c#"is"C#4") are accepted too. -
start_beats(float, default:0.0) –Where the note starts, zero or more.
-
duration_beats(float, default:1.0) –How long the key is held, greater than zero.
-
velocity(float, default:0.8) –Strength from 0.0 to 1.0.
Raises:
-
ValueError–If an argument is out of range or the pitch name is not recognized.
-
TypeError–If
pitchis not anintorstr.
Methods:
-
with_pitch–Return the note with another pitch.
-
with_start_beats–Return the note starting at another beat.
-
with_duration_beats–Return the note held for another number of beats.
-
with_velocity–Return the note with another strength.
-
transposed–Return the note moved by
semitones. -
__add__–Move the note up by semitones, or play something after it.
-
__sub__–Move the note down by semitones, or measure the distance to another note.
-
__or__–Play notes together, as a chord.
-
__int__–The MIDI pitch, so
int(TerminalNote("C4"))is60. -
__lt__–Notes are ordered by pitch, then by where they start.
Source code in packages/xnano-audio/src/xnano_audio/_xnano_audio/__init__.pyi
with_pitch
¶
with_pitch(pitch: Pitch) -> TerminalNote
with_start_beats
¶
with_start_beats(start_beats: float) -> TerminalNote
with_duration_beats
¶
with_duration_beats(duration_beats: float) -> TerminalNote
with_velocity
¶
with_velocity(velocity: float) -> TerminalNote
transposed
¶
transposed(semitones: int) -> TerminalNote
Return the note moved by semitones.
Raises:
-
ValueError–If the result leaves the MIDI range 0 to 127.
__add__
¶
__add__(other: int) -> TerminalNote
__add__(other: TerminalNote | TerminalMelody) -> TerminalMelody
__add__(other: int | TerminalNote | TerminalMelody) -> TerminalNote | TerminalMelody
Move the note up by semitones, or play something after it.
note + 1 is the note one semitone higher, note + 12 an octave higher.
Adding a note or a melody makes a TerminalMelody
that plays them one after the other.
Raises:
-
ValueError–If the result leaves the MIDI range 0 to 127.
Examples:
assert TerminalNote("C4") + 1 == TerminalNote("C#4")
assert 12 + TerminalNote("C4") == TerminalNote("C5")
melody = TerminalNote("C4") + TerminalNote("E4")
assert [note.name for note in melody] == ["C4", "E4"]
Source code in packages/xnano-audio/src/xnano_audio/_xnano_audio/__init__.pyi
__sub__
¶
__sub__(other: int) -> TerminalNote
__sub__(other: TerminalNote) -> int
__sub__(other: int | TerminalNote) -> TerminalNote | int
__or__
¶
__or__(other: TerminalNote | TerminalMelody) -> TerminalMelody
__lt__
¶
__lt__(other: TerminalNote) -> bool
TerminalSound
¶
A rendered mono sound: raw samples plus their sample rate.
Immutable. Create one with render_note,
then inspect it, run it through more effects, or play it with
TerminalAudioPlayer.play_sound.
len(sound) is the number of samples.
Attributes:
-
sample_rate(int) –Samples per second.
-
duration_milliseconds(float) –Length in milliseconds, including any effect tail.
-
peak(float) –Largest absolute sample value, from 0.0 to 1.0.
-
root_mean_square(float) –Root-mean-square level, from 0.0 to 1.0.
Methods:
-
from_samples–Build a sound from your own mono floats.
-
from_bytes–Build a sound from little-endian 32-bit float bytes.
-
to_bytes–The samples as little-endian 32-bit floats.
-
samples–The samples as a list of floats.
-
envelope–The loudness outline of the sound:
pointspeak levels from 0.0 to 1.0. -
with_effects–A new sound with
effectsapplied in order. -
mix–Mix sounds placed on a timeline into one sound.
-
add–Apply one or more audio effects (or effect chains) in order and return the new sound.
-
__or__–Apply an audio effect or effect chain to this sound and return the new sound.
duration_milliseconds
property
¶
duration_milliseconds: float
Length in milliseconds, including any effect tail.
from_samples
staticmethod
¶
from_samples(samples: list[float], *, sample_rate: int = 44100) -> TerminalSound
Build a sound from your own mono floats.
This is how to add a custom instrument or waveform: generate the
samples in Python (from numpy, call .tolist()), then process and
play them like any built-in sound.
Parameters:
-
samples(list[float]) –Values from -1.0 to 1.0; they are not rescaled.
-
sample_rate(int, default:44100) –Samples per second, 8000 to 192000.
Raises:
-
ValueError–If
samplesis empty or contains NaN or infinity, or the rate is out of range.
Source code in packages/xnano-audio/src/xnano_audio/_xnano_audio/__init__.pyi
from_bytes
staticmethod
¶
from_bytes(data: bytes, *, sample_rate: int = 44100) -> TerminalSound
Build a sound from little-endian 32-bit float bytes.
The inverse of to_bytes.
Raises:
-
ValueError–If
datais empty or its length is not a multiple of 4, it contains NaN or infinity, or the rate is out of range.
Source code in packages/xnano-audio/src/xnano_audio/_xnano_audio/__init__.pyi
samples
¶
envelope
¶
The loudness outline of the sound: points peak levels from 0.0 to 1.0.
Each value is the largest absolute sample in one equal slice of the sound, ready to draw as a waveform or a sparkline.
Raises:
-
ValueError–If
pointsis not between 1 and 100000.
Source code in packages/xnano-audio/src/xnano_audio/_xnano_audio/__init__.pyi
with_effects
¶
with_effects(effects: list[TerminalAudioEffect]) -> TerminalSound
A new sound with effects applied in order.
Echo and reverb tails make the result longer than the original. The peak is kept below 0.98 and the added tail is capped at 30 seconds. The GIL is released while processing.
Raises:
-
ValueError–If more than 32 effects are given.
-
TypeError–If an item is not a
TerminalAudioEffect.
Source code in packages/xnano-audio/src/xnano_audio/_xnano_audio/__init__.pyi
mix
staticmethod
¶
mix(layers: Sequence[tuple[TerminalSound, float, float]], *, duration_seconds: float, start_seconds: float = 0.0, sample_rate: int = 44100) -> TerminalSound
Mix sounds placed on a timeline into one sound.
The result is the part of the timeline from start_seconds for
duration_seconds. Each layer is added at its offset, multiplied by its gain, and
the sum is limited to -1.0 to 1.0. A layer that starts before the window is cut at
the window's start, and one that runs past its end is cut there. The GIL is released
while mixing.
Parameters:
-
layers(Sequence[tuple[TerminalSound, float, float]]) –(sound, offset_seconds, gain)tuples.offset_secondsis where on the timeline the sound begins (it may be negative or beforestart_seconds) andgainis a factor of 0.0 or more. -
duration_seconds(float) –The length of the mixed sound, above 0 and at most 3600.
-
start_seconds(float, default:0.0) –The time on the timeline where the mixed sound begins.
-
sample_rate(int, default:44100) –Samples per second, 8000 to 192000. Every layer must have been rendered at this rate.
Raises:
-
ValueError–If
duration_secondsorsample_rateis out of range, the window holds more than 172800000 samples or cannot be allocated, a layer does not have exactly three values, an offset is not finite, a gain is not a finite number from 0.0 to 1000.0, or a layer was rendered at a different sample rate. -
TypeError–If
layersis not a sequence of(sound, offset_seconds, gain)tuples, or an offset or gain is not a number.
Examples:
beat = render_note("kick", "C2")
note = render_note("sine", "A4", duration_milliseconds=500)
window = TerminalSound.mix(
[(beat, 0.0, 1.0), (note, 0.5, 0.5)],
duration_seconds=1.0,
)
assert window.duration_milliseconds == 1000.0
assert window.peak <= 1.0
quiet = TerminalSound.mix([(note, 0.0, 0.0)], duration_seconds=0.25)
assert quiet.peak == 0.0
Source code in packages/xnano-audio/src/xnano_audio/_xnano_audio/__init__.pyi
add
¶
add(*effects: AudioEffectSource) -> TerminalSound
Apply one or more audio effects (or effect chains) in order and return the new sound.
Examples:
sound = render_note("piano", "C4", duration_milliseconds=100).add(
TerminalAudioEffect.low_pass(cutoff=900.0)
)
assert len(sound) > 0
Source code in packages/xnano-audio/src/xnano_audio/_xnano_audio/__init__.pyi
__or__
¶
__or__(other: AudioEffectSource) -> TerminalSound
Apply an audio effect or effect chain to this sound and return the new sound.
Examples:
sound = (
render_note("piano", "C4", duration_milliseconds=100)
| TerminalAudioEffect.low_pass(cutoff=900.0)
| TerminalAudioEffect.reverb(room=0.4)
)
assert len(sound) > 0
Source code in packages/xnano-audio/src/xnano_audio/_xnano_audio/__init__.pyi
AudioDevice
¶
The canvas's sound: notes, melodies and sounds to play, a musical clock, and the microphone.
Get it from Canvas.audio. Playing never waits. Pitches are
MIDI note numbers or names everywhere, and the length of a beat follows the canvas's
bpm.
Where the sound goes depends on the canvas. A canvas that runs by itself (on the screen,
or created with realtime=True) plays on this computer's sound output: the sound is
rendered on your thread and handed to the canvas's own loop, which owns the device. A
canvas from new() has no device and a clock you move, so it records what is played
instead, and render_sound turns the recording
into a sound, step by step, the same on every run.
The microphone level and the musical beat are expressions, so they drive anything an expression can: a position, a size, a color, an effect.
Attributes:
-
is_available(bool) –Whether sound can be played: always on a canvas without a device, and when this computer has a sound output device otherwise.
-
is_offline(bool) –Whether this canvas records sound instead of playing it on a device.
-
beat(NumericExpression) –The musical clock's position in beats, as an expression.
-
level(NumericExpression) –The microphone's current level from 0.0 to 1.0, as an expression.
Examples:
import xnano
with xnano.new(width=20, height=3) as canvas:
meter = canvas.write('#', y=1)
meter.x = canvas.audio.level * 19
level = canvas.inspect(canvas.audio.level)
assert 0.0 <= level <= 1.0
assert canvas.audio.is_offline
Create the canvas's audio. Use canvas.audio instead of calling this.
Parameters:
Methods:
-
play–Play something that makes sound, and return at once.
-
render_sound–Turn what was played since the previous call into one sound, and forget it.
-
start–Start the musical clock.
-
resume–Let the musical clock run on from where
pauseleft it. -
pause–Pause the musical clock, keeping its position.
-
stop–Stop the musical clock and return it to the first beat.
-
level_history–Return the last
countmicrophone levels, oldest first.
Source code in xnano/audio.py
is_offline
property
¶
is_offline: bool
Whether this canvas records sound instead of playing it on a device.
It is the opposite of Time.is_realtime: a canvas that
runs on the wall clock plays on a device, and one whose clock you move records.
is_available
property
¶
is_available: bool
Whether sound can be played: always without a device, otherwise when one exists.
fx
property
¶
fx: PlayableEffects
The sound effects of everything this canvas plays, one method for each effect.
canvas.audio.fx.reverb(mix=0.4) adds a reverb to every sound played after it, from
any hook or from your own code, and canvas.audio.fx.clear() removes what was added
here. A sound that is already playing keeps the effects it started with. See
PlayableEffects.
Examples:
beat
property
¶
The musical clock's position in beats, as an expression.
It stays at 0 until the clock is started with
start or a sound is played with at_beat.
Examples:
level
property
¶
The microphone's current level from 0.0 to 1.0, as an expression.
Reading it opens the default input device the first time a frame evaluates it.
Without an input device, without permission, or on a canvas without a device, the
level is 0.
play
¶
play(source: PlaySource, *, instrument: InstrumentName | None = None, beats: float | None = None, velocity: float | None = None, effects: Sequence[PlayableEffectSpecification] | None = None, volume: float = 1.0, pan: float = 0.0, at_beat: int | None = None) -> PlayHandle
Play something that makes sound, and return at once.
source is a Note, an Audio, the name of a
registered symbol, a note name such as 'C#4', a MIDI number such as 60, a
frequency in hertz, melody text, or a sequence of those that plays one after another.
A str is a symbol name first; if no symbol has that name it is a note name, a
frequency such as '440hz' or melody text. Melody text has spaces, :2 holds a
note for two beats, rest is a silence, + joins a chord and | marks a bar:
'C4 E4 G4:2 rest C5+E5+G5'. An option you pass here replaces the same value on a
Note, so play(note('C4'), instrument='violin') plays the violin.
On a canvas without a device the sound is recorded at the canvas's current time and
nothing is synthesized until render_sound
asks for it; the arguments are still checked here.
Parameters:
-
source(PlaySource) –What to play.
-
instrument(InstrumentName | None, default:None) –A built-in instrument for notes and melodies, from
instrument_names;'piano'unless aNotesays otherwise. A sound is played as it is. -
beats(float | None, default:None) –How long a note name, number, frequency or
Noteis held, in beats at the canvas's tempo, and how long melody text holds a note that gives no length of its own.1.0unless aNotesays otherwise. -
velocity(float | None, default:None) –How hard a note is played, 0.0 to 1.0;
0.8unless aNotesays otherwise. -
effects(Sequence[PlayableEffectSpecification] | None, default:None) –Effects applied after the instrument, or to a sound as it is played, as dictionaries such as
{'kind': 'audio.reverb'}. A hook that calledcontext.fxwith a sound effect adds its effects to every sound it plays. -
volume(float, default:1.0) –Playback gain, 0.0 to 1.0.
-
pan(float, default:0.0) –Stereo position from -1.0 (left) to 1.0 (right). A recording is mono.
-
at_beat(int | None, default:None) –Start when the musical clock reaches this beat. The clock is started if it is not running.
Returns:
-
PlayHandle–A
PlayHandlethat tells when the sound is over.
Raises:
-
TerminalAudioUnavailableError–The canvas plays on a device, and this computer has no sound output device.
-
ValueError–An argument is out of range, the instrument is unknown, or a
stris neither a symbol, a note name nor melody text. The message lists the known sound symbols. -
TypeError–sourcecannot be played, or an effect is not an audio effect.
Examples:
import xnano
with xnano.new(width=8, height=1) as canvas:
canvas.audio.play('C4')
canvas.audio.play(64, instrument='bell')
canvas.audio.play(xnano.note('G4', beats=0.5))
canvas.audio.play('C4 E4 G4:2 rest C5+E5+G5')
canvas.step(delta_seconds=0.5)
assert canvas.audio.render_sound().duration_milliseconds == 500.0
Source code in xnano/audio.py
705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 | |
render_sound
¶
render_sound() -> TerminalSound
Turn what was played since the previous call into one sound, and forget it.
The sound covers the canvas time from the previous render_sound() (or from when
the audio was first used) up to now, so calling it after every
step gives the sound of each step. Everything played in
that time is synthesized and mixed (limited to -1.0 to 1.0, mono); a note that rings
on past the end continues in the next call. A second call at the same time returns
an empty instant of silence, because the first one took the sound. Sounds that
ended more than 60 seconds of canvas time ago are not kept, so a canvas that has not
been asked for longer than that returns the last 60 seconds.
Returns:
-
TerminalSound–The
TerminalSoundof that time.
Raises:
-
TerminalError–The canvas plays on a device, which does not record.
Examples:
import xnano
with xnano.new(width=8, height=1, fps=10) as canvas:
canvas.audio.play('C4', instrument='bell')
canvas.step()
sound = canvas.audio.render_sound()
assert sound.duration_milliseconds == 100.0
assert max(abs(sample) for sample in sound.samples()) > 0.0
Source code in xnano/audio.py
start
¶
Start the musical clock.
Source code in xnano/audio.py
resume
¶
pause
¶
Pause the musical clock, keeping its position.
Source code in xnano/audio.py
stop
¶
Stop the musical clock and return it to the first beat.
This rewinds only the musical clock. To freeze the whole canvas, including this
clock, use Canvas.stop; it resumes from where it stood
with Canvas.resume.
Source code in xnano/audio.py
level_history
¶
Return the last count microphone levels, oldest first.
Parameters:
-
count(int) –How many levels to return.
Source code in xnano/audio.py
instrument_names
¶
instrument_names() -> list[InstrumentName]
Names of every built-in instrument, in catalog order.