Source code for music21.analysis.transposition

# ------------------------------------------------------------------------------
# Name:         transposition.py
# Purpose:      Tools for checking distinct transposition
#
# Authors:      Mark Gotham
#
# Copyright:    Copyright © 2017 Michael Scott Asato Cuthbert
# License:      BSD, see license.txt
# ------------------------------------------------------------------------------
from __future__ import annotations

from collections.abc import Iterable
import unittest

from music21 import chord
from music21 import common
from music21 import environment
from music21 import pitch

environLocal = environment.Environment('analysis.transposition')


[docs] class TranspositionChecker: ''' Given a list of pitches, checks for the number of distinct transpositions. >>> pList = [pitch.Pitch('C4'), pitch.Pitch('E4'), pitch.Pitch('G#4')] >>> tc = analysis.transposition.TranspositionChecker(pList) >>> tc.numDistinctTranspositions() 4 >>> allNormalOrderPitchTuples = tc.getPitchesOfDistinctTranspositions() >>> allNormalOrderPitchTuples [(<music21.pitch.Pitch C>, <music21.pitch.Pitch E>, <music21.pitch.Pitch G#>), (<music21.pitch.Pitch C#>, <music21.pitch.Pitch F>, <music21.pitch.Pitch A>), (<music21.pitch.Pitch D>, <music21.pitch.Pitch F#>, <music21.pitch.Pitch A#>), (<music21.pitch.Pitch E->, <music21.pitch.Pitch G>, <music21.pitch.Pitch B>)] >>> myChord = chord.Chord(['C', 'E-', 'F#', 'A']) >>> pList = myChord.pitches >>> tc = analysis.transposition.TranspositionChecker(pList) >>> allNormalOrderChords = tc.getChordsOfDistinctTranspositions() >>> allNormalOrderChords [<music21.chord.Chord C E- F# A>, <music21.chord.Chord C# E G A#>, <music21.chord.Chord D F G# B>] ''' def __init__(self, pitches: Iterable[pitch.Pitch] = ()): if not pitches: raise TypeError( 'Must have at least one element in list' ) if not common.isIterable(pitches): raise TypeError('Must be a list or tuple') # p0 = pitches[0] # if not isinstance(p0, pitch.Pitch): # raise TypeError('List must have pitch objects') self.pitches: Iterable[pitch.Pitch] = pitches self.allTranspositions: list = [] self.allNormalOrders: list = [] self.distinctNormalOrders: list = []
[docs] def getTranspositions(self): # noinspection PyShadowingNames ''' Gets all 12 transpositions (distinct or otherwise). >>> p = [pitch.Pitch('D#')] >>> tc = analysis.transposition.TranspositionChecker(p) >>> tc.getTranspositions() [[<music21.pitch.Pitch E->], [<music21.pitch.Pitch E>], [<music21.pitch.Pitch F>], [<music21.pitch.Pitch F#>], [<music21.pitch.Pitch G>], [<music21.pitch.Pitch G#>], [<music21.pitch.Pitch A>], [<music21.pitch.Pitch B->], [<music21.pitch.Pitch B>], [<music21.pitch.Pitch C>], [<music21.pitch.Pitch C#>], [<music21.pitch.Pitch D>]] ''' allTranspositions = [] for i in range(12): thisTransposition = [] for p in self.pitches: thisTransposition.append(p.transpose(i)) allTranspositions.append(thisTransposition) self.allTranspositions = allTranspositions return allTranspositions
[docs] def listNormalOrders(self): ''' List the normal orders for all 12 transpositions >>> pList = [pitch.Pitch('C4'), pitch.Pitch('E4'), pitch.Pitch('G#4')] >>> tc = analysis.transposition.TranspositionChecker(pList) >>> tc.listNormalOrders() [[0, 4, 8], [1, 5, 9], [2, 6, 10], [3, 7, 11], [0, 4, 8], [1, 5, 9], [2, 6, 10], [3, 7, 11], [0, 4, 8], [1, 5, 9], [2, 6, 10], [3, 7, 11]] ''' if not self.allTranspositions: self.getTranspositions() allTranspositions = self.allTranspositions allNormalOrders = [] for thisTransposition in allTranspositions: # pass c = chord.Chord(thisTransposition) thisNormalOrder = c.normalOrder allNormalOrders.append(thisNormalOrder) self.allNormalOrders = allNormalOrders return allNormalOrders
[docs] def listDistinctNormalOrders(self): ''' List the distinct normal orders (without duplication). >>> pList = [pitch.Pitch('C4'), pitch.Pitch('E4'), pitch.Pitch('G#4')] >>> tc = analysis.transposition.TranspositionChecker(pList) >>> tc.listDistinctNormalOrders() [[0, 4, 8], [1, 5, 9], [2, 6, 10], [3, 7, 11]] ''' if not self.allNormalOrders: self.listNormalOrders() allNormalOrders = self.allNormalOrders seen = set() distinctNormalOrders = [x for x in allNormalOrders if not (tuple(x) in seen or seen.add(tuple(x)))] self.distinctNormalOrders = distinctNormalOrders return distinctNormalOrders
[docs] def numDistinctTranspositions(self): ''' Gives the number of distinct transpositions (normal orders). >>> pList = [pitch.Pitch('C4'), pitch.Pitch('E4'), pitch.Pitch('G#4')] >>> tc = analysis.transposition.TranspositionChecker(pList) >>> tc.numDistinctTranspositions() 4 ''' if not self.distinctNormalOrders: self.listDistinctNormalOrders() return len(self.distinctNormalOrders)
[docs] def getChordsOfDistinctTranspositions(self): ''' Outputs chords for each distinct transposition (normal order). >>> pList = [pitch.Pitch('C4'), pitch.Pitch('E4'), pitch.Pitch('G#4')] >>> tc = analysis.transposition.TranspositionChecker(pList) >>> tc.getChordsOfDistinctTranspositions() [<music21.chord.Chord C E G#>, <music21.chord.Chord C# F A>, <music21.chord.Chord D F# A#>, <music21.chord.Chord E- G B>] ''' if not self.distinctNormalOrders: self.listDistinctNormalOrders() distinctNormalOrders = self.distinctNormalOrders allNormalOrderChords = [] for thisNormalOrder in distinctNormalOrders: thisNormalOrderChord = chord.Chord(thisNormalOrder) allNormalOrderChords.append(thisNormalOrderChord) return allNormalOrderChords
[docs] def getPitchesOfDistinctTranspositions(self): ''' Outputs pitch tuples for each distinct transposition (normal order). >>> pList = [pitch.Pitch('C4'), pitch.Pitch('E4'), pitch.Pitch('G#4')] >>> tc = analysis.transposition.TranspositionChecker(pList) >>> tc.getPitchesOfDistinctTranspositions() [(<music21.pitch.Pitch C>, <music21.pitch.Pitch E>, <music21.pitch.Pitch G#>), (<music21.pitch.Pitch C#>, <music21.pitch.Pitch F>, <music21.pitch.Pitch A>), (<music21.pitch.Pitch D>, <music21.pitch.Pitch F#>, <music21.pitch.Pitch A#>), (<music21.pitch.Pitch E->, <music21.pitch.Pitch G>, <music21.pitch.Pitch B>)] ''' chords = self.getChordsOfDistinctTranspositions() allNormalOrderPitchTuples = [c.pitches for c in chords] return allNormalOrderPitchTuples
# ------------------------------------------------------------------------------ class Test(unittest.TestCase): def testCopyAndDeepcopy(self): from music21.test.commonTest import testCopyAll testCopyAll(self, globals()) def testConstructTranspositionChecker(self): p = [pitch.Pitch('D#')] tc = TranspositionChecker(p) self.assertEqual(tc.pitches, p) numberOfPitchesInTc = len(tc.pitches) self.assertEqual(numberOfPitchesInTc, len(p)) def testTranspositions(self): p = [pitch.Pitch('D#')] tc = TranspositionChecker(p) allTranspositions = tc.getTranspositions() self.assertEqual(len(allTranspositions), 12) self.assertIsInstance(allTranspositions[0][0], pitch.Pitch) self.assertEqual(allTranspositions[0][0].midi, p[0].midi) self.assertEqual(allTranspositions[1][0].midi, p[0].midi + 1) p = [pitch.Pitch('D#'), pitch.Pitch('F')] tc = TranspositionChecker(p) allTranspositions = tc.getTranspositions() self.assertEqual(len(allTranspositions), 12) self.assertIsInstance(allTranspositions[0][0], pitch.Pitch) self.assertEqual(allTranspositions[0][0].midi, p[0].midi) self.assertEqual(allTranspositions[0][1].midi, p[1].midi) def testNormalOrders(self): pList = [pitch.Pitch('C4'), pitch.Pitch('E4'), pitch.Pitch('G#4')] tc = TranspositionChecker(pList) normalOrders = tc.listNormalOrders() self.assertEqual(len(normalOrders), 12) self.assertLess(normalOrders[0][0], 13) def testDistinctNormalOrders(self): pList = [pitch.Pitch('C4'), pitch.Pitch('E4'), pitch.Pitch('G#4')] tc = TranspositionChecker(pList) allDistinctNormalOrders = tc.listDistinctNormalOrders() lengthDistinctNormalOrders = tc.numDistinctTranspositions() self.assertEqual(len(allDistinctNormalOrders), 4) self.assertEqual(lengthDistinctNormalOrders, 4) self.assertIsInstance(allDistinctNormalOrders, list) self.assertEqual(allDistinctNormalOrders[0], [0, 4, 8]) def testNormalOrderChords(self): pList = [pitch.Pitch('C4'), pitch.Pitch('E4'), pitch.Pitch('G#4')] tc = TranspositionChecker(pList) allNormalOrderChords = tc.getChordsOfDistinctTranspositions() self.assertEqual(len(allNormalOrderChords), 4) # self.assertEqual(lengthDistinctNormalOrders, 4) self.assertIsInstance(allNormalOrderChords[0], chord.Chord) self.assertIsInstance(allNormalOrderChords[0].pitches[0], pitch.Pitch) # self.assertEqual(allDistinctNormalOrders[0], [0,4,8]) def testNormalOrdersPitches(self): pList = [pitch.Pitch('C4'), pitch.Pitch('E4'), pitch.Pitch('G#4')] tc = TranspositionChecker(pList) allNormalOrderPitchTuples = tc.getPitchesOfDistinctTranspositions() self.assertEqual(len(allNormalOrderPitchTuples), 4) # self.assertEqual(lengthDistinctNormalOrders, 4) self.assertIsInstance(allNormalOrderPitchTuples[0], tuple) self.assertIsInstance(allNormalOrderPitchTuples[0][0], pitch.Pitch) # self.assertEqual(allDistinctNormalOrders[0], [0,4,8]) # ----------------------------------------------------------------------------- if __name__ == '__main__': import music21 music21.mainTest(Test)