Tutorial: Use the WorkflowManager

The script developed in Tutorial: Use N-Grams to Find Melodic Patterns is suitable for users doing exploratory work in an interactive Python shell. When a query becomes regularized and you want it to be easily repeatable, or if you are an application developer making a graphical interface (whether on the Web or in a desktop application) you can take advantage of a further layer of abstraction offered by our WorkflowManager. The WorkflowManager is designed as the point of interaction for end-user applications, providing a consistent interface and reference implementations of the steps involved in all queries. While every new query will involve modifying a portion of the run() method’s code, you may be able to re-use the existing input and output methods without change.

The WorkflowManager‘s documentation describes its functionality:

class vis.workflow.WorkflowManager(pathnames)[source]

Warning: The WorkflowManager is deprecated as of VIS 3.0 and will be entirely removed in VIS 4.0. Most of its functionality still works with VIS 3.0 but this is not guaranteed and it is no longer being supported in development.

Parameters:pathnames (list or tuple of string or IndexedPiece) – A list of pathnames.

The WorkflowManager automates several common music analysis patterns for counterpoint. Use the WorkflowManager with these four tasks:

  • load(), to import pieces from symbolic data formats.
  • run(), to perform a pre-defined analysis.
  • output(), to output analysis results.

Before you analyze, you may wish to use these methods:

  • metadata(), to get or set the metadata of a specific IndexedPiece managed by this WorkflowManager.
  • settings(), to get or set a setting related to analysis (for example, whether to display the quality of intervals).

You may also treat a WorkflowManager as a container:

>>> wm = WorkflowManager(['piece1.mxl', 'piece2.krn'])
>>> len(wm)
2
>>> ip = wm[1]
>>> type(ip)
<class 'vis.models.indexed_piece.IndexedPiece'>

Port a Query into the WorkflowManager

Porting an existing query to the WorkflowManager involves fitting its code into the appropriate pre-existing methods. The load() method prepares IndexedPiece objects and metadata by loading files for analysis. The output() method outputs query results to a variety of formats, including spreadsheets, charts, and scores. You will not usually need to modify load(), and you may not need to modify output() either.

The majority of development work will be spent in the run() method or its related hidden methods (like the _intervs(), _inteval_ngrams(), and other methods that are included in the default WorkflowManager).

TODO: continue revising here.

When I add my new query’s logic to the run() method, I get this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
def run(self):
    ngram_settings = {'vertical': [0], 'n': self.settigns(None, 'n')}
    ngram_freqs = []

    for i, piece in enumerate(self._data):
        interval_settings = {'quality': self.settings(i, 'interval quality')}
        intervals = piece.get_data( \
            [noterest.NoteRestIndexer, interval.HorizontalIntervalIndexer], \
            interval_settings)
        for part in intervals:
            ngram_freqs.append( \
                piece.get_data([ngram.NGramIndexer, frequency.FrequencyExperimenter], \
                               ngram_settings, \
                               [part]))

    agg_p = AggregatedPieces(ind_ps)
    self._result = agg_p.get_data([aggregator.ColumnAggregator], [], {}, ngram_freqs)

I made the following changes:

  • Remove the instruction parameter from run(), since there is only one experiment.
  • Use the import statements at the top of the file.
  • Use self._data rather than building my own list of IndexedPiece objects (in enumerate() on line 5).
  • Set interval_settings per-piece, and use the value from built-in WorkflowManager settings.
  • Set n from the built-in WorkflowManager settings.

I could also use the WorkflowManager.settings() method to get other settings by piece or shared across all pieces, like 'simple intervals', which tells the HorizontalIntervalIndexer whether to display all intervals as their single-octave equivalents.

To run the same analysis as in Tutorial: Use N-Grams to Find Melodic Patterns, use the WorkflowManager like this:

1
2
3
4
5
6
7
8
9
from vis.workflow import WorkflowManager

pathnames = [list_of_pathnames]
work = WorkflowManager(pathnames)
work.load('pieces')
for i in xrange(len(work)):
    work.settings(i, 'quality', True)
work.run()
work.export('CSV', 'output_filename.csv')

This script actually does more than the program in Tutorial: Use N-Grams to Find Melodic Patterns because export() “converts” the results to a DataFrame, sorts, and outputs the results.