Example #1
0
 def __init__(self, results=[], options=None, tagger=None, parser=None, 
                 formalism=None, env=None, seq_id=None, input_data=None):
     self.results = results
     self.options = options
     self.parser = parser
     self.env = env
     self.seq_id = seq_id
     self.input_data = input_data
     self.tagger = tagger
     self.data = {}
     
     if formalism is None:
         # Use the default formalism
         self.formalism = get_default_formalism()
     else:
         self.formalism = formalism
     
     self.tools = {}
     self.all_tools = []
Example #2
0
    def __init__(self, grammar_name=None):
        """ 
        Creates a new grammar by reading from an XML grammar file.
        
        Words (morph items) are stored in morph_items.
        Families (lexical families) are stored in families.
        
        Instantiate this directly only if you want, for some reason, to be sure 
        of getting a new instance of Grammar. Most of the time, you can 
        load a named grammar using L{get_grammar}, which will cache already 
        loaded grammars and return the same instance again if you ask for the 
        same name.
        
        @type grammar_name: string
        @param grammar_name: name of the grammar definition to be loaded. 
            Call L{get_grammar_names} for a list of available grammars. If 
            None, loads the default grammar.
        
        """
        if grammar_name is None:
            grammar_name = settings.DEFAULT_GRAMMAR
        self.name = grammar_name

        filename_base = os.path.join(settings.GRAMMAR_DATA_DIR, grammar_name)
        self.grammar_file = os.path.join(filename_base, "grammar.xml")
        # Read in the grammar
        logger.debug("Grammar: %s" % self.grammar_file)

        # Read in the XML from the file
        self.grammar_dom = xml.dom.minidom.parse(self.grammar_file)

        grammar_tag = get_single_element_by_tag_name(self.grammar_dom,
                                                     "grammar")
        # Get a named formalism, or the default one
        formalism_attr = grammar_tag.attributes.getNamedItem("formalism")
        if formalism_attr is None:
            formalism = get_default_formalism()
        else:
            formalism_name = str(formalism_attr.value)
            try:
                formalism = get_formalism(formalism_name)
            except FormalismLoadError:
                logger.error("The formalism '%s' does not exist. Possible "\
                    "formalisms are: %s" % (formalism_name, ", ".join(FORMALISMS)))
                raise
        self.formalism = formalism

        ###############################
        ### Reading in the lexicon
        lex_tag = get_single_element_by_tag_name(self.grammar_dom, "lexicon")
        lexicon_file = os.path.join(
            filename_base,
            lex_tag.attributes.getNamedItem("file").value)
        logger.debug("Lexicon: %s" % lexicon_file)
        # Read in the lexicon
        self.lexicon_dom = xml.dom.minidom.parse(lexicon_file)

        ###############################
        ### Reading in the words
        morph_tag = get_single_element_by_tag_name(self.grammar_dom,
                                                   "morphology")
        morph_file = os.path.join(
            filename_base,
            morph_tag.attributes.getNamedItem("file").value)
        logger.debug("Morphology: %s" % morph_file)
        # Read in the lexicon
        self.morph_dom = xml.dom.minidom.parse(morph_file)

        ###############################
        ### Reading in the rules
        rules_tag = get_single_element_by_tag_name(self.grammar_dom, "rules")
        rules_file = os.path.join(
            filename_base,
            rules_tag.attributes.getNamedItem("file").value)
        logger.debug("Rules: %s" % rules_file)
        # Read in the lexicon
        self.rules_dom = xml.dom.minidom.parse(rules_file)

        ###############################
        ### Reading in the functions list (only used for certain formalisms)
        functions_tag = get_single_element_by_tag_name(self.grammar_dom,
                                                       "functions",
                                                       optional=True)
        self.literal_functions = {}
        available_funs = formalism.literal_functions
        if functions_tag is not None:
            functions_file = os.path.join(
                filename_base,
                functions_tag.attributes.getNamedItem("file").value)
            logger.debug("Functions: %s" % functions_file)
            # Read in the functions from the XML
            functions_dom = xml.dom.minidom.parse(functions_file)
            functions_xml = get_single_element_by_tag_name(
                functions_dom, "functions")
            functions = remove_unwanted_elements(
                functions_xml.getElementsByTagName("function"))
            # Try adding each of the functions, using the formalism's definitions
            for func_el in functions:
                func_name = func_el.attributes.getNamedItem("name").value
                if func_name in available_funs:
                    lit_fun = available_funs[func_name]
                    self.literal_functions[lit_fun.name] = lit_fun
                else:
                    raise GrammarReadError, "The literal function \"%s\" is not defined in the code for the %s formalism." % formalism.get_name(
                    )

        ###############################
        ### Reading in the modality hierarchy
        modalities_tag = get_single_element_by_tag_name(self.grammar_dom,
                                                        "modalities",
                                                        optional=True)
        if modalities_tag is not None:
            modalities_file = os.path.join(
                filename_base,
                modalities_tag.attributes.getNamedItem("file").value)
            logger.debug("Modalities: %s" % modalities_file)
            # Read in the modalities
            self.modalities_dom = get_single_element_by_tag_name(
                xml.dom.minidom.parse(modalities_file), "modalities")
        else:
            self.modalities_dom = None

        ###############################
        ### Read in grammar-level meta data
        attrs = self.grammar_dom.getElementsByTagName("attr")
        # Initialize values that might not get set
        self.max_categories = None
        # Read in the values from the XML
        for el in attrs:
            name = el.getAttribute("name")
            value = el.getAttribute("value")
            # Check for all the attributes we recognize
            if name == "max_categories":
                self.max_categories = int(value)

        ###############################
        ### Prepare the morph word classes
        self.chord_classes = {}
        for entry in self.morph_dom.getElementsByTagName("class"):
            chord_class = ChordClass.from_dom(entry)
            self.chord_classes[chord_class.name] = chord_class

        # Maybe handle macros here. Not currently using them.

        ###############################
        ### Prepare lexical entries
        # Use a hash table for this too, indexed by pos
        self.families = {}
        self.inactive_families = []
        for family in self.lexicon_dom.getElementsByTagName("family"):
            fam = Family.from_dom(formalism, family)
            # Check whether the family has any entries and don't use it if not
            if len(fam.entries) > 0:
                # Put a new Family in the table for every family entry
                if fam.pos in self.families:
                    # Already an entry for this POS: add to the list
                    self.families[fam.pos].append(fam)
                else:
                    # No occurence of this POS yet: add a new list
                    self.families[fam.pos] = [fam]
            else:
                self.inactive_families.append(fam.pos)

        ###############################
        ### Prepare the morph items
        self.morphs = []
        for entry in self.morph_dom.getElementsByTagName("entry"):
            morph = MorphItem.from_dom(formalism, entry, self.chord_classes)
            self.morphs.append(morph)

        # Check that all the morphs correspond to a defined POS
        for morph in self.morphs:
            if morph.pos not in self.families:
                raise GrammarReadError, "morph item refers to undefined "\
                    "part-of-speech '%s': %s" % (morph.pos, morph.element.toxml())

        ###############################
        ### Prepare modalities hierarchy
        if self.modalities_dom:
            self.modality_tree = ModalityTree.from_dom(self.modalities_dom)
        else:
            # The modalities that existed before they were added to the
            #  XML spec were just "c" and "."
            self.modality_tree = ModalityTree(
                [ModalityTreeNode("", [ModalityTreeNode("c")])])

        ###############################
        ### Prepare rules
        self.rules = []
        # Go through each different type of rule and add appropriate Rule subclasses
        rule_block = get_single_element_by_tag_name(self.rules_dom, "rules")

        for rule_tag in remove_unwanted_elements(rule_block.childNodes):
            rulename = rule_tag.tagName
            if rulename == "lexrules":
                # We'll deal with these later
                continue
            if rulename not in self.formalism.rules:
                raise GrammarReadError, "unknown rule '%s' (formalism "\
                    "defines: %s)" % (rulename, ", ".join(formalism.rules.keys()))
            ruleclass = self.formalism.rules[rulename]
            # Instantiate the rule, using options from the XML
            self.rules.append(
                ruleclass(modalities=self.modality_tree,
                          grammar=self,
                          **attrs_to_dict(rule_tag.attributes)))

        # Keep rules sorted by arity for ease of access
        self.unary_rules = []
        self.binary_rules = []
        for rule in self.rules:
            if rule.arity == 1:
                self.unary_rules.append(rule)
            elif rule.arity == 2:
                self.binary_rules.append(rule)

        # Index rules by internal name for ease of access
        self.rules_by_name = {}
        for rule in self.rules:
            if rule.internal_name in self.rules_by_name:
                # This shouldn't happen: each rule name should only be used once
                raise GrammarReadError, "instantiated two rules with the same "\
                    "internal name: %s. Either the XML has mistakenly "\
                    "instantiated the same thing twice, or the rule class has "\
                    "failed to give different varieties of the rule different "\
                    "names" % rule.internal_name
            self.rules_by_name[rule.internal_name] = rule

        # Optionally read in a lexrules element and expand the lexicon
        #  using its entries
        self.lexical_rules = []
        lexrules_tag = get_single_element_by_tag_name(self.rules_dom,
                                                      "lexrules",
                                                      optional=True)
        if lexrules_tag is not None:
            for rule_tag in remove_unwanted_elements(lexrules_tag.childNodes):
                rulename = rule_tag.tagName
                if rulename not in self.formalism.rules:
                    raise GrammarReadError, "unknown lexical expansion "\
                        "rule '%s' (formalism defines: %s)" % \
                        (rulename, ", ".join(formalism.rules.keys()))
                ruleclass = self.formalism.rules[rulename]
                attrs = attrs_to_dict(rule_tag.attributes)
                # Make sure expanded category has a suffix to put on
                #  POSs. If one isn't given, set a default.
                if "pos_suffix" in attrs:
                    pos_suffix = attrs["pos_suffix"]
                    del attrs["pos_suffix"]
                else:
                    pos_suffix = "_Rep"
                # Instantiate the rule, using any options given
                rule = ruleclass(modalities=self.modality_tree,
                                 grammar=self,
                                 **attrs)
                rule.pos_suffix = pos_suffix
                # Can only use unary rules - check this one is
                if rule.arity != 1:
                    raise "can only use unary rules as lexical "\
                        "expansions. Tried to use %s, which has arity "\
                        "%d." % (rulename, rule.arity)
                self.lexical_rules.append(rule)
        # Use each lexical rule to expand the lexicon
        for rule in self.lexical_rules:
            for fam in sum(self.families.values(), []):
                for entry in fam.entries:
                    # Try apply the expansion rule to this entry
                    new_signs = rule.apply_rule([entry.sign])
                    if new_signs is not None and len(new_signs) > 0:
                        # Make a new POS for this expanded category
                        new_pos = "%s%s" % (fam.pos, rule.pos_suffix)
                        new_entries = [EntriesItem(self.formalism, "Expanded", new_sign) \
                                    for new_sign in new_signs]
                        new_family = Family(self.formalism,
                                            new_pos,
                                            new_pos,
                                            new_entries,
                                            chordfn=fam.chordfn,
                                            expanded=rule.internal_name)
                        self.families.setdefault(new_pos,
                                                 []).append(new_family)
                        # Also create morph items for each of those
                        #  that referenced the old unexpanded rules
                        for morph in [
                                m for m in self.morphs if m.pos == fam.pos
                        ]:
                            self.morphs.append(
                                MorphItem(self.formalism,
                                          copy.deepcopy(morph.words),
                                          new_pos,
                                          optional_minor=morph.optional_minor,
                                          chord_class=morph.chord_class))

        ###############
        # Index the morph items by word to make lookup easier
        self.morph_items = {}
        for morph in self.morphs:
            # If the pos is completely inactive in the lexicon, ignore this morph
            if not morph.pos in self.inactive_families:
                # Go through each of this morph's words
                for word in morph.words:
                    # Put a new MorphItem in the table for every entry
                    if word in self.morph_items:
                        # Already a list for this word: add to it
                        self.morph_items[word].append(morph)
                    else:
                        # First occurence of this word: add a new list
                        self.morph_items[word] = [morph]

        ###############
        # Read in an equivalence map if one is given for morph entries
        equiv_map_el = get_single_element_by_tag_name(self.morph_dom,
                                                      "equivmap",
                                                      optional=True)
        if equiv_map_el is not None:
            self.equiv_map = EquivalenceMap.from_dom(formalism, equiv_map_el,
                                                     self.chord_classes,
                                                     self.morphs)
        else:
            self.equiv_map = EquivalenceMap()

        ###########
        # Prepare a version of the family list for MIDI input
        self.midi_families = {}
        for pos, fams in self.families.items():
            new_fams = []
            for fam in fams:
                # Exclude any generated by lexical expansions, unless they're
                #  tonic function
                if fam.expanded is not None and fam.chordfn != "T":
                    continue
                new_fams.append(fam)
            if new_fams:
                # Exclude any that are mapped onto another entry by an equivalence
                #  mapping that changes the root
                if pos in self.equiv_map:
                    continue
                self.midi_families[pos] = new_fams

        ####### Debugging output
        logger.debug("Read the following information from the grammar:")
        logger.debug("Morphology:")
        logger.debug("\n".join(["%s: %s" % (word, ", ".join(["%s" % item.pos for item in items])) \
                         for word,items in self.morph_items.items()]))
        logger.debug("Lexicon:")
        logger.debug("\n".join([", ".join(["%s" % initem for initem in item]) \
                         for item in self.families.values()]))
        logger.debug("Rules:")
        logger.debug("\n".join(["  %s" % item for item in self.rules]))
        logger.debug("Lexical expansion rules:")
        logger.debug("\n".join(["  %s" % item for item in self.lexical_rules]))
        logger.debug("Modalities:")
        logger.debug("%s" % self.modality_tree)
        if len(self.literal_functions):
            logger.debug("Literal functions:")
            logger.debug("\n".join([
                "  %s: %s" % (name, val)
                for (name, val) in self.literal_functions.items()
            ]))
