389 lines
12 KiB
Python
389 lines
12 KiB
Python
# Copyright 2016 Google Inc. All Rights Reserved.
|
|
#
|
|
# 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.
|
|
|
|
The `EventsEncoderDecoder` is an abstract class for translating between event
|
|
sequences and model data.
|
|
"""
|
|
|
|
import abc
|
|
import numpy as np
|
|
|
|
from six.moves import range # pylint: disable=redefined-builtin
|
|
|
|
from magenta.common import sequence_example_lib
|
|
from magenta.music 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 NonIntegerStepsPerBarException(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 MonophonicMelody 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. 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.
|
|
"""
|
|
__metaclass__ = abc.ABCMeta
|
|
|
|
@abc.abstractproperty
|
|
def start_step(self):
|
|
pass
|
|
|
|
@abc.abstractproperty
|
|
def end_step(self):
|
|
pass
|
|
|
|
@abc.abstractproperty
|
|
def steps_per_quarter(self):
|
|
pass
|
|
|
|
@abc.abstractproperty
|
|
def steps_per_bar(self):
|
|
pass
|
|
|
|
@abc.abstractmethod
|
|
def append_event(self, event):
|
|
"""Appends event to the end of the sequence and increments the end step.
|
|
|
|
Args:
|
|
event: The event to append to the end.
|
|
"""
|
|
pass
|
|
|
|
@abc.abstractmethod
|
|
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.
|
|
|
|
Args:
|
|
events: List of events.
|
|
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.
|
|
"""
|
|
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, pads with `pad_event` 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 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 MonophonicMelody, ChordProgression, and any other simple stream of
|
|
musical events.
|
|
"""
|
|
|
|
def __init__(self, pad_event):
|
|
"""Construct an empty SimpleEventSequence.
|
|
|
|
Args:
|
|
pad_event: Event value to use when padding sequences.
|
|
"""
|
|
self._pad_event = pad_event
|
|
self._reset()
|
|
|
|
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 __iter__(self):
|
|
"""Return an iterator over the events in this SimpleEventSequence.
|
|
|
|
Returns:
|
|
Python iterator over events.
|
|
"""
|
|
return iter(self._events)
|
|
|
|
def __getitem__(self, i):
|
|
"""Returns the event at the given index."""
|
|
return self._events[i]
|
|
|
|
def __getslice__(self, i, j):
|
|
"""Returns the events in the given slice range."""
|
|
return self._events[i:j]
|
|
|
|
def __len__(self):
|
|
"""How many events are in this SimpleEventSequence.
|
|
|
|
Returns:
|
|
Number of events as an integer.
|
|
"""
|
|
return len(self._events)
|
|
|
|
def __deepcopy__(self, unused_memo=None):
|
|
new_copy = type(self)(pad_event=self._pad_event)
|
|
new_copy.from_event_list(list(self._events),
|
|
self.start_step,
|
|
self.steps_per_bar,
|
|
self.steps_per_quarter)
|
|
return new_copy
|
|
|
|
def __eq__(self, other):
|
|
if not isinstance(other, SimpleEventSequence):
|
|
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_per_bar(self):
|
|
return self._steps_per_bar
|
|
|
|
@property
|
|
def steps_per_quarter(self):
|
|
return self._steps_per_quarter
|
|
|
|
def append_event(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 from_event_list(self, events, start_step=0,
|
|
steps_per_bar=DEFAULT_STEPS_PER_BAR,
|
|
steps_per_quarter=DEFAULT_STEPS_PER_QUARTER):
|
|
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 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
|
|
|
|
|
|
class EventsEncoderDecoder(object):
|
|
"""An abstract class for translating between events and model data.
|
|
|
|
When building your dataset, the `encode` method takes in an event sequence
|
|
and returns a SequenceExample of inputs and labels. These SequenceExamples
|
|
are fed into the model during training and evaluation.
|
|
|
|
During generation, the `get_inputs_batch` method takes in a list of the
|
|
current event sequences and returns an inputs batch which is fed into the
|
|
model to predict what the next event should be for each sequence. The
|
|
`extend_event_sequences` method takes in the list of event sequences and the
|
|
softmax returned by the model and extends each sequence by one step by
|
|
sampling from the softmax probabilities. This loop (`get_inputs_batch` ->
|
|
inputs batch is fed through the model to get a softmax ->
|
|
`extend_event_sequences`) is repeated until the generated event sequences
|
|
have reached the desired length.
|
|
|
|
The `events_to_input`, `events_to_label`, and `class_index_to_event` methods
|
|
must be overwritten to be specific to your model.
|
|
"""
|
|
__metaclass__ = abc.ABCMeta
|
|
|
|
def _encode(self, events):
|
|
"""Returns a SequenceExample for the given event sequence.
|
|
|
|
Args:
|
|
events: An EventSequence object.
|
|
|
|
Returns:
|
|
A tf.train.SequenceExample containing inputs and labels.
|
|
"""
|
|
inputs = []
|
|
labels = []
|
|
for i in range(len(events) - 1):
|
|
inputs.append(self.events_to_input(events, i))
|
|
labels.append(self.events_to_label(events, i + 1))
|
|
return sequence_example_lib.make_sequence_example(inputs, labels)
|
|
|
|
@abc.abstractproperty
|
|
def input_size(self):
|
|
"""The size of the input vector used by this model.
|
|
|
|
Returns:
|
|
An integer, the length of the list returned by self.events_to_input.
|
|
"""
|
|
pass
|
|
|
|
@abc.abstractproperty
|
|
def num_classes(self):
|
|
"""The range of labels used by this model.
|
|
|
|
Returns:
|
|
An integer, the range of integers that can be returned by
|
|
self.events_to_label.
|
|
"""
|
|
pass
|
|
|
|
@abc.abstractmethod
|
|
def events_to_input(self, events, position):
|
|
"""Returns the input vector for the event at the given position.
|
|
|
|
Args:
|
|
events: An EventSequence object.
|
|
position: An integer event position in the sequence.
|
|
|
|
Returns:
|
|
An input vector, a self.input_size length list of floats.
|
|
"""
|
|
pass
|
|
|
|
@abc.abstractmethod
|
|
def events_to_label(self, events, position):
|
|
"""Returns the label for the event at the given position.
|
|
|
|
Args:
|
|
events: An EventSequence object.
|
|
position: An integer event position in the sequence.
|
|
|
|
Returns:
|
|
A label, an integer in the range [0, self.num_classes).
|
|
"""
|
|
pass
|
|
|
|
def get_inputs_batch(self, event_sequences, full_length=False):
|
|
"""Returns an inputs batch for the given event sequences.
|
|
|
|
Args:
|
|
event_sequences: A list of EventSequence objects.
|
|
full_length: If True, the inputs batch will be for the full length of
|
|
each event sequence. If False, the inputs batch will only be for the
|
|
last event of each event sequence. A full-length inputs batch is used
|
|
for the first step of extending the event sequences, since the RNN
|
|
cell state needs to be initialized with the priming sequence. For
|
|
subsequent generation steps, only a last-event inputs batch is used.
|
|
|
|
Returns:
|
|
An inputs batch. If `full_length` is True, the shape will be
|
|
[len(event_sequences), len(event_sequences[0]), INPUT_SIZE]. If
|
|
`full_length` is False, the shape will be
|
|
[len(event_sequences), 1, INPUT_SIZE].
|
|
"""
|
|
inputs_batch = []
|
|
for events in event_sequences:
|
|
inputs = []
|
|
if full_length and len(event_sequences):
|
|
for i in range(len(events)):
|
|
inputs.append(self.events_to_input(events, i))
|
|
else:
|
|
inputs.append(self.events_to_input(events, len(events) - 1))
|
|
inputs_batch.append(inputs)
|
|
return inputs_batch
|
|
|
|
@abc.abstractmethod
|
|
def class_index_to_event(self, class_index, events):
|
|
"""Returns the event for the given class index.
|
|
|
|
This is the reverse process of the self.events_to_label method.
|
|
|
|
Args:
|
|
class_index: An integer in the range [0, self.num_classes).
|
|
events: An EventSequence object.
|
|
|
|
Returns:
|
|
An event value.
|
|
"""
|
|
pass
|
|
|
|
def extend_event_sequences(self, event_sequences, softmax):
|
|
"""Extends the event_sequences by sampling the softmax probabilities.
|
|
|
|
Args:
|
|
event_sequences: A list of EventSequence objects.
|
|
softmax: A list of softmax probability vectors. The list of softmaxes
|
|
should be the same length as the list of event_sequences.
|
|
"""
|
|
num_classes = len(softmax[0][0])
|
|
for i in xrange(len(event_sequences)):
|
|
chosen_class = np.random.choice(num_classes, p=softmax[i][-1])
|
|
event = self.class_index_to_event(chosen_class, event_sequences[i])
|
|
event_sequences[i].append_event(event)
|