Skip to content

Commit

Permalink
Better handling of defaults; add Timeline.defaults.key
Browse files Browse the repository at this point in the history
  • Loading branch information
ideoforms committed Feb 19, 2021
1 parent 4e35564 commit 89643ae
Show file tree
Hide file tree
Showing 7 changed files with 40 additions and 32 deletions.
12 changes: 11 additions & 1 deletion isobar/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,15 @@
EVENT_SUPERCOLLIDER_SYNTH = "synth"
EVENT_SUPERCOLLIDER_SYNTH_PARAMS = "params"

#------------------------------------------------------------------------
# Quantize is used to beat-align events, so should not be included
# directly in the dictionary.
#------------------------------------------------------------------------
EVENT_QUANTIZE = "quantize"

#------------------------------------------------------------------------
# Legacy keys.
#------------------------------------------------------------------------
EVENT_DURATION_LEGACY = "dur"
EVENT_AMPLITUDE_LEGACY = "amp"

Expand All @@ -72,7 +81,7 @@
EVENT_TRANSPOSE, EVENT_EVENT, EVENT_ACTION, EVENT_ACTION_ARGS, EVENT_CONTROL,
EVENT_OSC_ADDRESS, EVENT_OSC_PARAMS, EVENT_VALUE, EVENT_TIME, EVENT_PATCH,
EVENT_PATCH_PARAMS, EVENT_PROGRAM_CHANGE, EVENT_SUPERCOLLIDER_SYNTH,
EVENT_SUPERCOLLIDER_SYNTH_PARAMS,
EVENT_SUPERCOLLIDER_SYNTH_PARAMS, EVENT_QUANTIZE,
EVENT_DURATION_LEGACY, EVENT_AMPLITUDE_LEGACY
]

Expand All @@ -97,6 +106,7 @@
DEFAULT_EVENT_AMPLITUDE = 64
DEFAULT_EVENT_OCTAVE = 0
DEFAULT_EVENT_TRANSPOSE = 0
DEFAULT_EVENT_QUANTIZE = 0

#------------------------------------------------------------------------
# Interpolation modes for continuous signals.
Expand Down
5 changes: 0 additions & 5 deletions isobar/key.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from .scale import Scale
from .note import Note
from .util import midi_note_to_note_name, note_name_to_midi_note
from .exceptions import InvalidKeyException

import random

Expand All @@ -14,10 +13,6 @@ def __init__(self, tonic=0, scale=Scale.major):
tonic = note_name_to_midi_note(tonic)
if type(scale) == str:
scale = Scale.byname(scale)
if tonic < 0:
raise InvalidKeyException("Tonic must be >= 0")
if tonic >= scale.octave_size:
raise InvalidKeyException("Tonic cannot be beyond octave size")

self.tonic = tonic
self.scale = scale
Expand Down
31 changes: 18 additions & 13 deletions isobar/timeline/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,8 @@

log = logging.getLogger(__name__)

class Event:
def __init__(self, event_values):
for key in event_values.keys():
if key not in ALL_EVENT_PARAMETERS:
raise ValueError("Invalid key for event: %s" % (key))

if EVENT_DURATION_LEGACY in event_values:
event_values[EVENT_DURATION] = event_values[EVENT_DURATION_LEGACY]
if EVENT_AMPLITUDE_LEGACY in event_values:
event_values[EVENT_AMPLITUDE] = event_values[EVENT_AMPLITUDE_LEGACY]

class EventDefaults:
def __init__(self):
default_values = {
EVENT_ACTIVE: True,
EVENT_CHANNEL: DEFAULT_EVENT_CHANNEL,
Expand All @@ -27,10 +18,24 @@ def __init__(self, event_values):
EVENT_AMPLITUDE: DEFAULT_EVENT_AMPLITUDE,
EVENT_OCTAVE: DEFAULT_EVENT_OCTAVE,
EVENT_TRANSPOSE: DEFAULT_EVENT_TRANSPOSE,
EVENT_KEY: Key("C", Scale.default)
EVENT_KEY: Key("C", Scale.default),
EVENT_QUANTIZE: DEFAULT_EVENT_QUANTIZE
}

for key, value in default_values.items():
setattr(self, key, value)

class Event:
def __init__(self, event_values, defaults=EventDefaults()):
for key in event_values.keys():
if key not in ALL_EVENT_PARAMETERS:
raise ValueError("Invalid key for event: %s" % (key))