Example #3
0
def main():
    usage = "%prog [options] <results-file> [<result-number>=0]"
    parser = OptionParser(usage=usage)
    parser.add_option("-q", "--quiet", dest="quiet", action="store_true", help="only output the requested information, no meta-info.")
    parser.add_option("-p", "--print", dest="printout", action="store_true", help="output the result to stdout.")
    parser.add_option("--path", dest="path", action="store_true", help="display the fully-specified tonal space path.")
    parser.add_option("--play", dest="play", action="store_true", help="use the harmonical to play the root sequence of the result's semantics.")
    parser.add_option("--audio", dest="audio", action="store", help="use the harmonical to render the root sequence, as with --play, and store the result to a wave file.")
    options, arguments = parser.parse_args()
    
    # Just get the default formalism
    formalism = get_default_formalism()
    
    def _print(string=""):
        if not options.quiet:
            print >>sys.stderr, string
        
    if len(arguments) == 0:
        print >>sys.stderr, "Specify a file to read the results from"
        sys.exit(1)
    results = ParseResults.from_file(arguments[0])
    
    if len(arguments) > 1:
        res_num = int(arguments[1])
    else:
        res_num = 0
    prob,result = results.sorted_results[res_num]
    
    if options.printout:
        _print("Result:")
        # Just display the resulting category
        print result
        _print()
        
    if options.path:
        _print("Tonal space path:")
        # Compute the tonal path (coordinates) from the result
        path = formalism.semantics_to_coordinates(result.semantics)
        points,timings = zip(*path)
        print ", ".join(coordinates_to_roman_names(points))
        _print()
        
    if options.play or options.audio is not None:
        _print("Building pitch structure from result...")
        # Convert the semantics into a list of TS points
        path = formalism.semantics_to_coordinates(result.semantics)
        # Decide on chord types
        # For now, since we don't know the original chords, use dom7 
        #  for dom chords, maj for subdoms, and M7 for tonics
        fun_chords = {
            'T' : 'M7',
            'D' : '7',
            'S' : '',
        }
        functions = formalism.semantics_to_functions(result.semantics)
        chord_types = [(fun_chords[f],t) for (f,t) in functions]
        
        tones = path_to_tones(path, chord_types=chord_types, double_root=True)
        
        _print("Rendering audio samples...")
        samples = tones.render()
        if options.audio is not None:
            filename = os.path.abspath(options.audio)
            _print("Writing wave data to %s" % filename)
            save_wave_data(samples, filename)
        if options.play:
            _print("Playing...")
            play_audio(samples, wait_for_end=True)
        _print()
