OTR Toolkit Developer's Manual

Last modified: March 20, 2013

Contents

Overview

The OTR toolkit is out-of-the-box already adaptive to different tablature symbols: you do not need to hack on the toolkit, but simply need to train the symbols of your print as described in the user's manual.

This document is for those who need to extend the toolkit beyond that, and describes two typical use cases:

The core functionality of this toolkit is wrapped in two classes: TabPage and TabGlyphList. In order to extend their functionality while still having access to the provided functions, you should derive from these classes.

Different Staff Removal

Choosing a different staff removal algorithm requires changes only to the Tabpage class. As TabPage uses the staff removal interface of the MusicStaves Toolkit, this requires minimal effort.

How TabPage removes Staves

The staff removal is not implemented in TabPage itself, but in the classes provided by the MusicStaves toolkit. In this toolkit, each class represents a specific staff removal algorithm. TabPage uses the class MusicStaves_rl_simple, which is initialized in the constructor of TabPage and assigned to the member variable musicstaves:

# import wanted staff removal implementation
from gamera.toolkits.musicstaves import MusicStaves_rl_simple as MusicStaves

class TabPage:

    def __init__(self, img, online=0, tab_type='french', staffline_height=0, staffspace_height=0):

        # set general properties
        self.stafflist=[]    # list of staffobj (staffno, yposlist)
        self.staffranges=[]  # list of [start, end] of staves
        self.on_lines=online # tab symbols in courses
        if tab_type not in ['french', 'italian']:
           raise RuntimeError, tab_type + ": Unknown Tabtype"
        else:
           self.tabtype=tab_type

        # use staff removal class rom MusicStaves toolkit
        self.music_staves=MusicStaves(img, staffline_height, staffspace_height)
        self.image=self.music_staves.image
        self.staffline_height=self.music_staves.staffline_height
        self.staffspace_height=self.music_staves.staffspace_height

TabPage.remove_staves simply calls the method remove_staves of the underlying MusicStaves class:

def remove_staves(self, crossing_symbols='auto', num_lines=0):

    if crossing_symbols=='auto':
        if self.on_lines==1:
            crossing_symbols='all'
        else:
            crossing_symbols='bars'

    # remove staves and extract information
    self.music_staves.remove_staves(crossing_symbols=crossing_symbols, num_lines=num_lines)
    self.stafflist=self.music_staves.get_staffpos()
    self.image=self.music_staves.image

Using a different Staff Removal Class

All you need to do is to set self.musicstaves to an instance of a MusicStaves class of your choice. This requires two steps:

  • import the wanted MusicStaves class
  • derive a custom class from TabPage and overwrite the __init__ method to use a different MusicStaves class

The following example will use Fujinaga's staff removal algorithm, which is provided by the class MusicStaves_rl_fujinaga in the MusicStaves toolkit:

# import wanted MusicStaves class
from gamera.toolkits.musicstaves import MusicStaves_rl_fujinaga as myMusicStaves
# original TabPage class
from gamera.toolkits.otr.otr_glyph import *

class myTabPage(TabPage):

    def __init__(self, img, online=0, tab_type='french', staffline_height=0, staffspace_height=0):

        # initialize all necessary data from base class
        TabPage.__init__(self, img, online, tab_type, staffline_height, staffspace_height)

        # use different external class module for removing the staves
        self.music_staves = myMusicStaves(img, staffline_height, staffspace_height)
        self.image = self.music_staves.image
        self.staffline_height = self.music_staves.staffline_height
        self.staffspace_height = self.music_staves.staffspace_height

Note that we have called the constructor of the base class first. Although this will unnecessarily call the constructor of MusicStaves_rl_simple, it has the advantage that all member variables required by TabPage will be correctly initialized.

Now you can use your own class just like the TabPage class:

tab = myTabPage(image)
tab.remove_staves(num_lines=5)

Generating a custom Tablature Code

The class TabGlyphList can generate an abc tablature encoding and a music transcription in abc. To create a custom tablature code or interpret custom trained symbol names in a special way, you must derive a custom class from TabGlyphList and add your own output method.

How TabGlyphList creates Tablature Code

The abc tablature code is created in TabGlyphList.to_abctab, so the code of this function is a good starting point. It requires that TabGlyphList.set_glyph_properties is already called. Here are some points for clarification:

  • The tablature glyph list is self.glyphs. TabGlyphList.set_glyph_properties has sorted the glyphs by staffno, x_number (vertical chord index) and course (from top to bottom).
  • Each glyph has the properties staff, x_number and course, which have been assigned by TabGlyphList.set_glyph_properties.
  • The symbol names set by the classifier are queried with the functions from the Gamera classifier API, eg. match_id_name("*flag*")

Adding an Output Method

Let us assume that our "tablature code" simply consists of all symbol names together with their position (staff, x_number, course); we call this code glyphliststring.

As we want to keep all functionality from the OTR toolkit, we derive a new class myTabGlyphList from TabGlyphList and add a new output method:

# original TabGlyphList class
 from gamera.toolkits.otr.otr_glyph import *

 class myTabGlyphList(TabGlyphList):

     # our custom function
     def to_glyphliststring(self):

         string=""

         for g in self.glyphs:

             # ignore symbols that are outside the staves
             if g.course < 0 or g.staff < 0:
                 continue

             # handling of the symbols
             if g.match_id_name('*bar*'):
                 string+="bar line"
             else:
                 string+=g.id_name[0][1]

             # information of a symbol's position
             string+=" (staff: %d, course: %d, x_number: %d)\n"\
                     % (g.staff, g.course, g.x_number)

         return string

A more realistic tablature encoding like abc would require to encode staff breaks and process one chord at a time. This is easily achieved with group breaks on changing staff and x_number.