def findScoreByPitchThreshold(material, thresholdPitch, lowHigh): '''list, int, str --> [music21.stream.Score] It takes the list returned by the collectMaterial function, a pitch midi value, and the string "low" or "high" to look for those scores that contain pitchs lower or higher than the given threshold. ''' scores = [] for score in material: # Loading the score to get the parts list scorePath = score[0] scoreName = scorePath.split('/')[-1] loadedScore = converter.parse(scorePath) print(scoreName, 'parsed') parts = jS.findVoiceParts(loadedScore) # Work with each part for partIndex in range(1, len(score)): if len(score[partIndex]) == 0: continue # Skip part if it's empty # Get the notes from the current part part = parts[partIndex - 1] notes = part.flat.notes.stream() # Find segments to analyze in the current part for startEnd in score[partIndex]: start = startEnd[0] end = startEnd[1] segment = notes.getElementsByOffset(start, end) ############################################################### ## Change here so that it returns scores with the notes ## ## beyond the threshold colored in red. ## ############################################################### segmentAmbitus = segment.analyze('ambitus') ambitusStart = segmentAmbitus.noteStart.midi ambitusEnd = segmentAmbitus.noteEnd.midi if lowHigh == 'low': if ambitusStart < pitch.Pitch(thresholdPitch).midi: if scoreName not in scores: scores.append(scoreName) if lowHigh == 'high': if ambitusEnd > pitch.Pitch(thresholdPitch).midi: if scoreName not in scores: scores.append(scoreName) print('Done!') return scores
def getAmbitus(material): '''list --> music21.interval.Interval It takes the list returned by the collectMaterial function, and returns an interval from the lowest note found to the highest note found. ''' ambitusStart = None ambitusEnd = None for score in material: # Loading the score to get the parts list scorePath = score[0] scoreName = scorePath.split('/')[-1] loadedScore = converter.parse(scorePath) print(scoreName, 'parsed') parts = jS.findVoiceParts(loadedScore) # Work with each part for partIndex in range(1, len(score)): if len(score[partIndex]) == 0: continue # Skip part if it's empty # Get the notes from the current part part = parts[partIndex - 1] notes = part.flat.notes.stream() # Find segments to analyze in the current part for startEnd in score[partIndex]: start = startEnd[0] end = startEnd[1] segment = notes.getElementsByOffset(start, end) segmentAmbitus = segment.analyze('ambitus') if ambitusStart == None and ambitusEnd == None: ambitusStart = segmentAmbitus.noteStart ambitusEnd = segmentAmbitus.noteEnd else: if segmentAmbitus.noteStart.midi < ambitusStart.midi: ambitusStart = segmentAmbitus.noteStart if segmentAmbitus.noteEnd.midi > ambitusEnd.midi: ambitusEnd = segmentAmbitus.noteEnd ambitusInterval = interval.Interval(ambitusStart, ambitusEnd) print('Ambitus:', ambitusInterval.niceName + ', from', ambitusStart.nameWithOctave, 'to', ambitusEnd.nameWithOctave) return ambitusInterval
def findScoreByPitch(material, pitchList): ''' ''' scores = [] for score in material[1:]: showScore = False pitchesFound = {} # Loading the score to get the parts list scorePath = score[0] scoreName = scorePath.split('/')[-1] loadedScore = converter.parse(scorePath) print(scoreName, 'parsed') parts = jS.findVoiceParts(loadedScore) # Work with each part for partIndex in range(1, len(score)): if len(score[partIndex]) == 0: continue # Skip part if it's empty # Get the notes from the current part part = parts[partIndex - 1] notes = part.flat.notes.stream() # Find segments to analyze in the current part for startEnd in score[partIndex]: start = startEnd[0] end = startEnd[1] segment = notes.getElementsByOffset(start, end) for n in segment: noteName = n.nameWithOctave if noteName in pitchList: n.color = 'red' pitchesFound[noteName] = pitchesFound.get(noteName, 0) + 1 showScore = True if scorePath not in scores: scores.append(scorePath) if showScore: for p in pitchesFound: print('\t' + str(pitchesFound[p]), 'samples of', p, 'found in this score') print('\tShowing', scoreName) loadedScore.show() return (scores)
def findLine(material, inputScore, patterns, a, b): '''list, list, list, int, int Inputs are the material, input score and patterns lists, and the index of the pattern and occurrence in the patterns list. ''' line = patterns[a][b] loc = line[-1][0] init = line[-1][1] print('Original line:') originalLine = inputScore[loc] for n in originalLine: print(n) print('Found pattern:') for n in line: print(n) x = material[-1][loc] score = material[x[0]][0] segment = material[x[0]][x[1]][x[2]] segStart = float(segment[0]) segEnd = float(segment[1]) s = converter.parse(score) print(score.split('/')[-1], 'parsed') parts = jS.findVoiceParts(s) part = parts[x[1] - 1] notes = part.flat.notesAndRests.stream() seg2show = notes.getElementsByOffset(segStart, segEnd) i = 0 while i < init: n = seg2show[i] i += 1 if (n.tie != None) and (n.tie.type != 'start'): init += 1 jump = 0 for j in range(len(line) - 1): n = seg2show[init + j + jump] if (n.tie != None) and (n.tie.type != 'start'): n.color = 'red' jump += 1 else: n.color = 'red' seg2show.show()
def showPatternsFromPickle(lyricsData, materialFile, inputScoreFile, resultsFile): ''' ''' with open(lyricsData, 'r', encoding='utf-8') as f: data = f.readlines() with open(materialFile, 'rb') as f: material = pickle.load(f) with open(inputScoreFile, 'rb') as f: inputScore = pickle.load(f) with open(resultsFile, 'rb') as f: patterns = pickle.load(f) print(len(patterns), 'patterns to show') dataDict = {} currentScore = '' for l in data: strInfo = l.strip().split(',') score = strInfo[0] if score != '': currentScore = score dataDict[currentScore] = [[]] if 'Part' in l: continue else: if 'Part' in l: dataDict[currentScore].append([]) continue info = strInfo[1] + ', ' + strInfo[2] + ', ' + strInfo[ 3] + ', ' + strInfo[4] start = strInfo[6] end = strInfo[7] dataDict[currentScore][-1].append([start, end, info]) ks = key.KeySignature(4) for i in range(len(patterns)): pat = patterns[i] print('\nDisplaying pattern', i + 1, 'with', len(pat), 'occurrences') s1 = stream.Score() s1.insert(0, metadata.Metadata(movementName='Pattern ' + str(i + 1))) for j in range(len(pat)): occ = pat[j] locator = occ[-1] line = locator[0] init = locator[1] # Chek if the occurrence retrieved coincides with a fragment of the # input score origLine = inputScore[line] for k in range(len(occ) - 1): if occ[k] != origLine[k + init]: print(origLine) print(occ) raise Exception('No match in result ' + str(i) + ', ' + str(j)) lineCoordinates = material[-1][line] s = lineCoordinates[0] p = lineCoordinates[1] l = lineCoordinates[2] scorePath = material[s][0] segStart = material[s][p][l][0] segEnd = material[s][p][l][1] s2 = converter.parse(scorePath) parts = jS.findVoiceParts(s2) part = parts[p - 1] notes = part.flat.notesAndRests.stream() seg2red = notes.getElementsByOffset(segStart, segEnd) newInit = 0 while newInit < init: note2check = seg2red[newInit] newInit += 1 if (note2check.tie != None) and (note2check.tie.type != 'start'): init += 1 tieJump = 0 # It stores how many tied notes are present for n in range(len(occ) - 1): note2red = seg2red[n + newInit + tieJump] while (note2red.tie != None) and (note2red.tie.type != 'start'): tieJump += 1 note2red = seg2red[n + newInit + tieJump] if note2red.isRest: noteName = note2red.name else: noteName = note2red.nameWithOctave if noteName != occ[n][0]: print('ERROR: An exception will be raised') findLine(material, inputScore, patterns, i, j) raise Exception("Notes doesn't match at " + str(i) + ', ' + str(j) + ', ' + str(k) + ' (' + noteName + ', ' + occ[n][0] + ')') note2red.color = 'red' tieHop = n + newInit + tieJump + 1 if note2red.tie != None: while (seg2red[tieHop].tie != None and seg2red[tieHop].tie.type != 'start' and tieHop < len(occ)): seg2red[tieHop].color = 'red' tieHop += 1 scoreName = scorePath.split('/')[-1] score = dataDict[scoreName] lineHop = 0 dataLine = score[p - 1][lineHop] while not ((segStart >= float(dataLine[0])) and (segStart < float(dataLine[1]))): lineHop += 1 dataLine = score[p - 1][lineHop] segmentStart = float(dataLine[0]) segmentEnd = float(dataLine[1]) bsju = dataLine[2].split(', ')[2] + ', ' + dataLine[2].split( ', ')[3] referenceText = scoreName + ': ' + str(lineHop + 1) + ' (' + bsju + ')' te = expressions.TextExpression(referenceText) te.positionVertical = 30 seg2add = notes.getElementsByOffset(segmentStart, segmentEnd) offsetHop = seg2add[0].offset for nn in seg2add: nn.offset += -offsetHop seg2add.insert(0, te) s1.insert(0, seg2add) for s1part in s1.parts: s1part.insert(0, ks) s1.makeNotation() s1.show()
def concatenateSegments(material, title=None): '''list --> music21.stream.Stream, list It takes the list returned by the collectMaterial function, and returns music21.stream.Stream with all the segments conatined in the material list concatenated into a single stave. It also returns the material list with a new list appended with the information to reconstruct the segments in their original scores from the new concatenated score. This new list contain a list of integers indicating: [start, end, score, part, segment] So that, - start: indicates the starting offset of a segment in the concatenated score - end: indicates the ending offset of a segment in the concatenated score - score: indicates the index in the material list of the score from where the original segment came from - part: indicates the index of the part in the previous score - segment: indicates the index of the segment as stored for the previous part in the material list If a title is given, it generates an xml file with the concatenated score and a pickle file with the material list ''' # Gather search info to name the concatenated score searchString = '' searchInfo = material[0] # Add hangdang info hd = searchInfo['hd'] if len(hd) != 2: for e in hd: searchString += e + '/' searchString = searchString[:-1] + ', ' # Add shengqiang info sq = searchInfo['sq'] if len(sq) != 2: for e in sq: searchString += e + '/' searchString = searchString[:-1] + ', ' # Add banshi info bs = searchInfo['bs'] if len(bs) != 8: for e in bs: searchString += e + '/' searchString = searchString[:-1] + ', ' # Add ju info ju = searchInfo['ju'] if len(ju) != 4: for e in ju: searchString += e + '/' searchString = searchString[:-1] concatenatedScore = stream.Stream() concatenatedSegments = [] accumulatedOffset = 0 for scoreIndex in range(1, len(material)): score = material[scoreIndex] scorePath = score[0] scoreName = scorePath.split('/')[-1] loadedScore = converter.parse(scorePath) print(scoreName, 'parsed') parts = jS.findVoiceParts(loadedScore) # Work with each part for partIndex in range(1, len(score)): if len(score[partIndex]) == 0: continue # Skip part if it's empty # Get the notes from the current part part = parts[partIndex - 1] notes = part.flat.notesAndRests.stream() # Find segments to analyze in the current part for segmentIndex in range(len(score[partIndex])): startEnd = score[partIndex][segmentIndex] start = startEnd[0] end = startEnd[1] segment = notes.getElementsByOffset(start, end) # Reassigning offsets newSegment = [accumulatedOffset] startingOffset = segment[0].offset endingOffset = segment[-1].offset for n in segment: n.offset += -startingOffset + accumulatedOffset concatenatedScore.append(n) accumulatedOffset += (endingOffset - startingOffset) newSegment.append(accumulatedOffset) newSegment.extend([scoreIndex, partIndex, segmentIndex]) accumulatedOffset += segment[-1].quarterLength concatenatedSegments.append(newSegment) extendedMaterial = copy.deepcopy(material) extendedMaterial.append(concatenatedSegments) # Check that the newSegments are equally long to the original segments: for newSegment in extendedMaterial[-1]: newSegmentStart = newSegment[0] newSegmentEnd = newSegment[1] length1 = newSegmentEnd - newSegmentStart score = newSegment[2] part = newSegment[3] segment = newSegment[4] originalSegment = extendedMaterial[score][part][segment] originalSegmentStart = originalSegment[0] originalSegmentEnd = originalSegment[1] length2 = originalSegmentEnd - originalSegmentStart if length1 != length2: print('Possible error with ' + extendedMaterial[score][0] + ', part ' + str(part) + ', segment ' + str(extendedMaterial[score][part][segment]) + ', and the new segment ' + str(newSegment[:2])) if title != None: print('Segments concatenated\nCreating files') concatenatedScore.insert(0, metadata.Metadata()) concatenatedScore.title = title concatenatedScore.write(fp=title + '.xml') with open(title + '.pkl', 'wb') as f: pickle.dump(extendedMaterial, f, protocol=2) print('Done!') return concatenatedScore, extendedMaterial
def recodeScore(material, title=None, graceNoteValue=2.0, noteName='pitch'): ''' ''' # Check that the given noteName is valid: if noteName not in ['pitch', 'midi']: raise Exception('The given noteName is invalid') print('The duration unit is a 64th note') print('The duration value for grace notes is ' + str(graceNoteValue) + ' duration units') # List the recoded score recodedScore = [] # Store information for line retrieval lineInfo = [] for scoreIndex in range(1, len(material)): score = material[scoreIndex] scorePath = score[0] scoreName = scorePath.split('/')[-1] loadedScore = converter.parse(scorePath) print(scoreName, 'parsed') parts = jS.findVoiceParts(loadedScore) # Work with each part for partIndex in range(1, len(score)): if len(score[partIndex]) == 0: continue # Skip part if it's empty # Get the notes from the current part part = parts[partIndex - 1] notes = part.flat.notesAndRests.stream() # Find segments to analyze in the current part for segmentIndex in range(len(score[partIndex])): startEnd = score[partIndex][segmentIndex] start = startEnd[0] end = startEnd[1] segment = notes.getElementsByOffset(start, end) # For validation segmentDuration = 0 for n in segment: segmentDuration += n.quarterLength * 16 if segment[-1].isRest: segmentDuration += -segment[-1].quarterLength * 16 r = -2 while segment[r].quarterLength == 0: segmentDuration += graceNoteValue r += -1 if segment[-1].quarterLength == 0: segmentDuration += graceNoteValue # START RECODING line = [] lineInfo.append([scoreIndex, partIndex, segmentIndex]) graceNote = 0 # It stores the accumulated dur of grace notes # to be substracted notePreGrace = None # It stores the index of the note before # grace notes found includeLyric = True # Check if there are several syllables into # brackets that shouldn't be included lyricAdjustment = 0 # Stores how many grace notes back the # lyric should be added for i in range(len(segment)): n = segment[i] # Check if n is note or rest if n.isRest: name = n.name dur = n.quarterLength * 16 lyr = False else: # If it is a note # Check if it is a grace note if n.quarterLength == 0: # It is a grace note, then # Set name if noteName == 'pitch': name = n.nameWithOctave elif noteName == 'midi': name = n.pitch.midi # Set duration with the value given dur = graceNoteValue # Accumulate grace note value to be subtracted graceNote += graceNoteValue # Store the index of the previous note, if there is # one and is not a grace note if (notePreGrace == None) and (len(line) > 0): notePreGrace = len(line) - 1 # Set lyric lyr = False # Update lyricAdjustment lyricAdjustment += -1 else: # If it's not a grace note, then # Set name if noteName == 'pitch': name = n.nameWithOctave elif noteName == 'midi': name = n.pitch.midi # Set duration currentNoteDur = n.quarterLength * 16 # Check if there is some grace note value to be # subtracted if graceNote > 0: # There is grace note(s) duration to be subtracted if n.hasLyrics(): # Subtract grace note value from the current # note. # But check first if its duration is bigger # than the one of the grace note(s) if currentNoteDur > graceNote: dur = currentNoteDur - graceNote else: # Try to substract it from previous note if notePreGrace != None: # There is a previous note... lastNote = line[notePreGrace] lastNoteDur = lastNote[1] if lastNoteDur > graceNote: # ... and its duration is bigger # than the grace note(s) duration lastNote[1] += -graceNote dur = currentNoteDur else: # But if not, adjust adjustment = 0 for j in range( notePreGrace + 1, i): note2adjust = line[j] note2adjust[1] += -1 adjustment += 1 dur = (currentNoteDur - graceNote + adjustment) else: # There is no previous note, so adjust adjustment = 0 for j in range(i): note2adjust = line[j] note2adjust[1] += -1 adjustment += 1 dur = (currentNoteDur - graceNote + adjustment) else: # Current note has no lyrics, the grace note(s) # duration is subtracted from the previous note # But check first if its duration is bigger # than the one of the grace note(s) lastNote = line[notePreGrace] lastNoteDur = lastNote[1] if lastNoteDur > graceNote: # It is bigger, duration of grace note(s) # subtracted from previous note lastNote[1] += -graceNote dur = currentNoteDur else: # It is not bigger # Check if the current note duration is # bigger than the grace note(s) duration if currentNoteDur > graceNote: # It is bigger, so subtract dur = currentNoteDur - graceNote else: # It is not bigger, so adjust adjustment = 0 for j in range(notePreGrace, i): note2adjust = line[j] note2adjust[1] += -1 adjustment += 1 lastNote[1] += (-graceNote + adjustment) dur = currentNoteDur # Set lyricAdjustment to 0 lyricAdjustment = 0 else: # There is no grace note(s) duration to subtract dur = currentNoteDur #Check if it has a tie if n.tie != None: if n.tie.type != 'start': # Check if there is a grace note if graceNote > 0: # There is a grace note, so current note # counts as not tied dur = currentNoteDur else: # There is no grace note, so add the dur # to the previous tied note line[-1][1] += currentNoteDur continue # Set lyric if n.hasLyrics(): # Check if the lyric is a padding syllable if ('(' in n.lyric) and (')' in n.lyric): lyr = False elif ('(' in n.lyric) and (')' not in n.lyric): lyr = False includeLyric = False elif ('(' not in n.lyric) and (')' in n.lyric): lyr = False includeLyric = True else: if includeLyric: # It is not a padding syllable if lyricAdjustment == 0: # It has no grace notes: lyr = True else: # It has grace note(s): line[lyricAdjustment][2] = True lyr = False else: lyr = False else: lyr = False # Set all counters to start mode notePreGrace = None graceNote = 0 lyricAdjustment = 0 if dur <= 0: pos = str(n.offset) message = ('\tDuration ' + str(dur) + ' in ' + scoreName + ', ' + pos) print(message) line.append([name, dur, lyr]) # Check if last note is a rest if line[-1][0] == 'rest': line.pop(-1) # For validation: lineDuration = 0 for n in line: lineDuration += n[1] if segmentDuration != lineDuration: print("\tDurations don't match at line", len(recodedScore)) print("\tSegment length: " + str(segmentDuration) + ", line length: " + str(lineDuration)) recodedScore.append(line) # Extend material list if len(lineInfo) != len(recodedScore): print('Possible problem with the information for line retrieval') extendedMaterial = copy.deepcopy(material) extendedMaterial.append(lineInfo) # Dump the list into a pickle file if title != None: with open(title, 'wb') as f: pickle.dump(recodedScore, f, protocol=2) with open(title[:-4] + '_material.pkl', 'wb') as f: pickle.dump(extendedMaterial, f, protocol=2) return recodedScore, extendedMaterial
def findCadentialNotes(judouMaterial, includeGraceNotes=True): ''' ''' cadNotCount = [{}, {}, {}] for score in judouMaterial[1:]: scorePath = score[0] loadedScore = converter.parse(scorePath) scoreName = scorePath.split('/')[-1] print(scoreName, 'parsed') parts = jS.findVoiceParts(loadedScore) # Work with each part for partIndex in range(1, len(score)): if len(score[partIndex]) == 0: continue # Skip part if it's empty # Get the notes from the current part part = parts[partIndex - 1] notes = part.flat.notesAndRests.stream() # Find segments to analyze in the current part for segInd in range(len(score[partIndex])): startEnd = score[partIndex][segInd] start = startEnd[0] end = startEnd[1] segment = notes.getElementsByOffset(start, end) i = -1 lastNote = segment[i] while lastNote.isRest: i += -1 lastNote = segment[i] if includeGraceNotes: cadenceNote = lastNote.nameWithOctave else: while lastNote.quarterLength == 0: print('Grace note omitted in ' + scoreName + ', ' + str(partIndex)) i += -1 lastNote = segment[i] cadenceNote = lastNote.nameWithOctave secInd = segInd % 3 sec = cadNotCount[secInd] sec[cadenceNote] = sec.get(cadenceNote, 0) + 1 noteNames = {} for secCount in cadNotCount: for noteName in secCount.keys(): noteNames[pitch.Pitch(noteName).midi] = noteName sortedNoteNames = [noteNames[j] for j in sorted(noteNames.keys())] for secCount in cadNotCount: counts = np.array([k for k in secCount.values()]) toPerCent = 100 / sum(counts) for noteName in secCount: secCount[noteName] = secCount[noteName] * toPerCent sortedValues = [] for noteName in sortedNoteNames: row = [] for secCount in cadNotCount: row.append(secCount.get(noteName, 0)) sortedValues.append(np.array(row)) # for i in range(len(cadNotCount)): # toDiscard, noteNames, noteCount = sortDict(cadNotCount[i]) # noteCount = np.array(noteCount) # toPerCent = 100 / np.sum(noteCount) # notePerCent = noteCount * toPerCent return sortedNoteNames, sortedValues
def melodicDensity(material, includeGraceNotes=True, notesOrDuration='notes'): '''list --> box plot It takes the list returned by the collectMaterial function, and returns ''' if notesOrDuration not in ['notes', 'duration']: raise Exception('The given value for notesOrDuration is not correct') syllables = [] totalCount = [] accumulatedCount = [] for score in material[1:]: # Loading the score to get the parts list scorePath = score[0] scoreName = scorePath.split('/')[-1] loadedScore = converter.parse(scorePath) print(scoreName, 'parsed') localCount = [] parts = jS.findVoiceParts(loadedScore) # Work with each part for partIndex in range(1, len(score)): if len(score[partIndex]) == 0: continue # Skip part if it's empty # Get the notes from the current part part = parts[partIndex - 1] notes = part.flat.notesAndRests.stream() # Find segments to analyze in the current part for startEnd in score[partIndex]: start = startEnd[0] end = startEnd[1] segment = notes.getElementsByOffset(start, end) openParenthesis = False graceNote = False for i in range(len(segment)): n = segment[i] if notesOrDuration == 'notes': value = 1 else: value = n.quarterLength if n.isRest: continue if n.quarterLength == 0: if not includeGraceNotes: continue j = 1 while (i + j < len(segment) and segment[i + j].quarterLength == 0): j += 1 if i + j == len(segment): continue n2 = segment[i + j] if n2.hasLyrics(): if (('(' in n2.lyric) or (')' in n2.lyric) or openParenthesis): localCount[-1] += value accumulatedCount[-1] += value else: if graceNote: localCount[-1] += value accumulatedCount[-1] += value else: localCount.append(value) accumulatedCount.append(value) syllables.append(n2.lyric) graceNote = True else: localCount[-1] += value accumulatedCount[-1] += value else: if n.hasLyrics(): # Check if the lyric is a padding syllable if ('(' in n.lyric) and (')' in n.lyric): localCount[-1] += value accumulatedCount[-1] += value elif ('(' in n.lyric) and (')' not in n.lyric): localCount[-1] += value accumulatedCount[-1] += value openParenthesis = True elif ('(' not in n.lyric) and (')' in n.lyric): localCount[-1] += value accumulatedCount[-1] += value openParenthesis = False else: if openParenthesis: localCount[-1] += value accumulatedCount[-1] += value elif graceNote: localCount[-1] += value accumulatedCount[-1] += value graceNote = False else: localCount.append(value) accumulatedCount.append(value) syllables.append(n.lyric) else: localCount[-1] += value accumulatedCount[-1] += value totalCount.append(localCount) # for i in range(len(syllables)): # print(syllables[i], notesPerSyl[i]) totalCount.append(accumulatedCount) # notesPerSyl = np.array(notesPerSyl) xLabels = [str(i) for i in range(1, len(totalCount))] xLabels.append('Avg') plt.boxplot(totalCount) plt.xticks(range(1, len(totalCount) + 1), xLabels, fontsize=20) plt.yticks(fontsize=18) plt.axvline(x=len(totalCount) - 0.5, ls='--', color='red') plt.ylim(0, 27) plt.xlabel('Sample scores', fontsize=26) plt.ylabel('Duration per quarter note', fontsize=26) plt.tight_layout() plt.show() return totalCount
def intervalHistogram(material, count='sum', directedInterval=False, silence2ignore=0.25, ignoreGraceNotes=False, makePlot=True): '''list --> , bar plot It takes the list returned by the collectMaterial function, and returns For the bar diagram to be plotted, the values can be normalised according to count: - if count=='sum', they are normalised to their summation, - if count=='max', they are normalised to their maximun value - if count=='abs', they are not normalised, but absolute values given ''' intervalCount = {} for score in material[1:]: # Loading the score to get the parts list scorePath = score[0] scoreName = scorePath.split('/')[-1] loadedScore = converter.parse(scorePath) print(scoreName, 'parsed') parts = jS.findVoiceParts(loadedScore) # Work with each part for partIndex in range(1, len(score)): if len(score[partIndex]) == 0: continue # Skip part if it's empty # Get the notes from the current part part = parts[partIndex - 1] notes = part.flat.notesAndRests.stream() # Find segments to analyze in the current part for startEnd in score[partIndex]: start = startEnd[0] end = startEnd[1] segment = notes.getElementsByOffset(start, end) # Count intervals in the current segment # Find the last note that is not a grace note i = 1 lastn = segment[-i] while lastn.quarterLength == 0: i += 1 lastn = segment[-i] for j in range(len(segment) - i): n1 = segment[j] if n1.isRest: continue if ignoreGraceNotes: if n1.quarterLength == 0: continue k = 1 while True: n2 = segment[j + k] if n2.isRest: if n2.quarterLength <= silence2ignore: k += 1 else: n2 = None break elif (n2.quarterLength == 0) and (ignoreGraceNotes == True): j += 1 else: break if n2 == None: continue intvl = interval.Interval(n1, n2) if directedInterval: intvlName = intvl.directedName else: intvlName = intvl.name intervalCount[intvlName] = ( intervalCount.get(intvlName, 0) + 1) # Sorting intervals per size intvlNames = intervalCount.keys() toSort = {i: interval.Interval(i).semitones for i in intvlNames} sortedIntvl = sorted(toSort.items(), key=lambda x: x[1]) xPositions = np.array([i[1] for i in sortedIntvl]) # Check if there repeated positions for i in range(1, len(xPositions)): if xPositions[i] != xPositions[i - 1]: continue for j in range(i): xPositions[j] += -1 xLabels = [i[0] for i in sortedIntvl] yValues = np.array([intervalCount[l] for l in xLabels]) ## Setting the parameters for plotting yValues, limX, yLabel, col, h = plottingParameters(material, count, yValues) if makePlot: # Start plotting print('Plotting...') # Setting x limits limX = None # Setting y limits limY = None if count == 'sum': if directedInterval: limY = [0, 0.27] else: limY = [0, 0.5] plotting(xPositions, xLabels, yValues, limX=limX, xLabel='Interval', limY=limY, yLabel=yLabel, col=col, h=h, scaleGuides=True, width=0.8) # List to return results = [] for i in range(len(xLabels)): results.append([xLabels[i], yValues[i]]) return results
def pitchHistogram(material, count='sum', countGraceNotes=True, makePlot=True): '''list --> dict, bar plot It takes the list returned by the collectMaterial function, and returns a dictionary with all the existing pitches' nameWithOctave as keys and its aggregated duration in quarterLengths as values. For the bar diagram to be plotted, the values can be normalised according to count: - if count=='sum', they are normalised to their summation, - if count=='max', they are normalised to their maximun value - if count=='abs', they are not normalised, but absolute values given If countGraceNotes==True, the grace notes will be counted with a duration value equivalent to the minimum one present in the analysed segments, but with a maximum value of 0.25. If countGraceNotes==False, grace notes will be ignored and not counted. ''' pitchCount = {} for score in material[1:]: # Loading the score to get the parts list scorePath = score[0] scoreName = scorePath.split('/')[-1] loadedScore = converter.parse(scorePath) print(scoreName, 'parsed') parts = jS.findVoiceParts(loadedScore) # Work with each part for partIndex in range(1, len(score)): if len(score[partIndex]) == 0: continue # Skip part if it's empty # Get the notes from the current part part = parts[partIndex - 1] notes = part.flat.notes.stream() # Set the duration of grace notes if needed if countGraceNotes: minDur = 0.25 for n in notes: noteDur = n.quarterLength if noteDur != 0 and noteDur < minDur: minDur = noteDur # Find segments to analyze in the current part for startEnd in score[partIndex]: start = startEnd[0] end = startEnd[1] segment = notes.getElementsByOffset(start, end) # Count pitches in the current segment for n in segment: noteName = n.nameWithOctave noteDur = n.quarterLength if noteDur == 0: if not countGraceNotes: continue noteDur = minDur pitchCount[noteName] = pitchCount.get(noteName, 0) + noteDur # Sorting duration per pitch class frequency pitches = pitchCount.keys() toSort = {p: pitch.Pitch(p).midi for p in pitches} sortedPitches = sorted(toSort.items(), key=lambda x: x[1]) xPositions = np.array([p[1] for p in sortedPitches]) xLabels = [p[0] for p in sortedPitches] yValues = np.array([pitchCount[l] for l in xLabels]) # Setting the parameters for plotting yValues, limX, yLabel, col, h = plottingParameters(material, count, yValues) if makePlot: # Start plotting print('Plotting...') # Setting y limits limY = None if count == 'sum': limY = [0, 0.31] plotting(xPositions, xLabels, yValues, limX=limX, xLabel='Pitch', limY=limY, yLabel=yLabel, col=col, h=h, scaleGuides=True, width=0.8) # List to return results = [] for i in range(len(xLabels)): results.append([xLabels[i], yValues[i]]) return results
def findScoreByInterval(material, intvlList, directedInterval=False, silence2ignore=0.25, ignoreGraceNotes=False): ''' ''' for score in material[1:]: showScore = False intvlsFound = {} # Loading the score to get the parts list scorePath = score[0] scoreName = scorePath.split('/')[-1] loadedScore = converter.parse(scorePath) print(scoreName, 'parsed') parts = jS.findVoiceParts(loadedScore) # Work with each part for partIndex in range(1, len(score)): if len(score[partIndex]) == 0: continue # Skip part if it's empty # Get the notes from the current part part = parts[partIndex - 1] notes = part.flat.notesAndRests.stream() # Find segments to analyze in the current part for startEnd in score[partIndex]: start = startEnd[0] end = startEnd[1] segment = notes.getElementsByOffset(start, end) # Count intervals in the current segment # Find the last note that is not a grace note i = 1 lastn = segment[-i] while lastn.quarterLength == 0: i += 1 lastn = segment[-i] for j in range(len(segment) - i): n1 = segment[j] if n1.isRest: continue if ignoreGraceNotes: if n1.quarterLength == 0: continue k = 1 while True: n2 = segment[j + k] if n2.isRest: if n2.quarterLength <= silence2ignore: k += 1 else: n2 = None break elif (n2.quarterLength == 0) and (ignoreGraceNotes == True): j += 1 else: break if n2 == None: continue currentIntvl = interval.Interval(n1, n2) if directedInterval: intvlName = currentIntvl.directedName else: intvlName = currentIntvl.name if intvlName in intvlList: n1.color = 'red' n2.color = 'red' intvlsFound[intvlName] = intvlsFound.get(intvlName, 0) + 1 showScore = True if showScore: for k in intvlsFound: print('\t' + str(intvlsFound[k]), 'samples of', k, 'found in this score') print('\tShowing', scoreName) loadedScore.show() ############################################################################### ############################################################################### ## TO IMPROVE ## ## 1. Keep the banshi information in material, so that the graceNoteDur can ## ## be adjusted accordingly. ## ## 2. Check if the arguments given to collectMaterial make no match at all ## ## and raise a message ## ############################################################################### ###############################################################################