Example #4
0
 def __init__(self, grammar_name=None):
     """ 
     Creates a new grammar by reading from an XML grammar file.
     
     Words (morph items) are stored in morph_items.
     Families (lexical families) are stored in families.
     
     Instantiate this directly only if you want, for some reason, to be sure 
     of getting a new instance of Grammar. Most of the time, you can 
     load a named grammar using L{get_grammar}, which will cache already 
     loaded grammars and return the same instance again if you ask for the 
     same name.
     
     @type grammar_name: string
     @param grammar_name: name of the grammar definition to be loaded. 
         Call L{get_grammar_names} for a list of available grammars. If 
         None, loads the default grammar.
     
     """
     if grammar_name is None:
         grammar_name = settings.DEFAULT_GRAMMAR
     self.name = grammar_name
     
     filename_base = os.path.join(settings.GRAMMAR_DATA_DIR, grammar_name)
     self.grammar_file = os.path.join(filename_base, "grammar.xml")
     # Read in the grammar
     logger.debug("Grammar: %s" % self.grammar_file)
     
     # Read in the XML from the file
     self.grammar_dom = xml.dom.minidom.parse(self.grammar_file)
     
     grammar_tag = get_single_element_by_tag_name(self.grammar_dom, "grammar")
     # Get a named formalism, or the default one
     formalism_attr = grammar_tag.attributes.getNamedItem("formalism")
     if formalism_attr is None:
         formalism = get_default_formalism()
     else:
         formalism_name = str(formalism_attr.value)
         try:
             formalism = get_formalism(formalism_name)
         except FormalismLoadError:
             logger.error("The formalism '%s' does not exist. Possible "\
                 "formalisms are: %s" % (formalism_name, ", ".join(FORMALISMS)))
             raise
     self.formalism = formalism
     
     ###############################
     ### Reading in the lexicon
     lex_tag = get_single_element_by_tag_name(self.grammar_dom, "lexicon")
     lexicon_file = os.path.join(filename_base, lex_tag.attributes.getNamedItem("file").value)
     logger.debug("Lexicon: %s" % lexicon_file)
     # Read in the lexicon
     self.lexicon_dom = xml.dom.minidom.parse(lexicon_file)
     
     ###############################
     ### Reading in the words
     morph_tag = get_single_element_by_tag_name(self.grammar_dom, "morphology")
     morph_file = os.path.join(filename_base, morph_tag.attributes.getNamedItem("file").value)
     logger.debug( "Morphology: %s" % morph_file)
     # Read in the lexicon
     self.morph_dom = xml.dom.minidom.parse(morph_file)
     
     ###############################
     ### Reading in the rules
     rules_tag = get_single_element_by_tag_name(self.grammar_dom, "rules")
     rules_file = os.path.join(filename_base, rules_tag.attributes.getNamedItem("file").value)
     logger.debug( "Rules: %s" % rules_file)
     # Read in the lexicon
     self.rules_dom = xml.dom.minidom.parse(rules_file)
     
     ###############################
     ### Reading in the functions list (only used for certain formalisms)
     functions_tag = get_single_element_by_tag_name(self.grammar_dom, "functions", optional=True)
     self.literal_functions = {}
     available_funs = formalism.literal_functions
     if functions_tag is not None:
         functions_file = os.path.join(filename_base, functions_tag.attributes.getNamedItem("file").value)
         logger.debug( "Functions: %s" % functions_file)
         # Read in the functions from the XML
         functions_dom = xml.dom.minidom.parse(functions_file)
         functions_xml = get_single_element_by_tag_name(functions_dom, "functions")
         functions = remove_unwanted_elements(functions_xml.getElementsByTagName("function"))
         # Try adding each of the functions, using the formalism's definitions
         for func_el in functions:
             func_name = func_el.attributes.getNamedItem("name").value
             if func_name in available_funs:
                 lit_fun = available_funs[func_name]
                 self.literal_functions[lit_fun.name] = lit_fun
             else:
                 raise GrammarReadError, "The literal function \"%s\" is not defined in the code for the %s formalism." % formalism.get_name()
     
     ###############################
     ### Reading in the modality hierarchy
     modalities_tag = get_single_element_by_tag_name(self.grammar_dom, "modalities", optional=True)
     if modalities_tag is not None:
         modalities_file = os.path.join(filename_base, modalities_tag.attributes.getNamedItem("file").value)
         logger.debug( "Modalities: %s" % modalities_file)
         # Read in the modalities
         self.modalities_dom = get_single_element_by_tag_name(xml.dom.minidom.parse(modalities_file), "modalities")
     else:
         self.modalities_dom = None
         
     ###############################
     ### Read in grammar-level meta data
     attrs = self.grammar_dom.getElementsByTagName("attr")
     # Initialize values that might not get set
     self.max_categories = None
     # Read in the values from the XML
     for el in attrs:
         name = el.getAttribute("name")
         value = el.getAttribute("value")
         # Check for all the attributes we recognize
         if name == "max_categories":
             self.max_categories = int(value)
     
     ###############################
     ### Prepare the morph word classes
     self.chord_classes = {}
     for entry in self.morph_dom.getElementsByTagName("class"):
         chord_class = ChordClass.from_dom(entry)
         self.chord_classes[chord_class.name] = chord_class
     
     # Maybe handle macros here. Not currently using them.
     
     ###############################
     ### Prepare lexical entries
     # Use a hash table for this too, indexed by pos
     self.families = {}
     self.inactive_families = []
     for family in self.lexicon_dom.getElementsByTagName("family"):
         fam = Family.from_dom(formalism, family)
         # Check whether the family has any entries and don't use it if not
         if len(fam.entries) > 0:
             # Put a new Family in the table for every family entry
             if fam.pos in self.families:
                 # Already an entry for this POS: add to the list
                 self.families[fam.pos].append(fam)
             else:
                 # No occurence of this POS yet: add a new list
                 self.families[fam.pos] = [fam]
         else:
             self.inactive_families.append(fam.pos)
     
     ###############################
     ### Prepare the morph items
     self.morphs = []
     for entry in self.morph_dom.getElementsByTagName("entry"):
         morph = MorphItem.from_dom(formalism,entry,self.chord_classes)
         self.morphs.append(morph)
     
     # Check that all the morphs correspond to a defined POS
     for morph in self.morphs:
         if morph.pos not in self.families:
             raise GrammarReadError, "morph item refers to undefined "\
                 "part-of-speech '%s': %s" % (morph.pos, morph.element.toxml())
             
     ###############################
     ### Prepare modalities hierarchy
     if self.modalities_dom:
         self.modality_tree = ModalityTree.from_dom(self.modalities_dom)
     else:
         # The modalities that existed before they were added to the 
         #  XML spec were just "c" and "."
         self.modality_tree = ModalityTree([
                                 ModalityTreeNode("", 
                                     [ModalityTreeNode("c")]) ])
         
     ###############################
     ### Prepare rules
     self.rules = []
     # Go through each different type of rule and add appropriate Rule subclasses
     rule_block = get_single_element_by_tag_name(self.rules_dom, "rules")
     
     for rule_tag in remove_unwanted_elements(rule_block.childNodes):
         rulename = rule_tag.tagName
         if rulename == "lexrules":
             # We'll deal with these later
             continue
         if rulename not in self.formalism.rules:
             raise GrammarReadError, "unknown rule '%s' (formalism "\
                 "defines: %s)" % (rulename, ", ".join(formalism.rules.keys()))
         ruleclass = self.formalism.rules[rulename]
         # Instantiate the rule, using options from the XML
         self.rules.append(ruleclass(modalities=self.modality_tree, grammar=self, **attrs_to_dict(rule_tag.attributes)))
         
     # Keep rules sorted by arity for ease of access
     self.unary_rules = []
     self.binary_rules = []
     for rule in self.rules:
         if rule.arity == 1:
             self.unary_rules.append(rule)
         elif rule.arity == 2:
             self.binary_rules.append(rule)
             
     # Index rules by internal name for ease of access
     self.rules_by_name = {}
     for rule in self.rules:
         if rule.internal_name in self.rules_by_name:
             # This shouldn't happen: each rule name should only be used once
             raise GrammarReadError, "instantiated two rules with the same "\
                 "internal name: %s. Either the XML has mistakenly "\
                 "instantiated the same thing twice, or the rule class has "\
                 "failed to give different varieties of the rule different "\
                 "names" % rule.internal_name
         self.rules_by_name[rule.internal_name] = rule
             
     # Optionally read in a lexrules element and expand the lexicon 
     #  using its entries
     self.lexical_rules = []
     lexrules_tag = get_single_element_by_tag_name(self.rules_dom, "lexrules", optional=True)
     if lexrules_tag is not None:
         for rule_tag in remove_unwanted_elements(lexrules_tag.childNodes):
             rulename = rule_tag.tagName
             if rulename not in self.formalism.rules:
                 raise GrammarReadError, "unknown lexical expansion "\
                     "rule '%s' (formalism defines: %s)" % \
                     (rulename, ", ".join(formalism.rules.keys()))
             ruleclass = self.formalism.rules[rulename]
             attrs = attrs_to_dict(rule_tag.attributes)
             # Make sure expanded category has a suffix to put on 
             #  POSs. If one isn't given, set a default.
             if "pos_suffix" in attrs:
                 pos_suffix = attrs["pos_suffix"]
                 del attrs["pos_suffix"]
             else:
                 pos_suffix = "_Rep"
             # Instantiate the rule, using any options given
             rule = ruleclass(modalities=self.modality_tree,
                                 grammar=self, 
                                 **attrs)
             rule.pos_suffix = pos_suffix
             # Can only use unary rules - check this one is
             if rule.arity != 1:
                 raise "can only use unary rules as lexical "\
                     "expansions. Tried to use %s, which has arity "\
                     "%d." % (rulename, rule.arity)
             self.lexical_rules.append(rule)
     # Use each lexical rule to expand the lexicon
     for rule in self.lexical_rules:
         for fam in sum(self.families.values(), []):
             for entry in fam.entries:
                 # Try apply the expansion rule to this entry
                 new_signs = rule.apply_rule([entry.sign])
                 if new_signs is not None and len(new_signs) > 0:
                     # Make a new POS for this expanded category
                     new_pos = "%s%s" % (fam.pos, rule.pos_suffix)
                     new_entries = [EntriesItem(self.formalism, "Expanded", new_sign) \
                                 for new_sign in new_signs]
                     new_family = Family(self.formalism, 
                                         new_pos, 
                                         new_pos,
                                         new_entries,
                                         chordfn=fam.chordfn,
                                         expanded=rule.internal_name)
                     self.families.setdefault(new_pos, []).append(new_family)
                     # Also create morph items for each of those 
                     #  that referenced the old unexpanded rules
                     for morph in [m for m in self.morphs if m.pos == fam.pos]:
                         self.morphs.append(
                                 MorphItem(
                                     self.formalism,
                                     copy.deepcopy(morph.words),
                                     new_pos,
                                     optional_minor=morph.optional_minor,
                                     chord_class=morph.chord_class))
     
     ###############
     # Index the morph items by word to make lookup easier
     self.morph_items = {}
     for morph in self.morphs:
         # If the pos is completely inactive in the lexicon, ignore this morph
         if not morph.pos in self.inactive_families:
             # Go through each of this morph's words
             for word in morph.words:
                 # Put a new MorphItem in the table for every entry
                 if word in self.morph_items:
                     # Already a list for this word: add to it
                     self.morph_items[word].append(morph)
                 else:
                     # First occurence of this word: add a new list
                     self.morph_items[word] = [morph]
     
     ###############
     # Read in an equivalence map if one is given for morph entries
     equiv_map_el = get_single_element_by_tag_name(self.morph_dom, "equivmap", optional=True)
     if equiv_map_el is not None:
         self.equiv_map = EquivalenceMap.from_dom(formalism, 
                                                  equiv_map_el, 
                                                  self.chord_classes, 
                                                  self.morphs)
     else:
         self.equiv_map = EquivalenceMap()
     
     ###########
     # Prepare a version of the family list for MIDI input
     self.midi_families = {}
     for pos,fams in self.families.items():
         new_fams = []
         for fam in fams:
             # Exclude any generated by lexical expansions, unless they're 
             #  tonic function
             if fam.expanded is not None and fam.chordfn != "T":
                 continue
             new_fams.append(fam)
         if new_fams:
             # Exclude any that are mapped onto another entry by an equivalence 
             #  mapping that changes the root
             if pos in self.equiv_map:
                 continue
             self.midi_families[pos] = new_fams
     
     ####### Debugging output
     logger.debug( "Read the following information from the grammar:")
     logger.debug( "Morphology:")
     logger.debug("\n".join(["%s: %s" % (word, ", ".join(["%s" % item.pos for item in items])) \
                      for word,items in self.morph_items.items()]))
     logger.debug("Lexicon:")
     logger.debug("\n".join([", ".join(["%s" % initem for initem in item]) \
                      for item in self.families.values()]))
     logger.debug("Rules:")
     logger.debug("\n".join(["  %s" % item for item in self.rules]))
     logger.debug("Lexical expansion rules:")
     logger.debug("\n".join(["  %s" % item for item in self.lexical_rules]))
     logger.debug("Modalities:")
     logger.debug("%s" % self.modality_tree)
     if len(self.literal_functions):
         logger.debug("Literal functions:")
         logger.debug("\n".join(["  %s: %s" % (name,val) for (name,val) in self.literal_functions.items()]))
