counterpoint.species – set of tools for dealing with Species Counterpoint and later other forms of counterpoint.
Mostly coded by Jackie Rogoff – some routines have been moved to by VoiceLeadingQuartet, and that module should be used for future work
Function to obtain a dictionary representation of a cantus firmus. Cantus firmi should be added to the list above in the format of a dictionary with the keys ‘notes’ and ‘mode’. Under ‘notes’ is a tiny notation string to be parsed into music21 objects, while ‘mode’ accesses a string representing the name of the related scale’s tonic, which can be made into a note and a scale object.
>>> from music21 import *
>>> cf = counterpoint.species.getRandomCF()
>>> cf.keys()
['notes', 'mode']
>>> isinstance(cf['notes'],str)
True
>>> isinstance(cf['mode'],str)
True
ModalCounterpoint attributes
Attributes without Documentation: legalMelodicIntervals, legalHarmonicIntervals, stream2, stream1, legalMiddleHarmonicIntervals
ModalCounterpoint methods
- allValidHarmony(stream1, stream2)¶
Given two simultaneous streams, returns True if all of the harmonies are legal and False if one or more is not. Legal harmonic intervals include ‘P1’, ‘P5’, ‘P8’, ‘m3’, ‘M3’, ‘m6’, and ‘M6’. Also assumes that final interval must be a perfect unison or octave.
>>> from music21 import * >>> n1 = note.Note('G4') >>> n2 = note.Note('A4') >>> n3 = note.Note('D4') >>> n4 = note.Note('C5') >>> m1 = note.Note('G4') >>> m2 = note.Note('A4') >>> m3 = note.Note('B4') >>> m4 = note.Note('C5') >>> bass = stream.Stream() >>> bass.append([n1, n2, n3, n4]) >>> sop = stream.Stream() >>> sop.append([m1, m2, m3, m4]) >>> cp = counterpoint.species.ModalCounterpoint(stream1 = sop, stream2 = bass) >>> cp.allValidHarmony(cp.stream1, cp.stream2) False >>> n4.name = 'C4' >>> cp.allValidHarmony(cp.stream1, cp.stream2) True >>> n1.name = 'F#4' >>> cp.allValidHarmony(cp.stream1, cp.stream2) False >>> n1.name = 'G4' >>> m4.name = 'G5' >>> cp.allValidHarmony(cp.stream1, cp.stream2) False
- allValidHarmonyMiddleVoices(stream1, stream2)¶
Given two simultaneous streams, returns True if all of the harmonies are legal and False if one or more is not. Legal harmonic intervals include ‘P1’, ‘P5’, ‘P8’, ‘m3’, ‘M3’, ‘m6’, and ‘M6’. As this is for middle voices, ‘P4’ is also allowed and the final interval is allowed to be a fifth.
>>> from music21 import * >>> n1 = note.Note('G4') >>> n2 = note.Note('A4') >>> n3 = note.Note('B4') >>> n4 = note.Note('C5') >>> m1 = note.Note('G4') >>> m2 = note.Note('A4') >>> m3 = note.Note('B4') >>> m4 = note.Note('C5') >>> bass = stream.Stream() >>> bass.append([n1, n2, n3, n4]) >>> sop = stream.Stream() >>> sop.append([m1, m2, m3, m4]) >>> cp = counterpoint.species.ModalCounterpoint(stream1 = bass, stream2 = sop) >>> cp.allValidHarmonyMiddleVoices(cp.stream1, cp.stream2) True >>> n1.name = 'F#4' >>> cp.allValidHarmonyMiddleVoices(cp.stream1, cp.stream2) False
- countBadHarmonies(stream1, stream2)¶
Given two simultaneous streams, counts the number of notes (in the first stream given) that create illegal harmonies when attacked.
>>> from music21 import * >>> n1 = note.Note('G4') >>> n2 = note.Note('A4') >>> n3 = note.Note('B4') >>> n4 = note.Note('C5') >>> m1 = note.Note('G4') >>> m2 = note.Note('A4') >>> m3 = note.Note('B4') >>> m4 = note.Note('C5') >>> bass = stream.Stream() >>> bass.append([n1, n2, n3, n4]) >>> sop = stream.Stream() >>> sop.append([m1, m2, m3, m4]) >>> cp = counterpoint.species.ModalCounterpoint(stream1 = bass, stream2 = sop) >>> cp.countBadHarmonies(cp.stream1, cp.stream2) 0 >>> n1.name = 'F#4' >>> cp.countBadHarmonies(cp.stream1, cp.stream2) 1
- countBadSteps(stream1)¶
Given a single stream, returns the number of illegal melodic intervals.
SHOULD BE RENAMED countBadMelodies?
>>> from music21 import * >>> n1 = note.Note('G-4') >>> n2 = note.Note('A4') >>> n3 = note.Note('B4') >>> n4 = note.Note('A5') >>> m1 = note.Note('G4') >>> m2 = note.Note('A4') >>> m3 = note.Note('B4') >>> m4 = note.Note('C5') >>> bass = stream.Stream() >>> bass.append([n1, n2, n3, n4]) >>> sop = stream.Stream() >>> sop.append([m1, m2, m3, m4]) >>> cp = counterpoint.species.ModalCounterpoint(stream1 = bass, stream2 = sop) >>> cp.countBadSteps(cp.stream1) 2 >>> n1.name = 'F#4' >>> cp.countBadSteps(cp.stream2) 0
- findAllBadFifths(stream1, stream2)¶
Given two streams, returns the total parallel and hidden fifths, and also puts the appropriate tags in note.editorial.misc under “Parallel Fifth” and “Hidden Fifth”.
>>> from music21 import * >>> n1 = note.Note('C4') >>> n2 = note.Note('D4') >>> n3 = note.Note('E4') >>> n4 = note.Note('F4') >>> s1 = stream.Stream() >>> s1.append([n1, n2, n3, n4]) >>> m1 = note.Note('G4') >>> m2 = note.Note('A4') >>> m3 = note.Note('G4') >>> m4 = note.Note('C5') >>> s2 = stream.Stream() >>> s2.append([m1, m2, m3, m4]) >>> cp = counterpoint.species.ModalCounterpoint(s1, s2) >>> cp.findAllBadFifths(cp.stream1, cp.stream2) 2
- findAllBadOctaves(stream1, stream2)¶
Given two streams, returns the total parallel and hidden octaves, and also puts the appropriate tags in note.editorial.misc under “Parallel Octave” and “Hidden Octave”.
>>> from music21 import * >>> n1 = note.Note('C4') >>> n2 = note.Note('D4') >>> n3 = note.Note('E4') >>> n4 = note.Note('F4') >>> s1 = stream.Stream() >>> s1.append([n1, n2, n3, n4]) >>> m1 = note.Note('C5') >>> m2 = note.Note('D5') >>> m3 = note.Note('C5') >>> m4 = note.Note('F5') >>> s2 = stream.Stream() >>> s2.append([m1, m2, m3, m4]) >>> cp = counterpoint.species.ModalCounterpoint(s1, s2) >>> cp.findAllBadOctaves(cp.stream1, cp.stream2) 2
- findHiddenFifths(stream1, stream2)¶
Given two streams, returns the number of hidden fifths and also assigns a flag under note.editorial.misc under “Hidden Fifth” for any note that has harmonic interval of a fifth where it creates a hidden parallel fifth. Note: a hidden fifth here is defined as anything where the two streams reach a fifth through parallel motion, but is not a parallel fifth.
>>> from music21 import * >>> n1 = note.Note('G3') >>> n2 = note.Note('A3') >>> n3 = note.Note('B3') >>> n4 = note.Note('C4') >>> m1 = note.Note('C4') >>> m2 = note.Note('E4') >>> m3 = note.Note('D4') >>> m4 = note.Note('G4') >>> bass = stream.Stream() >>> bass.append([n1, n2, n3, n4]) >>> sop = stream.Stream() >>> sop.append([m1, m2, m3, m4]) >>> cp = counterpoint.species.ModalCounterpoint(stream1 = bass, stream2 = sop) >>> cp.findHiddenFifths(cp.stream1, cp.stream2) 2 >>> n2.editorial.misc['Hidden Fifth'] True >>> cp.findHiddenFifths(cp.stream2, cp.stream1) 2
- findHiddenOctaves(stream1, stream2)¶
Given two streams, returns the number of hidden octaves and also assigns a flag under note.editorial.misc[“Hidden Octave”]for any note that has harmonic interval of an octave where it creates a hidden parallel octave. Note: a hidden octave here is defined as anything where the two streams reach an octave through parallel motion, but is not a parallel octave.
>>> from music21 import * >>> n1 = note.Note('F3') >>> n2 = note.Note('A3') >>> n3 = note.Note('A3') >>> n4 = note.Note('C4') >>> m1 = note.Note('G4') >>> m2 = note.Note('A4') >>> m3 = note.Note('B4') >>> m4 = note.Note('C5') >>> bass = stream.Stream() >>> bass.append([n1, n2, n3, n4]) >>> sop = stream.Stream() >>> sop.append([m1, m2, m3, m4]) >>> cp = counterpoint.species.ModalCounterpoint(stream1 = bass, stream2 = sop) >>> cp.findHiddenOctaves(cp.stream1, cp.stream2) 2 >>> n2.editorial.misc['Hidden Octave'] True >>> cp.findHiddenOctaves(cp.stream2, cp.stream1) 2 >>> m4.octave = 6 >>> cp.findHiddenOctaves(cp.stream2, cp.stream1) 2
- findParallelFifths(srcStream, cmpStream)¶
Given two streams, returns the number of parallel fifths and also assigns a flag under note.editorial.misc[“Parallel Fifth”] for any note that has harmonic interval of a fifth and is preceded by a harmonic interval of a fifth.
>>> from music21 import * >>> n1 = note.Note('G3') >>> n2 = note.Note('A3') >>> n3 = note.Note('B3') >>> n4 = note.Note('C4') >>> m1 = note.Note('D4') >>> m2 = note.Note('E4') >>> m3 = note.Note('F#4') >>> m4 = note.Note('G4') >>> bass = stream.Stream() >>> bass.append([n1, n2, n3, n4]) >>> sop = stream.Stream() >>> sop.append([m1, m2, m3, m4]) >>> cp = counterpoint.species.ModalCounterpoint(stream1 = bass, stream2 = sop) >>> cp.findParallelFifths(cp.stream1, cp.stream2) 3 >>> n1.editorial.harmonicInterval.name 'P5' >>> m4.octave = 5 #checking for 12ths as well >>> cp.findParallelFifths(cp.stream1, cp.stream2) 3
- findParallelOctaves(stream1, stream2)¶
Given two streams, returns the number of parallel octaves and also assigns a flag under note.editorial.misc[“Parallel Octave”] for any note that has harmonic interval of an octave and is preceded by a harmonic interval of an octave.
>>> from music21 import * >>> n1 = note.Note('G3') >>> n2 = note.Note('A3') >>> n3 = note.Note('B3') >>> n4 = note.Note('C4') >>> m1 = note.Note('G4') >>> m2 = note.Note('A4') >>> m3 = note.Note('B4') >>> m4 = note.Note('C5') >>> bass = stream.Stream() >>> bass.append([n1, n2, n3, n4]) >>> sop = stream.Stream() >>> sop.append([m1, m2, m3, m4]) >>> cp = counterpoint.species.ModalCounterpoint(stream1 = bass, stream2 = sop) >>> cp.findParallelOctaves(cp.stream1, cp.stream2) 3 >>> n2.editorial.misc['Parallel Octave'] True >>> cp.findParallelOctaves(cp.stream2, cp.stream1) 3 >>> m3.octave = 5 >>> m4.octave = 6 #check for parallel 17ths >>> cp.findParallelOctaves(cp.stream2, cp.stream1) 3
- findParallelUnisons(stream1, stream2)¶
Given two streams, returns the number of parallel unisons and also assigns a flag under note.editorial.misc[“Parallel Unison”] for any note that has harmonic interval of P1 and is preceded by a P1.
>>> from music21 import * >>> n1 = note.Note('G4') >>> n2 = note.Note('A4') >>> n3 = note.Note('B4') >>> n4 = note.Note('C5') >>> m1 = note.Note('G4') >>> m2 = note.Note('A4') >>> m3 = note.Note('B4') >>> m4 = note.Note('C5') >>> bass = stream.Stream() >>> bass.append([n1, n2, n3, n4]) >>> sop = stream.Stream() >>> sop.append([m1, m2, m3, m4]) >>> cp = counterpoint.species.ModalCounterpoint(stream1 = bass, stream2 = sop) >>> cp.findParallelUnisons(cp.stream1, cp.stream2) 3 >>> n2.editorial.misc['Parallel Unison'] True >>> cp.findParallelUnisons(cp.stream2, cp.stream1) 3
- generateFirstSpecies(cantusFirmus, minorScale, choice='random')¶
Doc
- generateFirstSpeciesThreeVoices(bottom, minorScale, choice='random')¶
Given a stream (the cantus firmus) and the stream’s key in the form of a MinorScale object, generates two streams of first species counterpoint that follow the rules of 21M.301 and together with the cantus firmus form valid three-part counterpoint.
choice is a flag that can be set to deterministically choose notes to add to the counterpoint. Right now, ‘random’, ‘first’, and ‘last’ are supported. This will be expanded so that all solution sets can be generated.
- generateValidLastNotes(prevFirmus, currFirmus, prevNote, afterLeap, minorScale, topVoice=True)¶
Helper function for generateFirstSpecies; gets a list of possible next notes based on valid melodic intervals, then checks each one so that parallel/hidden fifths/octaves, voice crossing, and invalid harmonies are prevented. Adds extra weight to notes that would create contrary motion.
- generateValidNotes(prevFirmus, currFirmus, prevNote, afterLeap, minorScale)¶
Helper function for getValidSecondVoice; gets a list of possible next notes based on valid melodic intervals, then checks each one so that parallel/hidden fifths/octaves, voice crossing, and invalid harmonies are prevented. Adds extra weight to notes that would create contrary motion.
- generateValidThirdNotes(prevBottom, currBottom, prevMiddle, currMiddle, prevTop, afterLeap, minorScale)¶
Helper function for getValidThirdVoice; gets a list of possible next notes based on valid melodic intervals, then checks each one so that parallel/hidden fifths/octaves, voice crossing, and invalid harmonies are prevented between all three voices
- getValidSecondVoice(stream1, minorScale, choice='random')¶
Given a stream (the cantus firmus) and the stream’s key in the form of a MinorScale object, generates a stream of first species counterpoint that follows the rules of 21M.301.
choice is a flag that can be set to deterministically choose notes to add to the counterpoint. Right now, ‘random’, ‘first’, and ‘last’ are supported. This will be expanded so that all solution sets can be generated.
- getValidThirdVoice(bottom, middle, minorScale, choice)¶
- isHiddenFifth(note11, note12, note21, note22)¶
Given four notes, assuming the first pair is one part and the second is another part sounding at the same time (i.e. argument order is isHiddenFifth(v1n1, v1n2, v2n1, v2n2)), returns True if there is a hidden fifth and false otherwise.
>>> from music21 import * >>> n1 = note.Note('G3') >>> n2 = note.Note('B-3') >>> m1 = note.Note('E4') >>> m2 = note.Note('F4') >>> cp = counterpoint.species.ModalCounterpoint() >>> cp.isHiddenFifth(n1, m1, n2, m2) #(n1, n2) and (m1, m2) are chords False >>> cp.isHiddenFifth(n1, n2, m1, m2) #(n1, m1) and (n2, m2) are chords True >>> m1.octave = 5 >>> m2.octave = 5 # check for hidden 12ths >>> cp.isHiddenFifth(n1, n2, m1, m2) True
- isHiddenOctave(note11, note12, note21, note22)¶
Given four notes, assuming the first pair is from one part and the second pair is from another part sounding at the same time, (i.e. argument order is isHiddenOctave(v1n1, v1n2, v2n1, v2n2)) returns True if there is a hidden octave and false otherwise.
>>> from music21 import * >>> n1 = note.Note('A3') >>> n2 = note.Note('B-3') >>> m1 = note.Note('F4') >>> m2 = note.Note('B-4') >>> cp = counterpoint.species.ModalCounterpoint() >>> cp.isHiddenOctave(n1, m1, n2, m2) #(n1, n2) and (m1, m2) are chords False >>> cp.isHiddenOctave(n1, n2, m1, m2) #(n1, m1) and (n2, m2) are chords True >>> m1.octave = 5 >>> m2.octave = 5 >>> cp.isHiddenOctave(n1, n2, m1, m2) True
- isParallelFifth(note11, note12, note21, note22)¶
Given four notes, assuming the first pair is one voice and the second pair is another voice sounding at the same time (i.e. argument order is isParallelFifth(v1n1, v1n2, v2n1, v2n2)), returns True if the two harmonic intervals are P5 and False otherwise.
>>> from music21 import * >>> n1 = note.Note('G3') >>> n2 = note.Note('B-3') >>> m1 = note.Note('D4') >>> m2 = note.Note('F4') >>> cp = counterpoint.species.ModalCounterpoint() >>> cp.isParallelFifth(n1, m1, n2, m2) #(n1, n2) and (m1, m2) are chords False >>> cp.isParallelFifth(n1, n2, m1, m2) #(n1, m1) and (n2, m2) are chords True >>> m1.octave = 5 >>> m2.octave = 5 #test parallel 12ths >>> cp.isParallelFifth(n1, n2, m1, m2) True
- isParallelOctave(note11, note12, note21, note22)¶
Given four notes, assuming the first pair sounds at the same time and the second pair sounds at the same time (i.e. argument order is isParallelOctave(v1n1, v1n2, v2n1, v2n2)), returns True if the two harmonic intervals are P8 and False otherwise.
>>> from music21 import * >>> n1 = note.Note('A3') >>> n2 = note.Note('B-3') >>> m1 = note.Note('A4') >>> m2 = note.Note('B-4') >>> cp = counterpoint.species.ModalCounterpoint() >>> cp.isParallelOctave(n1, m1, n2, m2) #(n1, n2) and (m1, m2) are chords False >>> cp.isParallelOctave(n1, n2, m1, m2) #(n1, m1) and (n2, m2) are chords True >>> m1.octave = 5 >>> m2.octave = 5 >>> cp.isParallelOctave(n1, n2, m1, m2) True
- isParallelUnison(note11, note12, note21, note22)¶
Given four notes, assuming the first pair sounds at the same time and the second pair sounds at the same time, (i.e. argument order is isParallelFifth(v1n1, v1n2, v2n1, v2n2)) returns True if the two harmonic intervals are P1 and False otherwise.
>>> from music21 import * >>> n1 = note.Note('A3') >>> n2 = note.Note('B-3') >>> m1 = note.Note('A3') >>> m2 = note.Note('B-3') >>> cp = counterpoint.species.ModalCounterpoint() >>> cp.isParallelUnison(n1, m1, n2, m2) #(n1, n2) and (m1, m2) are chords False >>> cp.isParallelUnison(n1, n2, m1, m2) #(n1, m1) and (n2, m2) are chords True >>> m1.octave = 4 >>> m2.octave = 4 >>> cp.isParallelUnison(n1, n2, m1, m2) #parallel octaves, not unison False
- isValidHarmony(note11, note21)¶
Determines if the harmonic interval between two given notes is “legal” according to 21M.301 rules of counterpoint. Legal harmonic intervals include ‘P1’, ‘P5’, ‘P8’, ‘m3’, ‘M3’, ‘m6’, and ‘M6’.
>>> from music21 import * >>> c = note.Note('C4') >>> d = note.Note('D4') >>> e = note.Note('E4') >>> cp = counterpoint.species.ModalCounterpoint() >>> cp.isValidHarmony(c, d) False >>> cp.isValidHarmony(c, c) True >>> cp.isValidHarmony(c, e) True
- isValidMelody(stream1)¶
Given a single stream, returns True if all melodic intervals between notes are legal and False otherwise. Legal melodic intervals include ‘P4’, ‘P5’, ‘P8’, ‘m2’, ‘M2’, ‘m3’, ‘M3’, and ‘m6’.
SHOULD BE RENAMED allValidMelody?
>>> from music21 import * >>> n1 = note.Note('G-4') >>> n2 = note.Note('A4') >>> n3 = note.Note('B4') >>> n4 = note.Note('B5') >>> m1 = note.Note('G4') >>> m2 = note.Note('A4') >>> m3 = note.Note('B4') >>> m4 = note.Note('C5') >>> bass = stream.Stream() >>> bass.append([n1, n2, n3, n4]) >>> sop = stream.Stream() >>> sop.append([m1, m2, m3, m4]) >>> cp = counterpoint.species.ModalCounterpoint(stream1 = bass, stream2 = sop) >>> cp.isValidMelody(cp.stream1) False >>> n1.name = 'F#4' >>> cp.isValidMelody(cp.stream2) True
- isValidMiddleHarmony(note11, note21)¶
Determines if the harmonic interval between two given notes is “legal” according to simple, species rules of counterpoint. Legal harmonic intervals for middle voices (i.e., not above the bass) include ‘P1’, ‘P5’, ‘P8’, ‘m3’, ‘M3’, ‘m6’, and ‘M6’, from before. ‘P4’ is now included because it is legal for middle harmonies.
>>> from music21 import * >>> c = note.Note('C4') >>> d = note.Note('D4') >>> e = note.Note('E4') >>> f = note.Note('F4') >>> cp = counterpoint.species.ModalCounterpoint() >>> cp.isValidMiddleHarmony(c, d) False >>> cp.isValidMiddleHarmony(c, c) True >>> cp.isValidMiddleHarmony(c, e) True >>> cp.isValidMiddleHarmony(c, f) True
- isValidStep(note11, note12)¶
Determines if the melodic interval between two given notes is “legal” according to 21M.301 rules of counterpoint. Legal melodic intervals include ‘P4’, ‘P5’, ‘P8’, ‘m2’, ‘M2’, ‘m3’, ‘M3’, and ‘m6’.
SHOULD BE RENAMED isValidMelody?
>>> from music21 import * >>> c = note.Note('C4') >>> d = note.Note('D4') >>> e = note.Note('E#4') >>> cp = counterpoint.species.ModalCounterpoint() >>> cp.isValidStep(c, d) True >>> cp.isValidStep(c, c) False >>> cp.isValidStep(c, e) False
- raiseLeadingTone(stream1, minorScale)¶
Given a stream of notes and a minor scale object, returns a new stream that raises all the leading tones of the original stream. Also raises the sixth if applicable to avoid augmented intervals.
>>> from music21 import * >>> n1 = note.Note('C4') >>> n2 = note.Note('G4') >>> n3 = note.Note('A4') >>> n4 = note.Note('G4') >>> n5 = note.Note('F4') >>> n6 = note.Note('G4') >>> n7 = note.Note('A4') >>> n8 = note.Note('F4') >>> n9 = note.Note('G4') >>> s1 = stream.Stream() >>> s2 = stream.Stream() >>> s1.append([n1, n2, n3, n4, n5, n6, n7, n8, n9]) >>> s2.append([n1, n2, n3, n4, n5, n6, n7, n8, n9]) >>> cp = counterpoint.species.ModalCounterpoint(s1, s2) >>> aMinor = scale.MinorScale(n3) >>> s2 = cp.raiseLeadingTone(s1, aMinor) >>> s2.notes[1].name 'G#' >>> s2.notes[3].name 'G' >>> s2.notes[4].name 'F#' >>> s2.notes[5].name 'G#' >>> s2.notes[7].name 'F'
- tooManySixths(stream1, stream2, limit=3)¶
Given two consecutive streams and a limit, returns True if the number of consecutive harmonic sixths exceeds the limit and False otherwise.
>>> from music21 import * >>> n1 = note.Note('E4') >>> n2 = note.Note('F4') >>> n3 = note.Note('G4') >>> n4 = note.Note('A4') >>> m1 = note.Note('C5') >>> m2 = note.Note('D5') >>> m3 = note.Note('E5') >>> m4 = note.Note('F5') >>> bass = stream.Stream() >>> bass.append([n1, n2, n3, n4]) >>> sop = stream.Stream() >>> sop.append([m1, m2, m3, m4]) >>> cp = counterpoint.species.ModalCounterpoint(stream1 = bass, stream2 = sop) >>> cp.tooManySixths(cp.stream1, cp.stream2) True >>> cp.tooManySixths(cp.stream1, cp.stream2, 4) False
- tooManyThirds(stream1, stream2, limit=3)¶
Given two consecutive streams and a limit, returns True if the number of consecutive harmonic thirds exceeds the limit and False otherwise.
>>> from music21 import * >>> n1 = note.Note('E4') >>> n2 = note.Note('F4') >>> n3 = note.Note('G4') >>> n4 = note.Note('A4') >>> m1 = note.Note('G4') >>> m2 = note.Note('A4') >>> m3 = note.Note('B4') >>> m4 = note.Note('C5') >>> bass = stream.Stream() >>> bass.append([n1, n2, n3, n4]) >>> sop = stream.Stream() >>> sop.append([m1, m2, m3, m4]) >>> cp = counterpoint.species.ModalCounterpoint(stream1 = bass, stream2 = sop) >>> cp.tooManyThirds(cp.stream1, cp.stream2) True >>> cp.tooManyThirds(cp.stream1, cp.stream2, 4) False