if EVENT_DURATION_LEGACY in event_values:
event_values[EVENT_DURATION] = event_values[EVENT_DURATION_LEGACY]
if EVENT_AMPLITUDE_LEGACY in event_values:
event_values[EVENT_AMPLITUDE] = event_values[EVENT_AMPLITUDE_LEGACY]

for key, value in defaults.__dict__.items():
event_values.setdefault(key, value)

if EVENT_NOTE in event_values and EVENT_DEGREE in event_values:
Expand Down
7 changes: 5 additions & 2 deletions isobar/timeline/timeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

from .track import Track
from .clock import Clock
from .event import EventDefaults
from ..key import Key
from ..io import MidiOutputDevice
from ..constants import DEFAULT_TICKS_PER_BEAT, DEFAULT_TEMPO
from ..constants import EVENT_TIME, EVENT_ACTION, INTERPOLATION_NONE
Expand Down Expand Up @@ -50,9 +52,10 @@ def __init__(self,
self.stop_when_done = False
self.events = []
self.running = False
self.default_quantize = None
self.ignore_exceptions = False

self.defaults = EventDefaults()

def get_clock_source(self):
return self._clock_source

Expand Down Expand Up @@ -347,7 +350,7 @@ def start_track(track):
output_device=output_device, remove_when_done=remove_when_done)

if quantize is None:
quantize = self.default_quantize
quantize = self.defaults.quantize
if quantize or delay:
#--------------------------------------------------------------------------------
# We don't want to begin events right away -- either wait till
Expand Down
8 changes: 4 additions & 4 deletions isobar/timeline/track.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def update(self, events, quantize=None):
events = PDict(events)

if quantize is None:
quantize = self.timeline.default_quantize
quantize = self.timeline.defaults.quantize

if quantize:
self.next_event_time = quantize * math.ceil(float(self.current_time) / quantize)
Expand Down Expand Up @@ -131,7 +131,7 @@ def tick(self):
#--------------------------------------------------------------------------------
try:
interpolated_values = next(self.interpolating_event)
interpolated_event = Event(interpolated_values)
interpolated_event = Event(interpolated_values, self.timeline.defaults)
self.perform_event(interpolated_event)
except StopIteration:
is_first_event = False
Expand Down Expand Up @@ -178,7 +178,7 @@ def tick(self):
self.interpolating_event = PDict(interpolating_event_fields)
if not is_first_event:
next(self.interpolating_event)
event = Event(next(self.interpolating_event))
event = Event(next(self.interpolating_event), self.timeline.defaults)
self.perform_event(event)

except StopIteration:
Expand Down Expand Up @@ -230,7 +230,7 @@ def get_next_event(self):
event_values = next(self.event_stream)
event_values = copy.copy(event_values)

event = Event(event_values)
event = Event(event_values, self.timeline.defaults)
self.current_event_count += 1

return event
Expand Down
5 changes: 0 additions & 5 deletions tests/test_key.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,6 @@ def test_key_invalid():
with pytest.raises(iso.UnknownScaleName):
a = iso.Key("C", "mundo")

with pytest.raises(iso.InvalidKeyException):
a = iso.Key(-1, "major")
with pytest.raises(iso.InvalidKeyException):
a = iso.Key(12, "minor")

def test_key_get():
a = iso.Key("C", "major")
assert a.get(0) == a[0] == 0
Expand Down
4 changes: 2 additions & 2 deletions tests/test_timeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ def test_timeline_schedule_quantize_on_beat(dummy_timeline, quantize):
assert dummy_timeline.output_device.events[1] == [pytest.approx(1.0, abs=dummy_timeline.tick_duration), "note_off", 1, 0]

def test_timeline_schedule_default_quantize(dummy_timeline):
dummy_timeline.default_quantize = 1
dummy_timeline.defaults.quantize = 1
dummy_timeline.stop_when_done = False
dummy_timeline.tick()
dummy_timeline.stop_when_done = True
Expand All @@ -179,7 +179,7 @@ def test_timeline_schedule_default_quantize(dummy_timeline):
assert dummy_timeline.output_device.events[1] == [pytest.approx(2, abs=dummy_timeline.tick_duration), "note_off", 1, 0]

def test_timeline_schedule_default_quantize_override(dummy_timeline):
dummy_timeline.default_quantize = 1
dummy_timeline.defaults.quantize = 1
dummy_timeline.stop_when_done = False
dummy_timeline.tick()
dummy_timeline.stop_when_done = True
Expand Down

0 comments on commit 89643ae

Please sign in to comment.