forked from openlp/openlp
Implement chord transposing in the lyrics editor.
This commit is contained in:
parent
0ee555cd84
commit
3d98d60d59
@ -25,7 +25,7 @@ import logging
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
from openlp.plugins.songs.lib import VerseType
|
||||
from openlp.plugins.songs.lib import VerseType, transpose_lyrics
|
||||
from .editversedialog import Ui_EditVerseDialog
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@ -101,13 +101,15 @@ class EditVerseForm(QtWidgets.QDialog, Ui_EditVerseDialog):
|
||||
"""
|
||||
The transpose up button clicked
|
||||
"""
|
||||
print('...')
|
||||
transposed_lyrics = transpose_lyrics(self.verse_text_edit.toPlainText(), 1)
|
||||
self.verse_text_edit.setPlainText(transposed_lyrics)
|
||||
|
||||
def on_transepose_down_button_clicked(self):
|
||||
"""
|
||||
The transpose down button clicked
|
||||
"""
|
||||
print('...')
|
||||
transposed_lyrics = transpose_lyrics(self.verse_text_edit.toPlainText(), -1)
|
||||
self.verse_text_edit.setPlainText(transposed_lyrics)
|
||||
|
||||
def update_suggested_verse_number(self):
|
||||
"""
|
||||
|
@ -29,7 +29,7 @@ import re
|
||||
|
||||
from PyQt5 import QtWidgets
|
||||
|
||||
from openlp.core.common import AppLocation
|
||||
from openlp.core.common import AppLocation, Settings
|
||||
from openlp.core.lib import translate
|
||||
from openlp.core.utils import CONTROL_CHARS
|
||||
from openlp.plugins.songs.lib.db import MediaFile, Song
|
||||
@ -521,18 +521,6 @@ def strip_rtf(text, default_encoding=None):
|
||||
return text, default_encoding
|
||||
|
||||
|
||||
def transpose_lyrics(lyric, transepose_value):
|
||||
"""
|
||||
Transepose lyrics
|
||||
|
||||
:param lyrcs: The lyrics to be transposed
|
||||
:param transepose_value: The value to transpose the lyrics with
|
||||
:return: The transposed lyrics
|
||||
"""
|
||||
if '[' not in lyrics:
|
||||
return lyrics
|
||||
|
||||
|
||||
def delete_song(song_id, song_plugin):
|
||||
"""
|
||||
Deletes a song from the database. Media files associated to the song are removed prior to the deletion of the song.
|
||||
@ -554,3 +542,107 @@ def delete_song(song_id, song_plugin):
|
||||
except OSError:
|
||||
log.exception('Could not remove directory: %s', save_path)
|
||||
song_plugin.manager.delete_object(Song, song_id)
|
||||
|
||||
|
||||
def transpose_lyrics(lyrics, transepose_value):
|
||||
"""
|
||||
Transepose lyrics
|
||||
|
||||
:param lyrcs: The lyrics to be transposed
|
||||
:param transepose_value: The value to transpose the lyrics with
|
||||
:return: The transposed lyrics
|
||||
"""
|
||||
if '[' not in lyrics:
|
||||
return lyrics
|
||||
# Split the lyrics based on chord tags
|
||||
lyric_list = re.split('(\[|\]|/)', lyrics)
|
||||
transposed_lyrics = ''
|
||||
in_tag = False
|
||||
notation = Settings().value('songs/chord notation')
|
||||
for word in lyric_list:
|
||||
if not in_tag:
|
||||
transposed_lyrics += word
|
||||
if word == '[':
|
||||
in_tag = True
|
||||
else:
|
||||
if word == ']':
|
||||
in_tag = False
|
||||
transposed_lyrics += word
|
||||
elif word == '/':
|
||||
transposed_lyrics += word
|
||||
else:
|
||||
# This MUST be a chord
|
||||
transposed_lyrics += transpose_chord(word, transepose_value, notation)
|
||||
# If still inside a chord tag something is wrong!
|
||||
if in_tag:
|
||||
return lyrics
|
||||
else:
|
||||
return transposed_lyrics
|
||||
|
||||
def transpose_chord(chord, transpose_value, notation):
|
||||
"""
|
||||
Transpose chord according to the notation used.
|
||||
NOTE: This function has a javascript equivalent in chords.js - make sure to update both!
|
||||
|
||||
:param chord: The chord to transpose.
|
||||
:param transpose_value: The value the chord should be transposed.
|
||||
:param notation: The notation to use when transposing.
|
||||
:return: The transposed chord.
|
||||
"""
|
||||
# See https://en.wikipedia.org/wiki/Musical_note#12-tone_chromatic_scale
|
||||
notes_sharp_notation = {}
|
||||
notes_flat_notation = {}
|
||||
notes_sharp_notation['german'] = ['C','C#','D','D#','E','F','F#','G','G#','A','A#','H']
|
||||
notes_flat_notation['german'] = ['C','Db','D','Eb','Fb','F','Gb','G','Ab','A','B','H']
|
||||
notes_sharp_notation['english'] = ['C','C#','D','D#','E','F','F#','G','G#','A','A#','B']
|
||||
notes_flat_notation['english'] = ['C','Db','D','Eb','Fb','F','Gb','G','Ab','A','Bb','B']
|
||||
notes_sharp_notation['neo-latin'] = ['Do','Do#','Re','Re#','Mi','Fa','Fa#','Sol','Sol#','La','La#','Si']
|
||||
notes_flat_notation['neo-latin'] = ['Do','Reb','Re','Mib','Fab','Fa','Solb','Sol','Lab','La','Sib','Si']
|
||||
chord_split = chord.replace('♭', 'b').split('/[\/\(\)]/')
|
||||
transposed_chord = ''
|
||||
note = ''
|
||||
notenumber = -1
|
||||
rest = ''
|
||||
current_chord = ''
|
||||
last_chord = ''
|
||||
notes_sharp = notes_sharp_notation[notation]
|
||||
notes_flat = notes_flat_notation[notation]
|
||||
notes_preferred = ['b','#','#','#','#','#','#','#','#','#','#','#']
|
||||
chord_notes = []
|
||||
for i in range(0, len(chord_split)):
|
||||
if i > 0:
|
||||
transposed_chord += '/'
|
||||
currentchord = chord_split[i]
|
||||
if currentchord[0] == '(':
|
||||
transposed_chord += '('
|
||||
if len(currentchord) > 1:
|
||||
currentchord = currentchord[1:]
|
||||
else:
|
||||
currentchord = ""
|
||||
if len(currentchord) > 0:
|
||||
if len(currentchord) > 1:
|
||||
if '#b'.find(currentchord[1]) == -1:
|
||||
note = currentchord[0:1]
|
||||
rest = currentchord[1:]
|
||||
else:
|
||||
note = currentchord[0:2]
|
||||
rest = currentchord[2:]
|
||||
else:
|
||||
note = currentchord
|
||||
rest = ''
|
||||
notenumber = notes_flat.index(note) if note not in notes_sharp else notes_sharp.index(note)
|
||||
notenumber += transpose_value
|
||||
while notenumber > 11:
|
||||
notenumber -= 12
|
||||
while notenumber < 0:
|
||||
notenumber += 12
|
||||
if i == 0:
|
||||
current_chord = notes_sharp[notenumber] if notes_preferred[notenumber] == '#' else notes_flat[notenumber]
|
||||
last_chord = current_chord
|
||||
else:
|
||||
current_chord = notes_flat[notenumber] if last_chord not in notes_sharp else notes_sharp[notenumber]
|
||||
if not (note not in notes_flat and note not in notes_sharp):
|
||||
transposed_chord += current_chord + rest
|
||||
else:
|
||||
transposed_chord += note + rest
|
||||
return transposed_chord
|
||||
|
@ -24,7 +24,7 @@ This module contains tests for the lib submodule of the Songs plugin.
|
||||
"""
|
||||
from unittest import TestCase
|
||||
|
||||
from openlp.plugins.songs.lib import VerseType, clean_string, clean_title, strip_rtf
|
||||
from openlp.plugins.songs.lib import VerseType, clean_string, clean_title, strip_rtf, transpose_chord
|
||||
from openlp.plugins.songs.lib.songcompare import songs_probably_equal, _remove_typos, _op_length
|
||||
from tests.functional import patch, MagicMock
|
||||
|
||||
@ -264,6 +264,32 @@ class TestLib(TestCase):
|
||||
# THEN: The stripped text matches thed expected result
|
||||
assert result == exp_result, 'The result should be %s' % exp_result
|
||||
|
||||
def transpose_chord_up_test(self):
|
||||
"""
|
||||
Test that the transpose_chord() method works when transposing up
|
||||
"""
|
||||
# GIVEN: A Chord
|
||||
chord = 'C'
|
||||
|
||||
# WHEN: Transposing it 1 up
|
||||
new_chord = transpose_chord(chord, 1, 'english')
|
||||
|
||||
# THEN: The chord should be transposed up one note
|
||||
self.assertEqual(new_chord, 'C#', 'The chord should be transposed up.')
|
||||
|
||||
def transpose_chord_down_test(self):
|
||||
"""
|
||||
Test that the transpose_chord() method works when transposing down
|
||||
"""
|
||||
# GIVEN: A Chord
|
||||
chord = 'C'
|
||||
|
||||
# WHEN: Transposing it 1 down
|
||||
new_chord = transpose_chord(chord, -1, 'english')
|
||||
|
||||
# THEN: The chord should be transposed down one note
|
||||
self.assertEqual(new_chord, 'B', 'The chord should be transposed down.')
|
||||
|
||||
|
||||
class TestVerseType(TestCase):
|
||||
"""
|
||||
|
Loading…
Reference in New Issue
Block a user