Example #5
0
def main():
    usage = "%prog [options] <results-file> [<result-number>=0]"
    parser = OptionParser(usage=usage)
    parser.add_option(
        "-q",
        "--quiet",
        dest="quiet",
        action="store_true",
        help="only output the requested information, no meta-info.")
    parser.add_option("-p",
                      "--print",
                      dest="printout",
                      action="store_true",
                      help="output the result to stdout.")
    parser.add_option("--path",
                      dest="path",
                      action="store_true",
                      help="display the fully-specified tonal space path.")
    parser.add_option(
        "--play",
        dest="play",
        action="store_true",
        help=
        "use the harmonical to play the root sequence of the result's semantics."
    )
    parser.add_option(
        "--audio",
        dest="audio",
        action="store",
        help=
        "use the harmonical to render the root sequence, as with --play, and store the result to a wave file."
    )
    options, arguments = parser.parse_args()

    # Just get the default formalism
    formalism = get_default_formalism()

    def _print(string=""):
        if not options.quiet:
            print >> sys.stderr, string

    if len(arguments) == 0:
        print >> sys.stderr, "Specify a file to read the results from"
        sys.exit(1)
    results = ParseResults.from_file(arguments[0])

    if len(arguments) > 1:
        res_num = int(arguments[1])
    else:
        res_num = 0
    prob, result = results.sorted_results[res_num]

    if options.printout:
        _print("Result:")
        # Just display the resulting category
        print result
        _print()

    if options.path:
        _print("Tonal space path:")
        # Compute the tonal path (coordinates) from the result
        path = formalism.semantics_to_coordinates(result.semantics)
        points, timings = zip(*path)
        print ", ".join(coordinates_to_roman_names(points))
        _print()

    if options.play or options.audio is not None:
        _print("Building pitch structure from result...")
        # Convert the semantics into a list of TS points
        path = formalism.semantics_to_coordinates(result.semantics)
        # Decide on chord types
        # For now, since we don't know the original chords, use dom7
        #  for dom chords, maj for subdoms, and M7 for tonics
        fun_chords = {
            'T': 'M7',
            'D': '7',
            'S': '',
        }
        functions = formalism.semantics_to_functions(result.semantics)
        chord_types = [(fun_chords[f], t) for (f, t) in functions]

        tones = path_to_tones(path, chord_types=chord_types, double_root=True)

        _print("Rendering audio samples...")
        samples = tones.render()
        if options.audio is not None:
            filename = os.path.abspath(options.audio)
            _print("Writing wave data to %s" % filename)
            save_wave_data(samples, filename)
        if options.play:
            _print("Playing...")
            play_audio(samples, wait_for_end=True)
        _print()