scamp.session.Session
- class scamp.session.Session(tempo: float = 60, default_soundfont: str = 'default', default_audio_driver: str = 'default', default_spelling_policy: SpellingPolicy | str | tuple = None, instruments: Sequence[ScampInstrument] = None, max_threads=200)[source]
Bases:
Clock,Ensemble,Transcriber,SavesToJSONA Session combines the functionality of a master Clock, an Ensemble, and a Transcriber. Since it’s a master Clock, it manages global tempo; since it’s an Ensemble, you use it to create and keep track of instruments, and since it’s a Transcriber, it allows you to transcribe and generate notation from what the instruments are playing. You can also use a Session to set up interactive callbacks for MIDI, OSC, mouse events, and keyboard events.
- Parameters:
tempo – the initial tempo of the master clock
default_soundfont – the default soundfont used by instruments in this session. (Can be overridden at instrument creation.)
default_audio_driver – the default driver used by (soundfont) instruments to output audio. (Can be overridden at instrument creation.)
Methods
Returns a list of available ports and devices for midi input.
Prints a list of available ports and devices for midi input.
Prints a list of available ports and devices for midi output.
register_keyboard_listener([on_press, ...])Register a callback_function to respond to incoming keyboard events
Register a callback_function to respond to incoming midi events from port_number_or_device_name
register_mouse_listener([on_move, on_press, ...])Register a callback_function to respond to incoming mouse events
register_osc_listener(port, ...[, ip_address])Register a callback function for OSC messages on a given address/port with given pattern
Remove a previously added keyboard listener.
remove_midi_listener(port_number_or_device_name)Removes the midi listener with the given port_number_or_device_name
Remove a previously added mouse listener
remove_osc_listener(port[, ip_address])Remove OSC listener on the given port and IP address
Hands this session off to a background thread that waits forever, so the calling thread stays free.
start_transcribing([...])Starts transcribing everything played in this Session's (or by the given instruments) to a Performance.
Inherited Methods
add_instrument(instrument)Adds an instance of
ScampInstrumentto this Ensemble.apply_beat_length_function(function[, ...])Drive this clock's beat_length from a function.
apply_rate_function(function[, ...])Drive this clock's rate from a function.
apply_tempo_envelope(envelope[, truncate, loop])Append the given
TempoEnvelopeonto this clock's internal tempo envelope, starting from the current beat.apply_tempo_function(function[, ...])Drive this clock's tempo (BPM) from a function.
Advance this clock's tempo_history to match where it currently is in scheduler time.
children()Get all direct child clocks forked by this clock.
clock_to_scheduler_time(beat_or_time[, units])Gets the time in the scheduler for a given beat or time in this clock, working recursively up the chain of clocks.
Get all children, grandchildren, etc.
Returns a copy of this object by serializing to and from JSON.
Extract this clock's absolute tempo curve — its tempo as observed in master (scheduler) time, with parent rate-changes folded in.
fast_forward([on_or_off])Turn indefinite fast-forwarding on or off.
Fast-forward, skipping ahead instantaneously by b beats.
Fast-forward, skipping ahead instantaneously by t seconds.
Fast-forward, skipping instantaneously up to beat b on this clock.
Fast-forward, skipping instantaneously up to time t (in seconds) on this clock, then resume real-time playback.
fork(forked_function[, args, kwargs, name, ...])Spawns a child clock running forked_function as a coordinated parallel timeline.
Returns an enumeration of available ports and devices for midi output.
get_instrument_by_name(name[, which])Returns the instrument of the given name.
Context manager that: 1) ensures that the scheduler is not executing scheduled actions — waiting if one is in flight, then preventing others from starting; 2) rouses the scheduler and updates its committed time to the current projected time.
inheritance([include_self])Get all parent, grandparent, etc.
Whether the clock is currently fast-forwarding.
Check if this is the master clock
Checks if any transcriptions are in progress.
iterate_all_relatives([include_self])Iterate through all related clocks to this clock.
iterate_descendants([include_self])Iterate through all children, grandchildren, etc.
iterate_inheritance([include_self])Iterate through parent, grandparent, etc.
Dump this object as a JSON string.
json_loads(s)Load this object from a JSON string.
kill()End this clock (and the corresponding forked function if not master) and cascade to all descendant clocks.
load_from_json(file_path)Load this object from a JSON file with the given path.
new_midi_part([name, midi_output_device, ...])Creates and returns a new ScampInstrument for this Ensemble that uses a MIDIStreamPlaybackImplementation.
new_osc_part([name, port, ip_address, ...])Creates and returns a new ScampInstrument for this Ensemble that uses a OSCPlaybackImplementation.
new_part([name, preset, soundfont, ...])Creates and returns a new ScampInstrument for this Ensemble that uses a SoundfontPlaybackImplementation.
new_silent_part([name, ...])Creates and returns a new ScampInstrument for this Ensemble with no PlaybackImplementations.
pop_instrument(index)Pops the instrument at the given index, severing its ties to the ensemble.
Prints a list of presets available with the default soundfont.
Print a hierarchical representation of this clock's family tree.
print_status([verbose])Print this clock's
status()snapshot.A wall-clock-interpolated estimate of
beat.A wall-clock-interpolated estimate of
time, advancing smoothly between events rather than holding still between them.register_note(instrument, note_info)Called when an instrument wants to register that it finished a note, records note in all transcriptions
save_to_json(file_path)Save this object to a JSON file using the given path.
schedule_action(action, when[, args, kwargs])The lightweight alternative to fork() for immediately returning functions.
scheduler_to_clock_time(scheduler_time[, ...])Gets beats or time in this clock for a given time in the scheduler.
set_beat_length_target(beat_length_target, when)Smoothly change this clock's beat length (seconds per beat) to
beat_length_target, arriving at the moment given bywhen.set_beat_length_targets(beat_length_targets, ...)Smoothly change this clock's beat length (seconds per beat) through a series of targets, building a multi-segment tempo curve in one call — the multi-segment form of
set_beat_length_target().set_rate_target(rate_target, when[, ...])Smoothly change this clock's rate (beats per second) to
rate_target, arriving atwhen.set_rate_targets(rate_targets, whens[, ...])Smoothly change this clock's rate (beats per second; the reciprocal of beat length, so a higher rate is a faster tempo) through a series of targets — the multi-segment form of
set_rate_target().set_tempo_target(tempo_target, when[, ...])Smoothly change this clock's tempo to
tempo_target, arriving atwhen.set_tempo_targets(tempo_targets, whens[, ...])Smoothly change this clock's tempo (beats per minute) through a series of targets — the multi-segment form of
set_tempo_target(), and usually the most natural of the three equivalent plural setters.status([verbose])A snapshot of this clock's name and position, alongside the scheduler's elapsed wall time and how far behind schedule it is currently running.
Stop following any function or looping envelope previously applied to this clock's tempo.
stop_transcribing([which_performance, ...])Stops transcribing a Performance and returns it.
Kill this clock's children and their descendants in turn, returning once they have finished unwinding.
Shorthand for
timing_policy = 0.0(always catch up to absolute schedule).use_mixed_timing_policy(absolute_relative_mix)Shorthand for
timing_policy = absolute_relative_mix(a blend between 0=absolute and 1=relative).Shorthand for
timing_policy = 1.0(always wait the full requested delay).wait(duration[, units])Block this clock for duration beats (or seconds, if units="time") from now, yielding to the scheduler.
Block this clock's own thread until all of its child clocks have finished, yielding to the scheduler so they can run, then return as soon as the last child ends.
wait_for_clock_to_finish(clock)Block this clock's own thread until clock has finished, yielding to the scheduler so it (and everything else) keeps running.
Block this clock's own thread indefinitely, yielding to the scheduler so child clocks keep running.
wait_until(when[, units])Block this clock until the beat (or time, if units="time") indicated by when — a convenience for absolute targets given as a bare number (equivalent to wait(Moment.at_beat(when)), or Moment.at_time for units="time").
Attributes
Beat length in true seconds, with all parent rates folded in.
Rate of this clock in beats / (true) second, with all parent rates folded in.
Tempo (BPM) in true minutes, with all parent rates folded in.
True if this clock is currently running (PENDING and DEAD both return False).
How many beats have passed since this clock was created.
The length of a beat in this clock in seconds.
Default spelling policy used for transcriptions made with this Ensemble.
Returns a tuple of the instruments currently in this Ensemble.
The master clock under which this clock operates (possibly itself)
Whether to use a busy wait in the final moments leading up to a scheduled event.
The rate of this clock in beats / second.
Width (seconds) of the busy-wait guard band used when
precise_timingis on.The rate of this clock in beats / minute Note that beat_length, tempo and rate are interconnected properties, and that by setting one of them the other two are automatically set in response according to the relationship: beat_length = 1/rate = 60/tempo.
How much time has passed since this clock was created.
How the family trades off relative vs. absolute timing, as a float from 0 to 1.
Tuple of all current transcriptions.
- run_as_server() Session[source]
Hands this session off to a background thread that waits forever, so the calling thread stays free. This is the approach to take when driving scamp from an interactive terminal (REPL) session, where you want the prompt to stay responsive: simply type
s = Session().run_as_server().Note the tradeoff: once handed off, the calling thread no longer owns the session’s clock (its
current_clock()becomesNone), so subsequent work must be scheduled on the returned object explicitly — e.g.s.fork(...)— rather than through the context-inferring module-level helpersfork()andwait(). In a normal top-to-bottom script you don’t need this at all: the thread that creates theSessionowns its clock, so just usewait/forkand, if you need to keep the process alive at the end,s.wait_forever().- Returns:
self
- static get_available_midi_input_devices() Iterator[tuple[int, str]][source]
Returns a list of available ports and devices for midi input.
- static print_available_midi_input_devices() None[source]
Prints a list of available ports and devices for midi input.
- static print_available_midi_output_devices() None[source]
Prints a list of available ports and devices for midi output.
- register_midi_listener(port_number_or_device_name: int | str, callback_function: Callable) None[source]
Register a callback_function to respond to incoming midi events from port_number_or_device_name
- Parameters:
port_number_or_device_name – either the port number to be used, or an device name for which the port number will be determined. (Fuzzy string matching is used to pick the device with closest name.)
callback_function – the callback function used when a new midi event arrives. Should take either one argument (the midi message) or two arguments (the midi message, and the dt since the last message)
- remove_midi_listener(port_number_or_device_name: int | str) None[source]
Removes the midi listener with the given port_number_or_device_name
- Parameters:
port_number_or_device_name – either the port number to be used, or an device name for which the port number will be determined. (Fuzzy string matching is used to pick the device with closest name.)
- register_osc_listener(port: int, osc_address_pattern: str, callback_function: Callable, ip_address: str = '127.0.0.1') None[source]
Register a callback function for OSC messages on a given address/port with given pattern
- Parameters:
port – port on which to receive messages
osc_address_pattern – address pattern to respond to (e.g. “/gesture/start”)
callback_function – function to call upon receiving a message. The first argument of the function will be the address, and the remaining arguments will be those passed along in the osc message.
ip_address – ip address on which to receive messages
- remove_osc_listener(port: int, ip_address: str = '127.0.0.1') None[source]
Remove OSC listener on the given port and IP address
- Parameters:
port – port of the listener to remove
ip_address – ip_address of the listener to remove
- register_keyboard_listener(on_press: Callable = None, on_release: Callable = None, suppress: bool = False, **kwargs) None[source]
Register a callback_function to respond to incoming keyboard events
- Parameters:
on_press – function taking two arguments: key name (string) and key number (int) called on key down
on_release – function taking two arguments: key name (string) and key number (int) called on key up
suppress – if true, keyboard events are consumed and not passed on to other processes
- register_mouse_listener(on_move: Callable = None, on_press: Callable = None, on_release: Callable = None, on_scroll: Callable = None, suppress: bool = False, relative_coordinates: bool = False, **kwargs) None[source]
Register a callback_function to respond to incoming mouse events
- Parameters:
on_move – callback function taking two arguments (x, y) called when the mouse is moved
on_press – callback function taking three arguments: (x, y, button), where button is one of “left”, “right”, or “middle”
on_release – callback function taking three arguments: (x, y, button), where button is one of “left”, “right”, or “middle”
on_scroll – callback function taking four arguments: (x, y, dx, dy)
relative_coordinates – if True (requires tkinter library), x and y values are normalized to screen width and height and are floating point. Otherwise they are ints in units of pixels.
suppress – if true, mouse events are consumed and not passed on to other processes
- start_transcribing(instrument_or_instruments: ScampInstrument | Sequence[ScampInstrument] = None, clock: Clock = None, units: str = 'beats') Performance[source]
Starts transcribing everything played in this Session’s (or by the given instruments) to a Performance. Defaults to using this Session as the clock.
- Parameters:
instrument_or_instruments – which instruments to transcribe. Defaults to all session instruments
clock – which clock to record on, i.e. what are all the timings notated relative to
units – one of [“beats”, “time”]. Do we use the beats of the clock or the time?
- Returns:
the Performance we will be transcribing to
- property absolute_beat_length: float
Beat length in true seconds, with all parent rates folded in. (Read-only property; the pre-1.1
clock.absolute_beat_length()spelling still works, with a DeprecationWarning.)
- property absolute_rate: float
Rate of this clock in beats / (true) second, with all parent rates folded in.
(Read-only property. Before clockblocks 1.1 this was a method; the
clock.absolute_rate()spelling still works, with a DeprecationWarning.)
- property absolute_tempo: float
Tempo (BPM) in true minutes, with all parent rates folded in. (Read-only property; the pre-1.1
clock.absolute_tempo()spelling still works, with a DeprecationWarning.)
- add_instrument(instrument: ScampInstrument) ScampInstrument
Adds an instance of
ScampInstrumentto this Ensemble. Generally, creating of and instrument and adding it to an ensemble are done simultaneously via one of the “new_instrument” methods.- Parameters:
instrument – instrument to add to this ensemble
- Returns:
self
- property alive: bool
True if this clock is currently running (PENDING and DEAD both return False).
- apply_beat_length_function(function: Callable, domain_start: float = 0, domain_end: float = None, duration_units: str = 'beats', truncate: bool = True, loop: bool = False, extension_increment: float = 2.0, **kwargs) None
Drive this clock’s beat_length from a function. See
apply_function()for the full parameter list (passed via**kwargs).
- apply_rate_function(function: Callable, domain_start: float = 0, domain_end: float = None, duration_units: str = 'beats', truncate: bool = True, loop: bool = False, extension_increment: float = 2.0, **kwargs) None
Drive this clock’s rate from a function.
- apply_tempo_envelope(envelope: TempoEnvelope, truncate: bool = True, loop: bool = False) None
Append the given
TempoEnvelopeonto this clock’s internal tempo envelope, starting from the current beat. With loop=True the envelope repeats indefinitely untilstop_tempo_loop_or_function(). truncate first discards any tempo curve already projected past the current beat so the envelope begins cleanly from now.
- apply_tempo_function(function: Callable, domain_start: float = 0, domain_end: float = None, duration_units: str = 'beats', truncate: bool = True, loop: bool = False, extension_increment: float = 2.0, **kwargs) None
Drive this clock’s tempo (BPM) from a function.
- property beat: float
How many beats have passed since this clock was created. Note that this is quantized to the scheduler’s event stream; for a live estimate when outside the clock system use
projected_time().(Read-only property. Before clockblocks 1.1 this was a method; the
clock.beat()spelling still works, with a DeprecationWarning.)
- property beat_length: float
The length of a beat in this clock in seconds. Note that beat_length, tempo and rate are interconnected properties, and that by setting one of them the other two are automatically set in response according to the relationship: beat_length = 1/rate = 60/tempo. Also, note that “seconds” refers to actual seconds only in the master clock; otherwise it refers to beats in the parent clock.
- bring_up_to_date()
Advance this clock’s tempo_history to match where it currently is in scheduler time.
Called before mutating tempo from a foreign thread: if we don’t, the tempo setter’s truncate() cuts at the last committed beat (whatever the clock last woke at), causing the new tempo to retroactively reshape the segment the clock is currently napping through. From the owning thread this is a no-op since committed == current.
- children() Sequence[Clock]
Get all direct child clocks forked by this clock.
- Returns:
tuple of all child clocks of this clock
- clock_to_scheduler_time(beat_or_time, units='beats')
Gets the time in the scheduler for a given beat or time in this clock, working recursively up the chain of clocks.
- Parameters:
beat_or_time – the beat or time of interest in this clock
units – one of (“beats”, “time”), determining the units for the first argument
- Returns:
how much time should pass in the scheduler
- property default_spelling_policy: SpellingPolicy
Default spelling policy used for transcriptions made with this Ensemble.
- descendants() Sequence[Clock]
Get all children, grandchildren, etc. of this clock
- Returns:
tuple of all descendants
- duplicate() T
Returns a copy of this object by serializing to and from JSON.
- extract_absolute_tempo_envelope(start_beat: float = 0, end_beat: float | None = None, step_size: float = 0.1, tolerance: float = 0.005) TempoEnvelope
Extract this clock’s absolute tempo curve — its tempo as observed in master (scheduler) time, with parent rate-changes folded in. Used when building a Score from this clock’s perspective.
Extracts over
[start_beat, end_beat], where end_beat defaults to this clock’s current beat. When all ancestors are pure, flat tempos, they simply scale this clock’s tempo curve directly. When the ancestors are changing, there is no exact solution, so we sample every step_size beats. Sampling is aligned to the tempo break points of every clock in the chain, so an instantaneous tempo change stays sharp rather than smearing into a short accelerando or ritardando.
- fast_forward(on_or_off: bool = True) None
Turn indefinite fast-forwarding on or off. While on, all waiting is instantaneous. (Only valid on the master clock.)
- Parameters:
on_or_off – True to start fast-forwarding, False to stop.
- fast_forward_in_beats(b: float) None
Fast-forward, skipping ahead instantaneously by b beats. (Only valid on the master clock.)
- Parameters:
b – number of beats to fast-forward by
- fast_forward_in_time(t: float) None
Fast-forward, skipping ahead instantaneously by t seconds. (Only valid on the master clock.)
- Parameters:
t – number of seconds to fast-forward by
- fast_forward_to_beat(b: float) None
Fast-forward, skipping instantaneously up to beat b on this clock. (Only valid on the master clock.)
- Parameters:
b – beat to fast-forward to
- fast_forward_to_time(t: float) None
Fast-forward, skipping instantaneously up to time t (in seconds) on this clock, then resume real-time playback. (Only valid on the master clock.)
- Parameters:
t – time to fast-forward to
- fork(forked_function: Callable, args: Sequence = (), kwargs: dict = None, name: str = None, initial_rate: float = None, initial_tempo: float = None, initial_beat_length: float = None, when: ResolvableMoment | None = None, done_callback: Callable[[], None] = None)
Spawns a child clock running forked_function as a coordinated parallel timeline.
The child runs on its own thread but stays synchronized under this clock’s master scheduler — “parallel” here means parallel musical time (like a separate voice or layer), not simultaneous CPU execution; the scheduler runs one clock’s code at a time.
- Parameters:
forked_function – the function to be run on the new child clock
args – positional arguments to be passed to the forked function. (Unlike legacy clockblocks, clockblocks does not inject the child clock as an extra first argument when the signature is one short — call
current_clock()from inside the forked function if you need a reference to it.)kwargs – keyword arguments to be passed to the forked function
name – name to be given to the spawned child clock
initial_rate – starting rate of this clock (if set, don’t set initial tempo or beat length)
initial_tempo – starting tempo of this clock (if set, don’t set initial rate or beat length)
initial_beat_length – starting beat length of this clock (if set, don’t set initial tempo or rate)
when – when the forked function should begin, as a
ResolvableMoment. None (default) starts it immediately. Otherwise pass an explicit Moment —at_beat()(orat_time()) for an absolute point, orafter_beats()(orafter_time()) for an offset from now. Unlike wait and wait_until, a bare number is rejected here, since it’s not clear whether it would be relative or absolute. Also possible is aMetricPhaseTargetwhich starts it at the next matching point in a cycle.done_callback – a callback function to be invoked when the clock has terminated
- Returns:
the spawned child clock
- static get_available_midi_output_devices() enumerate
Returns an enumeration of available ports and devices for midi output.
- get_instrument_by_name(name: str, which: int = 0)
Returns the instrument of the given name.
- Parameters:
name – name of the instrument to return
which – If there are multiple with the same name, this parameter specifies the one returned. (If none match the number given by which, the first name match is returned)
- hold_scheduler()
Context manager that: 1) ensures that the scheduler is not executing scheduled actions — waiting if one is in flight, then preventing others from starting; 2) rouses the scheduler and updates its committed time to the current projected time. Code under this context manager therefore acts like an immediately scheduled action, holding exclusive access to an up-to-date scheduler.
Note that, when used from within the clock system, this context manager is a no-op, since the current thread already holds exclusive access to an up-to-date scheduler. In fact, invoking clock.scheduler.held() in that context would cause deadlock, which is why this method exists as the main entry point.
- inheritance(include_self: bool = True) Sequence[Clock]
Get all parent, grandparent, etc. of this clock up until the master clock
- Parameters:
include_self – whether or not to include this clock in the iterator or start with the parent
- Returns:
tuple containing the clock’s inheritance
- property instruments
Returns a tuple of the instruments currently in this Ensemble.
- is_fast_forwarding() bool
Whether the clock is currently fast-forwarding. Since fast-forwarding is a scheduler-wide state, this is true for every clock in the family whenever it’s true for any of them.
- is_master() bool
Check if this is the master clock
- Returns:
True if this is the master clock, False otherwise
- is_transcribing() bool
Checks if any transcriptions are in progress.
- iterate_all_relatives(include_self: bool = False) Iterator[Clock]
Iterate through all related clocks to this clock.
- Parameters:
include_self – whether or not to include this clock in the iterator
- Returns:
iterator going through all clocks in the family tree, starting with the master
- iterate_descendants(include_self: bool = False) Iterator[Clock]
Iterate through all children, grandchildren, etc. of this clock
- Parameters:
include_self – whether or not to include this clock in the iterator
- Returns:
iterator going through all descendants
- iterate_inheritance(include_self: bool = True) Iterator[Clock]
Iterate through parent, grandparent, etc. of this clock up until the master clock
- Parameters:
include_self – whether or not to include this clock in the iterator or start with the parent
- Returns:
iterator going up the clock family tree up to the master clock
- json_dumps() str
Dump this object as a JSON string. This uses a custom encoder that recognizes and appropriately converts any attributes that are object inheriting from SavesToJSON.
- classmethod json_loads(s: str) T
Load this object from a JSON string. This uses a custom decoder that looks for a “_type” key in any object/dictionary being parsed and converts it to the class specified (assuming it a subclass of SavesToJSON).
- Parameters:
s – a string representing this object in JSON format
- kill() None
End this clock (and the corresponding forked function if not master) and cascade to all descendant clocks.
Pending scheduled work for this clock and its descendants is canceled and the clocks are marked dead. A clock currently blocked in
wait()raisesClockKilledError; any laterwait()orfork()made through a reference to a dead clock raisesDeadClockError.Killing the master also tears down the family’s scheduler, and releases its owning thread: that thread’s
current_clock()becomesNone, so the module-level helpers (wait(),fork(),get_beat(), …) raiseNoActiveClockErrorthere rather than reporting a dead clock.Returns once the killed clocks have finished unwinding to ensure synchronicity. Emits a warning if this takes more than a few seconds.
Safe to call from any thread, and killing an already-dead clock is a no-op.
(See the “Clock termination / lifecycle paths” note near the top of this module for how the five termination paths work internally.)
- classmethod load_from_json(file_path: str) T
Load this object from a JSON file with the given path. This uses a custom decoder that looks for a “_type” key in any object/dictionary being parsed and converts it to the class specified (assuming it a subclass of SavesToJSON).
- Parameters:
file_path – path for loading the file
- new_midi_part(name: str = None, midi_output_device: int | str = None, num_channels: int = 8, midi_output_name: str = None, max_pitch_bend: int = 'default', start_note_fixed: bool = True, default_spelling_policy: SpellingPolicy | str | tuple = None, clef_preference='from_name', start_channel: int = 0, volume_cc_num: int = 11, note_on_and_off_only: bool = False) ScampInstrument
Creates and returns a new ScampInstrument for this Ensemble that uses a MIDIStreamPlaybackImplementation. This means that when notes are played by this instrument, midi messages are sent out to the given device.
- Parameters:
name – name used for this instrument in score, etc. for a preset of the appropriate name.
midi_output_device – name or number of the device used to output midi. Call get_available_midi_output_devices to check what’s available.
num_channels – maximum of midi channels available to this midi part. It’s wise to use more when doing microtonal playback, since pitch bends are applied per channel.
midi_output_name – name of this part
max_pitch_bend – max pitch bend to use for this instrument
start_note_fixed – default
start_note_fixedfor notes started withstart_note()on this part.default_spelling_policy – the
default_spelling_policyfor the new partclef_preference – the
clef_preferencefor the new partstart_channel – the first channel to use. For instance, if start_channel is 4, and num_channels is 5, we will use channels (4, 5, 6, 7, 8). NOTE: channel counting in SCAMP starts from 0, so this may show up as channels 5-9 in your MIDI software.
volume_cc_num – The cc value used for volume changes. Defaults to 11 (expression).
note_on_and_off_only – Deprecated; use start_note_fixed.
- Returns:
the newly created ScampInstrument
- new_osc_part(name: str = None, port: int = None, ip_address: str = '127.0.0.1', message_prefix: str = None, osc_message_addresses: dict = 'default', default_spelling_policy: SpellingPolicy | str | tuple = None, clef_preference='from_name', start_note_fixed: bool = True) ScampInstrument
Creates and returns a new ScampInstrument for this Ensemble that uses a OSCPlaybackImplementation. This means that when notes are played by this instrument, osc messages are sent out to the specified address
- Parameters:
name – name used for this instrument in score, etc. for a preset of the appropriate name.
port – port osc messages are sent to
ip_address – ip_address osc messages are sent to
message_prefix – prefix used for this instrument in osc messages
osc_message_addresses – dictionary defining the address used for each type of playback message. defaults to using “start_note”, “end_note”, “change_pitch”, “change_volume”, “change_parameter”. The default can be changed in playback settings.
default_spelling_policy – the
default_spelling_policyfor the new partclef_preference – the
clef_preferencefor the new partstart_note_fixed – default
start_note_fixedfor notes started withstart_note()on this part.
- Returns:
the newly created ScampInstrument
- new_part(name: str = None, preset='auto', soundfont: str = 'default', num_channels: int = 8, audio_driver: str = 'default', max_pitch_bend: int = 'default', start_note_fixed: bool = True, default_spelling_policy: SpellingPolicy | str | tuple = None, clef_preference='from_name', volume_cc_num: int = 11, note_on_and_off_only: bool = False) ScampInstrument
Creates and returns a new ScampInstrument for this Ensemble that uses a SoundfontPlaybackImplementation. Unless otherwise specified, the default soundfont for this Ensemble/Session will be used, and we will search for the preset that best matches the name given.
- Parameters:
name – name used for this instrument in score, etc.
preset – if an int, assumes bank #0; can also be a tuple of form (bank, preset). If “auto”, searches for a preset of the appropriate name.
soundfont – the name of the soundfont to use for fluidsynth playback
num_channels – maximum of midi channels available to this midi part. It’s wise to use more when doing microtonal playback, since pitch bends are applied per channel.
audio_driver – which audio driver to use for this instrument (defaults to ensemble default)
max_pitch_bend – max pitch bend to use for this instrument
start_note_fixed – default
start_note_fixedfor notes started withstart_note()on this part.default_spelling_policy – the
default_spelling_policyfor the new partclef_preference – the
clef_preferencefor the new partvolume_cc_num – The cc value used for volume changes. Defaults to 11 (expression).
note_on_and_off_only – Deprecated; use start_note_fixed.
- Returns:
the newly created ScampInstrument
- new_silent_part(name: str = None, default_spelling_policy: SpellingPolicy | str | tuple = None, clef_preference='from_name', start_note_fixed: bool = True) ScampInstrument
Creates and returns a new ScampInstrument for this Ensemble with no PlaybackImplementations.
- Parameters:
name – name of the new part
default_spelling_policy – the
default_spelling_policyfor the new partclef_preference – the
clef_preferencefor the new partstart_note_fixed – the
start_note_fixeddefault for the new part. Only relevant if adding a MIDI playback implementation.
- Returns:
the newly created ScampInstrument
- pop_instrument(index)
Pops the instrument at the given index, severing its ties to the ensemble.
- Parameters:
index – which instrument
- property precise_timing: bool
Whether to use a busy wait in the final moments leading up to a scheduled event. The busy wait fully occupies a CPU core and lasts at most
spin_guard_durationseconds per event. (Forwards to the scheduler, settable only on the master clock)
- print_default_soundfont_presets() None
Prints a list of presets available with the default soundfont.
- print_family_tree() None
Print a hierarchical representation of this clock’s family tree.
- projected_beat() float
A wall-clock-interpolated estimate of
beat. Seeprojected_time().- Returns:
the estimated elapsed beats.
- projected_time() float
A wall-clock-interpolated estimate of
time, advancing smoothly between events rather than holding still between them. For readers outside the clock system; see the section comment above. (A method rather than a property, because it is a live sample: every call returns a different value.)This is an estimate, and it can step backward — see
projected_time().- Returns:
the estimated elapsed time, in the same units as
time.
- property rate: float
The rate of this clock in beats / second. Note that beat_length, tempo and rate are interconnected properties, and that by setting one of them the other two are automatically set in response according to the relationship: beat_length = 1/rate = 60/tempo. Also, note that “seconds” refers to actual seconds only in the master clock; otherwise it refers to beats in the parent clock.
- register_note(instrument: ScampInstrument, note_info: dict) None
Called when an instrument wants to register that it finished a note, records note in all transcriptions
- Parameters:
instrument – the ScampInstrument that played the note
note_info – the note info dictionary on that note, containing time stamps, parameter changes, etc.
- save_to_json(file_path: str) None
Save this object to a JSON file using the given path. This uses a custom encoder that recognizes and appropriately converts any attributes that are object inheriting from SavesToJSON.
- Parameters:
file_path – path for saving the file
- schedule_action(action: Callable, when: ResolvableMoment, args: Sequence = (), kwargs: dict = None) None
The lightweight alternative to fork() for immediately returning functions. Schedules action to run once at when, as a leaf event on the scheduler thread — without spawning a child clock. For functions that don’t wait, this is much more performant: there is no thread creation, no scheduler/clock handoff; just a callable fired at the right musical time.
For work that needs to wait, fork() a child clock instead. The function scheduled here will not have an active clock to wait on, and will hold up the entire scheduler if it sleeps.
- Parameters:
action – the callable to run (exceptions are caught and logged by the scheduler)
when – when to run it, as a
ResolvableMoment— same convention as fork(): an explicit Moment (Moment.at_beat/at_time for an absolute point, Moment.after_beats/ after_time for an offset from now) or a MetricPhaseTarget. Unlike wait and wait_until, a bare number is rejected here, since it’s not clear whether it would be relative or absolute.args – positional arguments to pass to action
kwargs – keyword arguments to pass to action
- scheduler_to_clock_time(scheduler_time, desired_units='beats')
Gets beats or time in this clock for a given time in the scheduler.
- Parameters:
scheduler_time – the time of interest in the scheduler
desired_units – one of (“beats”, “time”), whether we’re looking for beats or time in this clock
- Returns:
how much time should pass in the scheduler
- set_beat_length_target(beat_length_target: float, when: ResolvableMoment, curve_shape: float = None, truncate: bool = True, align_to: ResolvableMoment = None) None
Smoothly change this clock’s beat length (seconds per beat) to
beat_length_target, arriving at the moment given bywhen. This is the underlying representation behindset_tempo_target()andset_rate_target(); a longer beat length means a slower tempo.- Parameters:
beat_length_target – the beat length to arrive at, in seconds per beat.
when – when the target should be reached, as a
Moment(e.g.Moment.after_beats(4),Moment.at_time(10)) or aMetricPhaseTarget(the next point matching a particular phase within the beat cycle).after_*moments count from the clock’s current position. A plain number is rejected; wrap it in a Moment to clarify beat/time and relative/absolute.curve_shape – the bend of the transition.
0(the default) is a straight line;> 0keeps the old tempo longer and changes late;< 0changes early then eases in. Whenalign_tosolves for the curvature (see below) this acts only as a starting hint, and with a fixedalign_to, it is ignored entirely (and a warning is issued if set).truncate – if
True(the default), any tempo curve already scheduled past the current beat is discarded first, so the change starts from where the clock is right now. IfFalse, this target is appended after whatever is already scheduled.align_to – optional. If when is an arrival beat, this specifies the desired arrival time. If when is an arrival time, this specifies the desired arrival beat. A MetricPhaseTarget can be given instead of a singular time/beat, to express when we should arrive within a time/beat cycle. Note that the coordination of beat and time is done via mutating the curve shape, so an explicit curve shape is only a hint (ignored completely if align_to is a fixed point rather than a metric phase target).
- Raises:
ValueError – if
when/align_toshare an axis, or if no curvature can reach the requestedalign_to(in which case the tempo curve is left unchanged).
- set_beat_length_targets(beat_length_targets: Sequence[float], whens: Sequence[ResolvableMoment], curve_shapes: Sequence[float] = None, truncate: bool = True, align_to: ResolvableMoment | Sequence[ResolvableMoment | None] = None) None
Smoothly change this clock’s beat length (seconds per beat) through a series of targets, building a multi-segment tempo curve in one call — the multi-segment form of
set_beat_length_target(). This is non-looping; to apply a looping tempo shape, build aTempoEnvelopeand useapply_tempo_envelope().- Parameters:
beat_length_targets – the beat lengths (seconds per beat) to arrive at, one per segment.
whens – when each target is reached (same length as
beat_length_targets), each aMomentorMetricPhaseTarget. Note that every when is resolved against the clock’s current position, so anafter_*moment counts from now, not from the previous segment’s. Beats- and time-axis moments may be freely mixed across the list, so long as the whens come out in strictly increasing clock-time.curve_shapes – optional per-segment bends (same length as
beat_length_targets), each as inset_beat_length_target().Nonefor the whole argument (the default), or a per-elementNone, means linear (0) for that segment.truncate – if
True(the default), any tempo curve already scheduled past the current beat is discarded first, so the run starts from where the clock is now; ifFalsethe run is appended after whatever is already scheduled.align_to –
optional curvature-solved alignment, extending
set_beat_length_target()’salign_toto runs of segments. Either:a single
Moment/MetricPhaseTarget— align the whole call as one run, landing its end on the target; ora per-segment list/tuple (same length as
beat_length_targets) ofNone/targets — each non-Noneentry closes a run (all segments since the previous alignment, or the start) and bends them collectively so the run lands on that target.
A group-aligned run of more than one segment must have its
whenson a single axis (all beats or all time), with the alignment target on the opposite (free) axis.
- Raises:
ValueError – if
whens/curve_shapes/align_tolengths are inconsistent withbeat_length_targets; if thewhensdo not come out strictly increasing in clock-time (the error names the offending index); if a multi-segment aligned run mixes beat- and time-axiswhens; or if an alignment target is unreachable by curvature adjustment. On any failure the tempo curve is left unchanged.
- set_rate_target(rate_target: float, when: ResolvableMoment, curve_shape: float = None, truncate: bool = True, align_to: ResolvableMoment = None) None
Smoothly change this clock’s rate (beats per second) to
rate_target, arriving atwhen. Rate is the reciprocal of beat length: a higher rate is a faster tempo.- Parameters:
rate_target – the rate to arrive at, in beats per second.
when – when the target is reached. See
set_beat_length_target().curve_shape – the bend of the transition. See
set_beat_length_target().truncate – whether to discard already-scheduled tempo changes first. See
set_beat_length_target().align_to – optional; solve the curvature to land the free axis on a phase or coordinate. See
set_beat_length_target().
- Raises:
ValueError – see
set_beat_length_target().
- set_rate_targets(rate_targets: Sequence[float], whens: Sequence[ResolvableMoment], curve_shapes: Sequence[float] = None, truncate: bool = True, align_to: ResolvableMoment | Sequence[ResolvableMoment | None] = None) None
Smoothly change this clock’s rate (beats per second; the reciprocal of beat length, so a higher rate is a faster tempo) through a series of targets — the multi-segment form of
set_rate_target().- Parameters:
rate_targets – the rates (beats per second) to arrive at, one per segment.
whens – when each target is reached. See
set_beat_length_targets().curve_shapes – optional per-segment bends. See
set_beat_length_targets().truncate – whether to discard already-scheduled tempo changes first. See
set_beat_length_targets().align_to – optional curvature-solved alignment of segment runs. See
set_beat_length_targets().
- Raises:
ValueError – see
set_beat_length_targets().
- set_tempo_target(tempo_target: float, when: ResolvableMoment, curve_shape: float = None, truncate: bool = True, align_to: ResolvableMoment = None) None
Smoothly change this clock’s tempo to
tempo_target, arriving atwhen. Tempo is measured in beats per minute; this is usually the most natural of the three equivalent ways to set a target (see alsoset_rate_target()andset_beat_length_target()).- Parameters:
tempo_target – the tempo to arrive at, in beats per minute (quarter-notes per minute by convention).
when – when the target is reached. See
set_beat_length_target().curve_shape – the bend of the transition. See
set_beat_length_target().truncate – whether to discard already-scheduled tempo changes first. See
set_beat_length_target().align_to – optional; solve the curvature to land the free axis on a phase or coordinate. See
set_beat_length_target().
- Raises:
ValueError – see
set_beat_length_target().
- set_tempo_targets(tempo_targets: Sequence[float], whens: Sequence[ResolvableMoment], curve_shapes: Sequence[float] = None, truncate: bool = True, align_to: ResolvableMoment | Sequence[ResolvableMoment | None] = None) None
Smoothly change this clock’s tempo (beats per minute) through a series of targets — the multi-segment form of
set_tempo_target(), and usually the most natural of the three equivalent plural setters.- Parameters:
tempo_targets – the tempos (beats per minute) to arrive at, one per segment.
whens – when each target is reached. See
set_beat_length_targets().curve_shapes – optional per-segment bends. See
set_beat_length_targets().truncate – whether to discard already-scheduled tempo changes first. See
set_beat_length_targets().align_to – optional curvature-solved alignment of segment runs. See
set_beat_length_targets().
- Raises:
ValueError – see
set_beat_length_targets().
- property spin_guard_duration: float
Width (seconds) of the busy-wait guard band used when
precise_timingis on. (Forwards to the scheduler, settable only on the master clock)
- status(verbose: bool = False) str
A snapshot of this clock’s name and position, alongside the scheduler’s elapsed wall time and how far behind schedule it is currently running.
- stop_tempo_loop_or_function() None
Stop following any function or looping envelope previously applied to this clock’s tempo.
- stop_transcribing(which_performance=None, tempo_envelope_tolerance=0.001) Performance
Stops transcribing a Performance and returns it. Defaults to the oldest started performance, unless otherwise specified.
- Parameters:
which_performance – which performance to stop transcribing; defaults to oldest started
tempo_envelope_tolerance – error tolerance when extracting the absolute tempo envelope for the Performance
- Returns:
the created Performance
- property tempo: float
The rate of this clock in beats / minute Note that beat_length, tempo and rate are interconnected properties, and that by setting one of them the other two are automatically set in response according to the relationship: beat_length = 1/rate = 60/tempo. Also, note that “seconds” refers to actual seconds only in the master clock; otherwise it refers to beats in the parent clock.
- terminate_forked_children() None
Kill this clock’s children and their descendants in turn, returning once they have finished unwinding. At the natural end of a forked clock that has (or might have) still-active children, either this or
wait_for_children_to_finish()should be called, making explicit what to do with those forked children. (The default is termination with a warning, in case it’s not what the user wants.)
- property time: float
How much time has passed since this clock was created. Either in seconds, if this is the master clock, or in beats in the parent clock, if this clock was the result of a call to fork. Note that this is quantized to the scheduler’s event stream; for a live estimate when outside the clock system use
projected_time().(Read-only property. Before clockblocks 1.1 this was a method; the
clock.time()spelling still works, with a DeprecationWarning.)
- property timing_policy: float
How the family trades off relative vs. absolute timing, as a float from 0 to 1.
At 1.0 (relative) each wait is kept as faithful as possible to its requested duration, timed from when the previous event fired (callback runtime does not count against the wait). This can still let the clock fall behind real time: if a wait overruns (e.g. through a callback that runs longer than its own wait or the OS waking late) that lateness is never made up. At 0.0 (absolute) the clock instead stays faithful to the time elapsed since it began — a wait that ran long is followed by shorter waits to catch up, at the cost of some relative-timing accuracy. A value in between clamps the catch-up: the clock attempts to stay on the absolute schedule but can only compress a wait to wait_dur * timing_policy (or stretch it to wait_dur / timing_policy if trying to compensate for being ahead). (Forwards to the scheduler, settable only on the master clock)
- property transcriptions_in_progress: tuple[Performance]
Tuple of all current transcriptions.
- use_absolute_timing_policy() None
Shorthand for
timing_policy = 0.0(always catch up to absolute schedule).
- use_mixed_timing_policy(absolute_relative_mix: float) None
Shorthand for
timing_policy = absolute_relative_mix(a blend between 0=absolute and 1=relative).
- use_relative_timing_policy() None
Shorthand for
timing_policy = 1.0(always wait the full requested delay).
- wait(duration: float | ResolvableMoment, units: str = 'beats') None
Block this clock for duration beats (or seconds, if units=”time”) from now, yielding to the scheduler. duration may also be any ResolvableMoment (a Moment or MetricPhaseTarget), in which case it is resolved directly and units is ignored — e.g. wait(Moment.at_beat(8)) or wait(MetricPhaseTarget(0, 4)). wait_until() is just a convenience for absolute targets given as a bare number; passing an absolute Moment to wait() does the same thing.
- wait_for_children_to_finish() None
Block this clock’s own thread until all of its child clocks have finished, yielding to the scheduler so they can run, then return as soon as the last child ends.
If this clock is itself killed while waiting, raises
ClockKilledErrorrather than returning — so a normal return always means the children genuinely finished.
- wait_for_clock_to_finish(clock: Clock) None
Block this clock’s own thread until clock has finished, yielding to the scheduler so it (and everything else) keeps running. The counterpart to
wait_for_children_to_finish()for a specific clock rather than one’s own children — clock may be any other clock in the same family (a sibling, a descendant, an unrelated fork), so long as it shares this clock’s scheduler.Returns as soon as clock finishes, whether it ended on its own or was killed. If clock has already finished, returns immediately. If this clock is itself killed while waiting, raises
ClockKilledErrorrather than returning.- Parameters:
clock – the clock to wait on. Must belong to the same family (raises ValueError otherwise), and must not be this clock itself (which would wait forever).
- wait_forever() None
Block this clock’s own thread indefinitely, yielding to the scheduler so child clocks keep running. Typically called once a clock has forked its work and has nothing left to do itself (e.g. the master clock keeping the main thread alive).
This only ever unblocks via
kill(), at which point it raisesClockKilledError. For a forked clock, this is caught by the fork wrapper and it cleanly unwinds. For a master clock it would need to be caught. A built-in way to do this is by using the clock as a context manager, which does exception handling and teardown for you. (run_as_server()also absorbs the exception automatically within the spawned thread).That said, typically this is used at end of script and killed via ctrl-c/process exit, so no explicit catching of this exception is necessary.
- wait_until(when: float | ResolvableMoment, units: str = 'beats') None
Block this clock until the beat (or time, if units=”time”) indicated by when — a convenience for absolute targets given as a bare number (equivalent to wait(Moment.at_beat(when)), or Moment.at_time for units=”time”). when may also be any ResolvableMoment, in which case the units are ignored and behavior is identical to wait(). If when is in the past, returns essentially immediately.
- description: str | None