def test_grid(self): chart = chords.ChordChart() chart.load_tuning_by_name("Soprano Uke") self.assertEqual(chart.grid_as_md("F#7"), chart.grid_as_md("F#7!")) #chart.get_default("F#7").show() self.assertEqual( chart.get_default("Gbm7").to_chordpro(), chart.get_default("F#m7").to_chordpro()) F_sharp_chord_def = "{define: F#m7 base-fret 7 frets 2 1 2 0}" Gb_chord_def = "{define: Gbm7 base-fret 7 frets 2 1 2 0}" c = chords.ChordChart() c.add_grid(Gb_chord_def) self.assertEqual( c.get_default("Gbm7").to_chordpro(), F_sharp_chord_def) c = chords.ChordChart() c.add_grid(Gb_chord_def) self.assertEqual( c.get_default("F#m7").to_chordpro(), F_sharp_chord_def) self.assertEqual( c.get_default("F#m7//").to_chordpro(), F_sharp_chord_def) self.assertEqual( c.get_default("F#m7!").to_chordpro(), F_sharp_chord_def)
def test_normalisation(self): chart = chords.ChordChart() chart.load_tuning_by_name("Soprano Uke") #Check normalisation code self.assertEqual(chart.grid_as_md("F#7///"), chart.grid_as_md("F#7")) self.assertEqual(chart.normalise_chord_name("Am7-5"), "Am7-5") self.assertEqual(chart.normalise_chord_name("Cmaj"), "C") self.assertEqual(chart.normalise_chord_name("CM7"), "Cmaj7") self.assertEqual(chart.normalise_chord_name("Cmaj7"), "Cmaj7") self.assertEqual(chart.normalise_chord_name("C+"), "Caug") self.assertEqual(chart.clean_chord_name("A!"), "A") self.assertEqual(chart.normalise_chord_name("AM7 / / /"), "Amaj7") # Clean removes rhythm marks but does not change chord names self.assertEqual(chart.clean_chord_name("Amaj7 / / /"), "Amaj7") self.assertEqual(chart.clean_chord_name("AM7 / / /"), "AM7")
def test_nashvillization(self): #TODO refactor code so this is no longer hanging off the ChordChart class chart = chords.ChordChart() self.assertEqual(chart.nashvillize("C","C"), "I") self.assertEqual(chart.nashvillize("C7","C"), "I⁷") self.assertEqual(chart.nashvillize("Cm","C"), "i") self.assertEqual(chart.nashvillize("Bb","Bb"), "I") self.assertEqual(chart.nashvillize("G/F#","C"), "V/♭5") self.assertEqual(chart.nashvillize("Bb/A","Bb"), "I/7") self.assertEqual(chart.nashvillize("Bb/A","Bb"), "I/7") self.assertEqual(chart.nashvillize("Am","Am"), "i") self.assertEqual(chart.nashvillize("Am/G","Am"), "i/7") self.assertEqual(chart.nashvillize("Am/C","Am"), "i/3") self.assertEqual(chart.nashvillize("Am/C","Am", major_chart=True), "vi/1") self.assertEqual(chart.nashvillize("Am","Am", major_chart=True), "vi") self.assertEqual(chart.nashvillize("C","C", major_chart=True), "I") self.assertEqual(chart.nashvillize("Cmaj7","C", major_chart=True), "IΔ")
def generate_defs(): transposer = chords.transposer() parser = argparse.ArgumentParser() parser.add_argument('tuning', default=None, help='tuning') args = vars(parser.parse_args()) tuning = args["tuning"] chordpro_definitions = "" for note_index in range(0, 12): for variant in [ "", "7", "6", "sus4", "m", "maj7", "m7", "Add9", "dim" ]: chord_name = transposer.get_note(note_index) + variant defs = generate_grids(chord_name, tuning) grids = chords.ChordChart() grids.load(defs) norm_chord_name = grids.normalise_chord_name(chord_name) grids.sort_by_playability(norm_chord_name) chordpro_definitions += "{c: %s}\n%s\n" % ( norm_chord_name, str(grids.to_chordpro(norm_chord_name))) open("%s_chords.cho" % tuning.replace(" ", ""), 'w').write(chordpro_definitions)
def format(self, transpose=None, instrument_name=None, stand_alone=True): """ Create a markdown version of the song, transposed if necessary, does the last-minute formatting on the song incuding transposition and fetching chord grids """ self.pages = 1 if instrument_name == None: instrument_name = self.instrument_name self.local_grids = None self.grids = None if transpose: self.transpose = transpose #self.transpose = transpose if instrument_name != None: instrument = self.instruments.get_instrument_by_name( instrument_name) if instrument != None: instrument.load_chord_chart(lefty=self.lefty) self.grids = instrument.chart if self.local_instruments != None and instrument_name in self.local_instrument_names: self.local_grids = self.local_instruments.get_instrument_by_name( instrument_name).chart if transpose and self.original_key: self.key = self.transposer.transpose_chord(self.original_key) key_string = self.get_key_string() title = "%s %s" % (self.title, key_string) self.chords_used = [] # TODO Move this to a stand-alone-function nv = chordprobook.chords.ChordChart( ) if self.nashville and self.original_key else None def format_chord(chord): if nv: chord = nv.nashvillize(chord, key=key, major_chart=self.major_chart) else: if self.transposer.offset != 0: chord = self.transposer.transpose_chord(chord) if self.grids != None: clean_chord = self.grids.clean_chord_name(chord) if not clean_chord in self.chords_used: self.chords_used.append(clean_chord) return ("[%s]" % chord) key = self.original_key song = "" tr = chordprobook.chords.transposer(key=key, major_chart=self.major_chart) if self.major_chart: song += "*NOTE: Chart is for relative major key* \n" for line in self.text.split("\n"): dir = directive(line) if dir.type == directive.key: key = dir.value.strip() if self.original_key: # TODO fix minors tr = chordprobook.chords.transposer(key=key) minor = " (minor)" if tr.minor else "" if self.nashville: chart = chords.ChordChart() song += "\n### Modulate: %s (%+d semitones%s) \n" % ( chart.nashvillize(key, self.original_key), tr.offset - tr.get_note_index(self.original_key) % 12, minor) else: song += "\n### Change key to %s\n" % self.transposer.transpose_chord( key) else: song += re.sub("\[(.*?)\]", lambda m: format_chord(m.group(1)), line) + "\n" if stand_alone and instrument_name != None: title = "%s (%s %s)" % (title, "Left-handed" if self.lefty else "", instrument_name) self.md = song self.formatted_title = title
def parse(self): """ Deal with directives and turn song into markdown""" in_tab = False in_block = False new_text = "" current_instrument = None add_extra_newline = True for line in self.text.split("\n"): dir = directive(line) if dir.type == None: if not line.startswith('#'): line = normalize_chord_markup(line) if in_tab: #Four spaces in Markdown means preformatted pass else: #Highlight chords line = line.replace("][", "] [").strip() line = re.sub( '\[(.*?)\]', '<span class="chord-bracket">[<span class="chord">\\1</span>]</span>', line) if line.startswith("."): line = re.sub("^\.(.*?) (.*)", "<span class='\\1'>\\1 \\2</span>", line) if add_extra_newline: new_text += '<div class="spacer"></div>' add_extra_newline = False new_text += "%s\n" % line else: if dir.type == directive.comment: if in_block: new_text += "</div>" in_block = False if dir.value.startswith("."): dir.value = dir.value[1:] classs = dir.value.split(" ")[0] if classs: in_block = True new_text += "<div class='%s'>" % classs new_text += "\n**%s**\n" % dir.value elif dir.type == directive.title: self.title += dir.value elif dir.type == directive.subtitle: new_text += "\n**%s**\n" % dir.value elif dir.type == directive.artist: new_text += "\n***Artist: ***%s\n" % dir.value elif dir.type == directive.composer: new_text += "\n***Composer: ***%s\n" % dir.value elif dir.type == directive.lyricist: new_text += "\n***Lyricist: ***%s\n" % dir.value elif dir.type == directive.time: new_text += "\n***Time: ***%s\n" % dir.value elif dir.type == directive.tempo: new_text += "\n***Tempo: ***%s\n" % dir.value elif dir.type == directive.key: if self.original_key: new_text += "%s\n" % line else: self.original_key = dir.value self.key = self.transposer.transpose_chord( self.original_key) elif dir.type == directive.transpose: trans = dir.value.split(" ") self.standard_transpositions += [int(x) for x in trans] elif dir.type == directive.start_chorus: new_text += "<blockquote class='chorus'>\n" elif dir.type == directive.start_bridge: new_text += "<blockquote class='bridge'>\n" elif dir.type in [directive.end_chorus, directive.end_bridge]: new_text += "</blockquote>\n" elif dir.type == directive.start_tab and not in_tab: in_tab = True new_text += "```\n" elif dir.type == directive.end_tab and in_tab: new_text += "```\n" in_tab = False elif dir.type == directive.new_page: if in_block: new_text += "</div>\n" in_block = False new_text += "\n<!-- new_page -->\n" self.pages += 1 elif dir.type == directive.page_image: if in_block: new_text += "</div>\n" in_block = False new_text += "<img src='file://%s/%s' width='680'/>" % ( self.dir, dir.value) elif dir.type == directive.instrument: inst_name = dir.value if self.local_instruments == None: self.local_instruments = chordprobook.instruments.Instruments( ) current_instrument = self.local_instruments.get_instrument_by_name( inst_name) self.local_instrument_names.append(inst_name) if current_instrument == None: current_instrument = chordprobook.instruments.Instrument( name=inst_name) console.log("Loading lefty instrument", inst_name) current_instrument.chart = chords.ChordChart( lefty=self.lefty) self.local_instruments.add_instrument( current_instrument) else: current_instrument.load_chord_chart() elif dir.type == directive.define: if current_instrument != None: current_instrument.chart.add_grid(line) self.text = new_text #Add four spaces to mid-stanza line ends to force Markdown to add breaks self.text = re.sub("(.)\n(.)", "\\1 \\n\\2", self.text)
def parse(self): """ Deal with directives and turn song into markdown""" in_chorus = False in_tab = False in_block = False new_text = "" current_instrument = None for line in self.text.split("\n"): dir = directive(line) if dir.type == None: if not line.startswith('#'): line = normalize_chord_markup(line) if in_chorus: #">" is Markdown for blockquote new_text += "> " if in_tab: #Four spaces in Markdown means preformatted pass else: #Highlight chords line = line.replace("][", "] [").strip() line = re.sub("\[(.*?)\]", "**[\\1]**", line) if line.startswith("."): line = re.sub("^\.(.*?) (.*)", "<span class='\\1'>\\1 \\2</span>", line) new_text += "%s\n" % line else: if dir.type == directive.comment: if in_block: new_text += "</div>" in_block = False if dir.value.startswith("."): dir.value = dir.value[1:] classs = dir.value.split(" ")[0] if classs: in_block = True new_text += "<div class='%s'>" % classs if in_chorus: #">" is Markdown for blockquote new_text += "\n> **%s**\n" % dir.value else: new_text += "\n**%s**\n" % dir.value elif dir.type == directive.title: self.title += dir.value elif dir.type == directive.subtitle: new_text += "\n**%s**\n" % dir.value elif dir.type == directive.key: if self.original_key: new_text += "%s\n" % line else: self.original_key = dir.value self.key = self.transposer.transpose_chord( self.original_key) elif dir.type == directive.transpose: trans = dir.value.split(" ") self.standard_transpositions += [int(x) for x in trans] elif dir.type in [ directive.start_chorus, directive.start_bridge ]: #Treat bridge and chorus formatting the same in_chorus = True elif dir.type in [directive.end_chorus, directive.end_bridge]: in_chorus = False elif dir.type == directive.start_tab and not in_tab: in_tab = True new_text += "```\n" elif dir.type == directive.end_tab and in_tab: new_text += "```\n" in_tab = False elif dir.type == directive.new_page: if in_block: new_text += "</div>\n" in_block = False new_text += "\n<!-- new_page -->\n" self.pages += 1 elif dir.type == directive.instrument: inst_name = dir.value if self.local_instruments == None: self.local_instruments = chordprobook.instruments.Instruments( ) current_instrument = self.local_instruments.get_instrument_by_name( inst_name) self.local_instrument_names.append(inst_name) if current_instrument == None: current_instrument = chordprobook.instruments.Instrument( name=inst_name) current_instrument.chart = chords.ChordChart() self.local_instruments.add_instrument( current_instrument) else: current_instrument.load_chord_chart() elif dir.type == directive.define: if current_instrument != None: current_instrument.chart.add_grid(line) self.text = new_text #Add four spaces to mid-stanza line ends to force Markdown to add breaks self.text = re.sub("(.)\n(.)", "\\1 \\n\\2", self.text)