Module note_seq.events_lib
Abstract base classes for working with musical event sequences.
The abstract EventSequence
class is an interface for a sequence of musical
events. The SimpleEventSequence
class is a basic implementation of this
interface.
Expand source code
# Copyright 2021 The Magenta Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Abstract base classes for working with musical event sequences.
The abstract `EventSequence` class is an interface for a sequence of musical
events. The `SimpleEventSequence` class is a basic implementation of this
interface.
"""
import abc
import copy
from note_seq import constants
DEFAULT_STEPS_PER_BAR = constants.DEFAULT_STEPS_PER_BAR
DEFAULT_STEPS_PER_QUARTER = constants.DEFAULT_STEPS_PER_QUARTER
STANDARD_PPQ = constants.STANDARD_PPQ
class NonIntegerStepsPerBarError(Exception):
pass
class EventSequence(object):
"""Stores a quantized stream of events.
EventSequence is an abstract class to use as an interface for interacting
with musical event sequences. Concrete implementations SimpleEventSequence
(and its descendants Melody and ChordProgression) and LeadSheet represent
sequences of musical events of particular types. In all cases, model-specific
code is responsible for converting this representation to SequenceExample
protos for TensorFlow.
EventSequence represents an iterable object. Simply iterate to retrieve the
events.
Attributes:
start_step: The offset of the first step of the sequence relative to the
beginning of the source sequence.
end_step: The offset to the beginning of the bar following the last step
of the sequence relative to the beginning of the source sequence.
steps: A Python list containing the time step at each event of the sequence.
"""
__metaclass__ = abc.ABCMeta
@property
@abc.abstractmethod
def start_step(self):
pass
@property
@abc.abstractmethod
def end_step(self):
pass
@property
@abc.abstractmethod
def steps(self):
pass
@abc.abstractmethod
def append(self, event):
"""Appends event to the end of the sequence.
Args:
event: The event to append to the end.
"""
pass
@abc.abstractmethod
def set_length(self, steps, from_left=False):
"""Sets the length of the sequence to the specified number of steps.
If the event sequence is not long enough, will pad to make the sequence
the specified length. If it is too long, it will be truncated to the
requested length.
Args:
steps: How many steps long the event sequence should be.
from_left: Whether to add/remove from the left instead of right.
"""
pass
@abc.abstractmethod
def __getitem__(self, i):
"""Returns the event at the given index."""
pass
@abc.abstractmethod
def __iter__(self):
"""Returns an iterator over the events."""
pass
@abc.abstractmethod
def __len__(self):
"""How many events are in this EventSequence.
Returns:
Number of events as an integer.
"""
pass
class SimpleEventSequence(EventSequence):
"""Stores a quantized stream of events.
This class can be instantiated, but its main purpose is to serve as a base
class for Melody, ChordProgression, and any other simple stream of musical
events.
SimpleEventSequence represents an iterable object. Simply iterate to retrieve
the events.
Attributes:
start_step: The offset of the first step of the sequence relative to the
beginning of the source sequence. Should always be the first step of a
bar.
end_step: The offset to the beginning of the bar following the last step
of the sequence relative to the beginning of the source sequence. Will
always be the first step of a bar.
steps_per_quarter: Number of steps in in a quarter note.
steps_per_bar: Number of steps in a bar (measure) of music.
"""
def __init__(self, pad_event, events=None, start_step=0,
steps_per_bar=DEFAULT_STEPS_PER_BAR,
steps_per_quarter=DEFAULT_STEPS_PER_QUARTER):
"""Construct a SimpleEventSequence.
If `events` is specified, instantiate with the provided event list.
Otherwise, create an empty SimpleEventSequence.
Args:
pad_event: Event value to use when padding sequences.
events: List of events to instantiate with.
start_step: The integer starting step offset.
steps_per_bar: The number of steps in a bar.
steps_per_quarter: The number of steps in a quarter note.
"""
self._pad_event = pad_event
if events is not None:
self._from_event_list(events, start_step=start_step,
steps_per_bar=steps_per_bar,
steps_per_quarter=steps_per_quarter)
else:
self._events = []
self._steps_per_bar = steps_per_bar
self._steps_per_quarter = steps_per_quarter
self._start_step = start_step
self._end_step = start_step
def _reset(self):
"""Clear events and reset object state."""
self._events = []
self._steps_per_bar = DEFAULT_STEPS_PER_BAR
self._steps_per_quarter = DEFAULT_STEPS_PER_QUARTER
self._start_step = 0
self._end_step = 0
def _from_event_list(self, events, start_step=0,
steps_per_bar=DEFAULT_STEPS_PER_BAR,
steps_per_quarter=DEFAULT_STEPS_PER_QUARTER):
"""Initializes with a list of event values and sets attributes."""
self._events = list(events)
self._start_step = start_step
self._end_step = start_step + len(self)
self._steps_per_bar = steps_per_bar
self._steps_per_quarter = steps_per_quarter
def __iter__(self):
"""Return an iterator over the events in this SimpleEventSequence.
Returns:
Python iterator over events.
"""
return iter(self._events)
def __getitem__(self, key):
"""Returns the slice or individual item."""
if isinstance(key, int):
return self._events[key]
elif isinstance(key, slice):
events = self._events.__getitem__(key)
return type(self)(pad_event=self._pad_event,
events=events,
start_step=self.start_step + (key.start or 0),
steps_per_bar=self.steps_per_bar,
steps_per_quarter=self.steps_per_quarter)
def __len__(self):
"""How many events are in this SimpleEventSequence.
Returns:
Number of events as an integer.
"""
return len(self._events)
def __deepcopy__(self, memo=None):
return type(self)(pad_event=self._pad_event,
events=copy.deepcopy(self._events, memo),
start_step=self.start_step,
steps_per_bar=self.steps_per_bar,
steps_per_quarter=self.steps_per_quarter)
def __eq__(self, other):
if type(self) is not type(other):
return False
return (list(self) == list(other) and
self.steps_per_bar == other.steps_per_bar and
self.steps_per_quarter == other.steps_per_quarter and
self.start_step == other.start_step and
self.end_step == other.end_step)
@property
def start_step(self):
return self._start_step
@property
def end_step(self):
return self._end_step
@property
def steps(self):
return list(range(self._start_step, self._end_step))
@property
def steps_per_bar(self):
return self._steps_per_bar
@property
def steps_per_quarter(self):
return self._steps_per_quarter
def append(self, event):
"""Appends event to the end of the sequence and increments the end step.
Args:
event: The event to append to the end.
"""
self._events.append(event)
self._end_step += 1
def set_length(self, steps, from_left=False):
"""Sets the length of the sequence to the specified number of steps.
If the event sequence is not long enough, pads to make the sequence the
specified length. If it is too long, it will be truncated to the requested
length.
Args:
steps: How many steps long the event sequence should be.
from_left: Whether to add/remove from the left instead of right.
"""
if steps > len(self):
if from_left:
self._events[:0] = [self._pad_event] * (steps - len(self))
else:
self._events.extend([self._pad_event] * (steps - len(self)))
else:
if from_left:
del self._events[0:-steps]
else:
del self._events[steps:]
if from_left:
self._start_step = self._end_step - steps
else:
self._end_step = self._start_step + steps
def increase_resolution(self, k, fill_event=None):
"""Increase the resolution of an event sequence.
Increases the resolution of a SimpleEventSequence object by a factor of
`k`.
Args:
k: An integer, the factor by which to increase the resolution of the
event sequence.
fill_event: Event value to use to extend each low-resolution event. If
None, each low-resolution event value will be repeated `k` times.
"""
if fill_event is None:
fill = lambda event: [event] * k
else:
fill = lambda event: [event] + [fill_event] * (k - 1)
new_events = []
for event in self._events:
new_events += fill(event)
self._events = new_events
self._start_step *= k
self._end_step *= k
self._steps_per_bar *= k
self._steps_per_quarter *= k
Classes
class EventSequence
-
Stores a quantized stream of events.
EventSequence is an abstract class to use as an interface for interacting with musical event sequences. Concrete implementations SimpleEventSequence (and its descendants Melody and ChordProgression) and LeadSheet represent sequences of musical events of particular types. In all cases, model-specific code is responsible for converting this representation to SequenceExample protos for TensorFlow.
EventSequence represents an iterable object. Simply iterate to retrieve the events.
Attributes
start_step
- The offset of the first step of the sequence relative to the beginning of the source sequence.
end_step
- The offset to the beginning of the bar following the last step of the sequence relative to the beginning of the source sequence.
steps
- A Python list containing the time step at each event of the sequence.
Expand source code
class EventSequence(object): """Stores a quantized stream of events. EventSequence is an abstract class to use as an interface for interacting with musical event sequences. Concrete implementations SimpleEventSequence (and its descendants Melody and ChordProgression) and LeadSheet represent sequences of musical events of particular types. In all cases, model-specific code is responsible for converting this representation to SequenceExample protos for TensorFlow. EventSequence represents an iterable object. Simply iterate to retrieve the events. Attributes: start_step: The offset of the first step of the sequence relative to the beginning of the source sequence. end_step: The offset to the beginning of the bar following the last step of the sequence relative to the beginning of the source sequence. steps: A Python list containing the time step at each event of the sequence. """ __metaclass__ = abc.ABCMeta @property @abc.abstractmethod def start_step(self): pass @property @abc.abstractmethod def end_step(self): pass @property @abc.abstractmethod def steps(self): pass @abc.abstractmethod def append(self, event): """Appends event to the end of the sequence. Args: event: The event to append to the end. """ pass @abc.abstractmethod def set_length(self, steps, from_left=False): """Sets the length of the sequence to the specified number of steps. If the event sequence is not long enough, will pad to make the sequence the specified length. If it is too long, it will be truncated to the requested length. Args: steps: How many steps long the event sequence should be. from_left: Whether to add/remove from the left instead of right. """ pass @abc.abstractmethod def __getitem__(self, i): """Returns the event at the given index.""" pass @abc.abstractmethod def __iter__(self): """Returns an iterator over the events.""" pass @abc.abstractmethod def __len__(self): """How many events are in this EventSequence. Returns: Number of events as an integer. """ pass
Subclasses
Instance variables
var end_step
-
Expand source code
@property @abc.abstractmethod def end_step(self): pass
var start_step
-
Expand source code
@property @abc.abstractmethod def start_step(self): pass
var steps
-
Expand source code
@property @abc.abstractmethod def steps(self): pass
Methods
def append(self, event)
-
Appends event to the end of the sequence.
Args
event
- The event to append to the end.
Expand source code
@abc.abstractmethod def append(self, event): """Appends event to the end of the sequence. Args: event: The event to append to the end. """ pass
def set_length(self, steps, from_left=False)
-
Sets the length of the sequence to the specified number of steps.
If the event sequence is not long enough, will pad to make the sequence the specified length. If it is too long, it will be truncated to the requested length.
Args
steps
- How many steps long the event sequence should be.
from_left
- Whether to add/remove from the left instead of right.
Expand source code
@abc.abstractmethod def set_length(self, steps, from_left=False): """Sets the length of the sequence to the specified number of steps. If the event sequence is not long enough, will pad to make the sequence the specified length. If it is too long, it will be truncated to the requested length. Args: steps: How many steps long the event sequence should be. from_left: Whether to add/remove from the left instead of right. """ pass
class NonIntegerStepsPerBarError (*args, **kwargs)
-
Common base class for all non-exit exceptions.
Expand source code
class NonIntegerStepsPerBarError(Exception): pass
Ancestors
- builtins.Exception
- builtins.BaseException
class SimpleEventSequence (pad_event, events=None, start_step=0, steps_per_bar=16, steps_per_quarter=4)
-
Stores a quantized stream of events.
This class can be instantiated, but its main purpose is to serve as a base class for Melody, ChordProgression, and any other simple stream of musical events.
SimpleEventSequence represents an iterable object. Simply iterate to retrieve the events.
Attributes
start_step
- The offset of the first step of the sequence relative to the beginning of the source sequence. Should always be the first step of a bar.
end_step
- The offset to the beginning of the bar following the last step of the sequence relative to the beginning of the source sequence. Will always be the first step of a bar.
steps_per_quarter
- Number of steps in in a quarter note.
steps_per_bar
- Number of steps in a bar (measure) of music.
Construct a SimpleEventSequence.
If
events
is specified, instantiate with the provided event list. Otherwise, create an empty SimpleEventSequence.Args
pad_event
- Event value to use when padding sequences.
events
- List of events to instantiate with.
start_step
- The integer starting step offset.
steps_per_bar
- The number of steps in a bar.
steps_per_quarter
- The number of steps in a quarter note.
Expand source code
class SimpleEventSequence(EventSequence): """Stores a quantized stream of events. This class can be instantiated, but its main purpose is to serve as a base class for Melody, ChordProgression, and any other simple stream of musical events. SimpleEventSequence represents an iterable object. Simply iterate to retrieve the events. Attributes: start_step: The offset of the first step of the sequence relative to the beginning of the source sequence. Should always be the first step of a bar. end_step: The offset to the beginning of the bar following the last step of the sequence relative to the beginning of the source sequence. Will always be the first step of a bar. steps_per_quarter: Number of steps in in a quarter note. steps_per_bar: Number of steps in a bar (measure) of music. """ def __init__(self, pad_event, events=None, start_step=0, steps_per_bar=DEFAULT_STEPS_PER_BAR, steps_per_quarter=DEFAULT_STEPS_PER_QUARTER): """Construct a SimpleEventSequence. If `events` is specified, instantiate with the provided event list. Otherwise, create an empty SimpleEventSequence. Args: pad_event: Event value to use when padding sequences. events: List of events to instantiate with. start_step: The integer starting step offset. steps_per_bar: The number of steps in a bar. steps_per_quarter: The number of steps in a quarter note. """ self._pad_event = pad_event if events is not None: self._from_event_list(events, start_step=start_step, steps_per_bar=steps_per_bar, steps_per_quarter=steps_per_quarter) else: self._events = [] self._steps_per_bar = steps_per_bar self._steps_per_quarter = steps_per_quarter self._start_step = start_step self._end_step = start_step def _reset(self): """Clear events and reset object state.""" self._events = [] self._steps_per_bar = DEFAULT_STEPS_PER_BAR self._steps_per_quarter = DEFAULT_STEPS_PER_QUARTER self._start_step = 0 self._end_step = 0 def _from_event_list(self, events, start_step=0, steps_per_bar=DEFAULT_STEPS_PER_BAR, steps_per_quarter=DEFAULT_STEPS_PER_QUARTER): """Initializes with a list of event values and sets attributes.""" self._events = list(events) self._start_step = start_step self._end_step = start_step + len(self) self._steps_per_bar = steps_per_bar self._steps_per_quarter = steps_per_quarter def __iter__(self): """Return an iterator over the events in this SimpleEventSequence. Returns: Python iterator over events. """ return iter(self._events) def __getitem__(self, key): """Returns the slice or individual item.""" if isinstance(key, int): return self._events[key] elif isinstance(key, slice): events = self._events.__getitem__(key) return type(self)(pad_event=self._pad_event, events=events, start_step=self.start_step + (key.start or 0), steps_per_bar=self.steps_per_bar, steps_per_quarter=self.steps_per_quarter) def __len__(self): """How many events are in this SimpleEventSequence. Returns: Number of events as an integer. """ return len(self._events) def __deepcopy__(self, memo=None): return type(self)(pad_event=self._pad_event, events=copy.deepcopy(self._events, memo), start_step=self.start_step, steps_per_bar=self.steps_per_bar, steps_per_quarter=self.steps_per_quarter) def __eq__(self, other): if type(self) is not type(other): return False return (list(self) == list(other) and self.steps_per_bar == other.steps_per_bar and self.steps_per_quarter == other.steps_per_quarter and self.start_step == other.start_step and self.end_step == other.end_step) @property def start_step(self): return self._start_step @property def end_step(self): return self._end_step @property def steps(self): return list(range(self._start_step, self._end_step)) @property def steps_per_bar(self): return self._steps_per_bar @property def steps_per_quarter(self): return self._steps_per_quarter def append(self, event): """Appends event to the end of the sequence and increments the end step. Args: event: The event to append to the end. """ self._events.append(event) self._end_step += 1 def set_length(self, steps, from_left=False): """Sets the length of the sequence to the specified number of steps. If the event sequence is not long enough, pads to make the sequence the specified length. If it is too long, it will be truncated to the requested length. Args: steps: How many steps long the event sequence should be. from_left: Whether to add/remove from the left instead of right. """ if steps > len(self): if from_left: self._events[:0] = [self._pad_event] * (steps - len(self)) else: self._events.extend([self._pad_event] * (steps - len(self))) else: if from_left: del self._events[0:-steps] else: del self._events[steps:] if from_left: self._start_step = self._end_step - steps else: self._end_step = self._start_step + steps def increase_resolution(self, k, fill_event=None): """Increase the resolution of an event sequence. Increases the resolution of a SimpleEventSequence object by a factor of `k`. Args: k: An integer, the factor by which to increase the resolution of the event sequence. fill_event: Event value to use to extend each low-resolution event. If None, each low-resolution event value will be repeated `k` times. """ if fill_event is None: fill = lambda event: [event] * k else: fill = lambda event: [event] + [fill_event] * (k - 1) new_events = [] for event in self._events: new_events += fill(event) self._events = new_events self._start_step *= k self._end_step *= k self._steps_per_bar *= k self._steps_per_quarter *= k
Ancestors
Subclasses
Instance variables
var end_step
-
Expand source code
@property def end_step(self): return self._end_step
var start_step
-
Expand source code
@property def start_step(self): return self._start_step
var steps
-
Expand source code
@property def steps(self): return list(range(self._start_step, self._end_step))
var steps_per_bar
-
Expand source code
@property def steps_per_bar(self): return self._steps_per_bar
var steps_per_quarter
-
Expand source code
@property def steps_per_quarter(self): return self._steps_per_quarter
Methods
def append(self, event)
-
Appends event to the end of the sequence and increments the end step.
Args
event
- The event to append to the end.
Expand source code
def append(self, event): """Appends event to the end of the sequence and increments the end step. Args: event: The event to append to the end. """ self._events.append(event) self._end_step += 1
def increase_resolution(self, k, fill_event=None)
-
Increase the resolution of an event sequence.
Increases the resolution of a SimpleEventSequence object by a factor of
k
.Args
k
- An integer, the factor by which to increase the resolution of the event sequence.
fill_event
- Event value to use to extend each low-resolution event. If
None, each low-resolution event value will be repeated
k
times.
Expand source code
def increase_resolution(self, k, fill_event=None): """Increase the resolution of an event sequence. Increases the resolution of a SimpleEventSequence object by a factor of `k`. Args: k: An integer, the factor by which to increase the resolution of the event sequence. fill_event: Event value to use to extend each low-resolution event. If None, each low-resolution event value will be repeated `k` times. """ if fill_event is None: fill = lambda event: [event] * k else: fill = lambda event: [event] + [fill_event] * (k - 1) new_events = [] for event in self._events: new_events += fill(event) self._events = new_events self._start_step *= k self._end_step *= k self._steps_per_bar *= k self._steps_per_quarter *= k
def set_length(self, steps, from_left=False)
-
Sets the length of the sequence to the specified number of steps.
If the event sequence is not long enough, pads to make the sequence the specified length. If it is too long, it will be truncated to the requested length.
Args
steps
- How many steps long the event sequence should be.
from_left
- Whether to add/remove from the left instead of right.
Expand source code
def set_length(self, steps, from_left=False): """Sets the length of the sequence to the specified number of steps. If the event sequence is not long enough, pads to make the sequence the specified length. If it is too long, it will be truncated to the requested length. Args: steps: How many steps long the event sequence should be. from_left: Whether to add/remove from the left instead of right. """ if steps > len(self): if from_left: self._events[:0] = [self._pad_event] * (steps - len(self)) else: self._events.extend([self._pad_event] * (steps - len(self))) else: if from_left: del self._events[0:-steps] else: del self._events[steps:] if from_left: self._start_step = self._end_step - steps else: self._end_step = self._start_step + steps