1# -*- coding: utf-8 -*- 2# ------------------------------------------------------------------------------ 3# Name: note.py 4# Purpose: music21 classes for representing notes 5# 6# Authors: Michael Scott Cuthbert 7# Christopher Ariza 8# 9# Copyright: Copyright © 2006-2019 Michael Scott Cuthbert and the music21 Project 10# License: BSD, see license.txt 11# ------------------------------------------------------------------------------ 12''' 13Classes and functions for creating Notes, Rests, and Lyrics. 14 15The :class:`~music21.pitch.Pitch` object is stored within, 16and used to configure, :class:`~music21.note.Note` objects. 17''' 18 19import copy 20import unittest 21 22from typing import Optional, List, Union, Tuple, Iterable 23 24from music21 import base 25from music21 import beam 26from music21 import common 27from music21 import duration 28from music21 import exceptions21 29from music21 import expressions 30from music21 import interval 31from music21 import pitch 32from music21 import prebase 33from music21 import style 34from music21 import tie 35from music21 import volume 36 37from music21 import environment 38environLocal = environment.Environment('note') 39 40noteheadTypeNames = ( 41 'arrow down', 42 'arrow up', 43 'back slashed', 44 'circle dot', 45 'circle-x', 46 'circled', 47 'cluster', 48 'cross', 49 'diamond', 50 'do', 51 'fa', 52 'inverted triangle', 53 'la', 54 'left triangle', 55 'mi', 56 'none', 57 'normal', 58 'other', 59 're', 60 'rectangle', 61 'slash', 62 'slashed', 63 'so', 64 'square', 65 'ti', 66 'triangle', 67 'x', 68) 69 70stemDirectionNames = ( 71 'double', 72 'down', 73 'noStem', 74 'none', 75 'unspecified', 76 'up', 77) 78 79def __dir__(): 80 out = [n for n in globals() if not n.startswith('__') and not n.startswith('Test')] 81 for n in ('Optional', 'List', 'Union', 'Tuple', 'Iterable'): 82 out.remove(n) 83 out.remove('unittest') 84 out.remove('copy') 85 out.remove('_DOC_ORDER') 86 return out 87 88# ----------------------------------------------------------------------------- 89class LyricException(exceptions21.Music21Exception): 90 pass 91 92 93class NoteException(exceptions21.Music21Exception): 94 pass 95 96 97class NotRestException(exceptions21.Music21Exception): 98 pass 99 100 101# ------------------------------------------------------------------------------ 102SYLLABIC_CHOICES: List[Optional[str]] = [ 103 None, 'begin', 'single', 'end', 'middle', 'composite', 104] 105 106 107class Lyric(prebase.ProtoM21Object, style.StyleMixin): 108 ''' 109 An object representing a single Lyric as part of a note's .lyrics property. 110 111 The note.lyric property is a simple way of specifying a single lyric, but 112 Lyric objects are needed for working with multiple lyrics. 113 114 >>> l = note.Lyric(text='hello') 115 >>> l 116 <music21.note.Lyric number=1 syllabic=single text='hello'> 117 118 Music21 processes leading and following hyphens intelligently... 119 120 >>> l2 = note.Lyric(text='hel-') 121 >>> l2 122 <music21.note.Lyric number=1 syllabic=begin text='hel'> 123 124 ...unless applyRaw is set to True 125 126 >>> l3 = note.Lyric(number=3, text='hel-', applyRaw=True) 127 >>> l3 128 <music21.note.Lyric number=3 syllabic=single text='hel-'> 129 130 Lyrics have four properties: text, number, identifier, syllabic (single, 131 begin, middle, end, or (not in musicxml) composite) 132 133 >>> l3.text 134 'hel-' 135 136 >>> l3.number 137 3 138 139 >>> l3.syllabic 140 'single' 141 142 Note musicXML only supports one 'identifier' attribute which is called 143 'number' but which can be a number or a descriptive identifier like 144 'part2verse1.' To preserve lyric ordering, music21 stores a number and a 145 descriptive identifier separately. The descriptive identifier is by default 146 the same as the number, but in cases where a string identifier is present, 147 it will be different. 148 149 Both music21 and musicxml support multiple `Lyric` objects in the same stanza, 150 for instance, if there is an elision on a note then multiple lyrics with 151 different syllabics can appear on a single note. In music21 these are supported 152 by setting .components into a list of `Lyric` object. For instance in 153 the madrigal "Il bianco e dolce cigno", the "co" and "e" of "bianco e" 154 are elided into a single lyric: 155 156 >>> bianco = note.Lyric() 157 >>> co = note.Lyric('co', syllabic='end') 158 >>> e = note.Lyric('e', syllabic='single') 159 >>> bianco.components = [co, e] 160 >>> bianco.isComposite 161 True 162 >>> bianco.text 163 'co e' 164 >>> bianco.syllabic 165 'composite' 166 >>> e.elisionBefore = '_' 167 >>> bianco.text 168 'co_e' 169 170 >>> [component.syllabic for component in bianco.components] 171 ['end', 'single'] 172 173 Custom elision elements for composite components will be supported later. 174 175 New in v6.7 -- composite components, elisionBefore 176 ''' 177 _styleClass = style.TextStylePlacement 178 # CLASS VARIABLES # 179 180 __slots__ = ( 181 '_identifier', 182 '_number', 183 '_syllabic', 184 '_text', 185 'components', 186 'elisionBefore', 187 ) 188 189 # INITIALIZER # 190 191 def __init__(self, text=None, number=1, **kwargs): 192 super().__init__() 193 self._identifier: Optional[str] = None 194 self._number: Optional[int] = None 195 self._text: Optional[str] = None 196 self._syllabic = None 197 self.components: Optional[List['music21.note.Lyric']] = None 198 self.elisionBefore = ' ' 199 200 applyRaw = kwargs.get('applyRaw', False) 201 202 # these are set by setTextAndSyllabic 203 if text is not None: 204 self.setTextAndSyllabic(text, applyRaw) 205 206 # given as begin, middle, end, or single 207 if 'syllabic' in kwargs: 208 self.syllabic = kwargs['syllabic'] 209 210 self.number = number 211 self.identifier = kwargs.get('identifier', None) 212 213 # PRIVATE METHODS # 214 215 def _reprInternal(self): 216 out = '' 217 if self.number is not None: 218 out += f'number={self.number} ' 219 if self._identifier is not None: 220 out += f'identifier={self.identifier!r} ' 221 if self.syllabic is not None: 222 out += f'syllabic={self.syllabic} ' 223 if self.text is not None: 224 out += f'text={self.text!r} ' 225 return out 226 227 # PUBLIC PROPERTIES # 228 @property 229 def isComposite(self) -> bool: 230 ''' 231 Returns True if this Lyric has composite elements, 232 for instance, is multiple lyrics placed together. 233 ''' 234 return bool(self.components) 235 236 @property 237 def text(self) -> Optional[str]: 238 ''' 239 Gets or sets the text of the lyric. For composite lyrics, set 240 the text of individual components instead of setting the text here. 241 242 Setting the text of a composite lyric wipes out the components 243 ''' 244 if not self.isComposite: 245 return self._text 246 else: 247 text_out = self.components[0].text 248 if text_out is None: 249 text_out = '' 250 for component in self.components[1:]: 251 componentText = component.text if component.text is not None else '' 252 text_out += component.elisionBefore + componentText 253 return text_out 254 255 @text.setter 256 def text(self, newText: Optional[str]): 257 if self.isComposite: 258 self.components = None 259 self._text = newText 260 261 @property 262 def syllabic(self) -> Optional[str]: 263 ''' 264 Returns or sets the syllabic property of a lyric. 265 266 >>> fragment = note.Lyric('frag', syllabic='begin') 267 >>> fragment.syllabic 268 'begin' 269 >>> fragment.rawText 270 'frag-' 271 >>> fragment.syllabic = 'end' 272 >>> fragment.rawText 273 '-frag' 274 275 Illegal values raise a LyricException 276 277 >>> fragment.syllabic = 'slide' 278 Traceback (most recent call last): 279 music21.note.LyricException: Syllabic value 'slide' is not in 280 note.SYLLABIC_CHOICES, namely: 281 [None, 'begin', 'single', 'end', 'middle', 'composite'] 282 ''' 283 if self.isComposite: 284 return 'composite' 285 else: 286 return self._syllabic 287 288 289 @syllabic.setter 290 def syllabic(self, newSyllabic): 291 if newSyllabic not in SYLLABIC_CHOICES: 292 raise LyricException( 293 f'Syllabic value {newSyllabic!r} is not in ' 294 + f'note.SYLLABIC_CHOICES, namely: {SYLLABIC_CHOICES}' 295 ) 296 self._syllabic = newSyllabic 297 298 @property 299 def identifier(self) -> str: 300 ''' 301 By default, this is the same as self.number. However, if there is a 302 descriptive identifier like 'part2verse1', it is stored here and 303 will be different from self.number. When converting to musicXML, 304 this property will be stored in the lyric 'number' attribute which 305 can store a number or a descriptive identifier but not both. 306 307 >>> l = note.Lyric() 308 >>> l.number = 12 309 >>> l.identifier 310 12 311 312 >>> l.identifier = 'Rainbow' 313 >>> l.identifier 314 'Rainbow' 315 ''' 316 if self._identifier is None: 317 return self._number 318 else: 319 return self._identifier 320 321 @identifier.setter 322 def identifier(self, value: str): 323 self._identifier = value 324 325 @property 326 def rawText(self) -> str: 327 ''' 328 returns the text of the syllable with '-' etc. 329 330 >>> l = note.Lyric('hel-') 331 >>> l.text 332 'hel' 333 >>> l.rawText 334 'hel-' 335 336 >>> l = note.Lyric('lo', syllabic='end') 337 >>> l.rawText 338 '-lo' 339 340 >>> l = note.Lyric('-ti-') 341 >>> l.rawText 342 '-ti-' 343 344 >>> l = note.Lyric('bye') 345 >>> l.rawText 346 'bye' 347 348 Composite lyrics take their endings from the first and last components: 349 350 >>> composite = note.Lyric() 351 >>> co = note.Lyric('co', syllabic='end') 352 >>> e = note.Lyric('e', syllabic='single') 353 >>> e.elisionBefore = '_' 354 >>> composite.components = [co, e] 355 >>> composite.rawText 356 '-co_e' 357 >>> e.syllabic = 'middle' 358 >>> composite.rawText 359 '-co_e-' 360 ''' 361 text = self.text 362 if not self.isComposite: 363 syllabic = self.syllabic 364 if syllabic == 'begin': 365 return text + '-' 366 elif syllabic == 'middle': 367 return '-' + text + '-' 368 elif syllabic == 'end': 369 return '-' + text 370 else: 371 return text 372 else: 373 firstSyllabic = self.components[0].syllabic 374 lastSyllabic = self.components[-1].syllabic 375 if firstSyllabic in ['middle', 'end']: 376 text = '-' + text 377 if lastSyllabic in ['begin', 'middle']: 378 text += '-' 379 return text 380 381 382 @rawText.setter 383 def rawText(self, t): 384 self.setTextAndSyllabic(t, applyRaw=True) 385 386 @property 387 def number(self) -> int: 388 ''' 389 This stores the number of the lyric (which determines the order 390 lyrics appear in the score if there are multiple lyrics). Unlike 391 the musicXML lyric number attribute, this value must always be a 392 number; lyric order is always stored in this form. Descriptive 393 identifiers like 'part2verse1' which can be found in the musicXML 394 lyric number attribute should be stored in self.identifier. 395 396 >>> l = note.Lyric('Hi') 397 >>> l.number = 5 398 >>> l.number 399 5 400 >>> l.number = None 401 Traceback (most recent call last): 402 music21.note.LyricException: Number best be number 403 ''' 404 return self._number 405 406 @number.setter 407 def number(self, value: int) -> None: 408 if not common.isNum(value): 409 raise LyricException('Number best be number') 410 self._number = value 411 412 # PUBLIC METHODS # 413 def setTextAndSyllabic(self, rawText: str, applyRaw: bool = False) -> None: 414 ''' 415 Given a setting for rawText and applyRaw, 416 sets the syllabic type for a lyric based on the rawText. Useful for 417 parsing raw text from, say, an OMR score. Or just to quickly set text 418 and syllabic. 419 420 >>> l = note.Lyric() 421 >>> l.setTextAndSyllabic('hel-') 422 >>> l.text 423 'hel' 424 >>> l.syllabic 425 'begin' 426 >>> l.setTextAndSyllabic('-lo') 427 >>> l.text 428 'lo' 429 >>> l.syllabic 430 'end' 431 >>> l.setTextAndSyllabic('the') 432 >>> l.text 433 'the' 434 >>> l.syllabic 435 'single' 436 437 If applyRaw is True then this will assume you actually want hyphens 438 in the text, and if syllabic is None, sets it to 'single' 439 440 >>> l = note.Lyric() 441 >>> l.setTextAndSyllabic('hel-', applyRaw=True) 442 >>> l.text 443 'hel-' 444 >>> l.syllabic 445 'single' 446 447 If applyRaw is True, other syllabic settings except None are retained 448 449 >>> l.syllabic = 'begin' 450 >>> l.setTextAndSyllabic('-lo', applyRaw=True) 451 >>> l.text 452 '-lo' 453 >>> l.syllabic 454 'begin' 455 456 This method wipes out components. 457 ''' 458 # do not want to do this unless we are sure this is not a string 459 # possible might alter unicode or other string-like representations 460 if not isinstance(rawText, str): 461 rawText = str(rawText) 462 463 # check for hyphens 464 if applyRaw is False and rawText.startswith('-') and not rawText.endswith('-'): 465 self.text = rawText[1:] 466 self.syllabic = 'end' 467 elif applyRaw is False and not rawText.startswith('-') and rawText.endswith('-'): 468 self.text = rawText[:-1] 469 self.syllabic = 'begin' 470 elif applyRaw is False and rawText.startswith('-') and rawText.endswith('-'): 471 self.text = rawText[1:-1] 472 self.syllabic = 'middle' 473 else: # assume single 474 self.text = rawText 475 if self.syllabic is None or not applyRaw: 476 self.syllabic = 'single' 477 478 479# ------------------------------------------------------------------------------ 480 481 482class GeneralNote(base.Music21Object): 483 ''' 484 A GeneralNote object is the base class object 485 for the :class:`~music21.note.Note`, 486 :class:`~music21.note.Rest`, :class:`~music21.chord.Chord`, 487 and related objects. 488 489 Keywords can be passed to 490 a GeneralNote which are then passed to the 491 underlying :class:`~music21.duration.Duration`. 492 These keywords might be listed like 493 type='16th', dots=2 etc. to create a 494 double-dotted sixteenth note. 495 496 In almost every circumstance, you should 497 create note.Note() or note.Rest() or chord.Chord() 498 objects directly, and not use this underlying 499 structure. 500 501 502 >>> gn = note.GeneralNote(type='16th', dots=2) 503 >>> gn.quarterLength 504 0.4375 505 ''' 506 isNote = False 507 isRest = False 508 isChord = False 509 _styleClass = style.NoteStyle 510 511 # define order to present names in documentation; use strings 512 _DOC_ORDER = ['duration', 'quarterLength'] 513 # documentation for all attributes (not properties or methods) 514 _DOC_ATTR = { 515 'isChord': 'Boolean read-only value describing if this object is a Chord.', 516 'lyrics': 'A list of :class:`~music21.note.Lyric` objects.', 517 'tie': 'either None or a :class:`~music21.note.Tie` object.', 518 'expressions': '''a list of expressions (such 519 as :class:`~music21.expressions.Fermata`, etc.) 520 that are stored on this Note.''', 521 'articulations': '''a list of articulations such 522 as :class:`~music21.articulations.Staccato`, etc.) that are stored on this Note.''' 523 } 524 525 def __init__(self, *arguments, **keywords): 526 if 'duration' not in keywords: 527 # ensure music21base not automatically create a duration. 528 if not keywords: 529 tempDuration = duration.Duration(1.0) 530 else: 531 tempDuration = duration.Duration(**keywords) 532 # only apply default if components are empty 533 # looking at currentComponents so as not to trigger 534 # _updateComponents 535 if (tempDuration.quarterLength == 0 536 and not tempDuration.currentComponents()): 537 tempDuration.quarterLength = 1.0 538 else: 539 tempDuration = keywords['duration'] 540 # this sets the stored duration defined in Music21Object 541 super().__init__(duration=tempDuration) 542 543 self.lyrics: List[Lyric] = [] # a list of lyric objects 544 self.expressions = [] 545 self.articulations = [] 546 547 if 'lyric' in keywords and keywords['lyric'] is not None: 548 self.addLyric(keywords['lyric']) 549 550 # note: Chords handle ties differently 551 self.tie = None # store a Tie object 552 553 def __eq__(self, other): 554 ''' 555 General Note objects are equal if their durations are equal and 556 they have the same articulation and expression classes (in any order) 557 and their ties are equal. 558 ''' 559 560 if other is None or not isinstance(other, GeneralNote): 561 return NotImplemented 562 # checks type, dots, tuplets, quarterLength, uses Pitch.__eq__ 563 if self.duration != other.duration: 564 return False 565 # articulations are a list of Articulation objects 566 # converting to sets produces ordered cols that remove duplicate 567 # however, must then convert to list to match based on class == 568 # not on class id() 569 if (sorted({x.classes[0] for x in self.articulations}) 570 != sorted({x.classes[0] for x in other.articulations})): 571 return False 572 if (sorted({x.classes[0] for x in self.expressions}) 573 != sorted({x.classes[0] for x in other.expressions})): 574 return False 575 576 # Tie objects if present compare only type 577 if self.tie != other.tie: 578 return False 579 return True 580 581 # -------------------------------------------------------------------------- 582 def _getLyric(self) -> Optional[str]: 583 if not self.lyrics: 584 return None 585 586 allText = [ly.text for ly in self.lyrics] 587 return '\n'.join([t for t in allText if t is not None]) 588 589 def _setLyric(self, value: Union[str, Lyric, None]) -> None: 590 self.lyrics = [] 591 if value is None: 592 return 593 594 if isinstance(value, Lyric): 595 self.lyrics.append(value) 596 return 597 598 if not isinstance(value, str): 599 value = str(value) 600 601 values = value.split('\n') 602 for i, v in enumerate(values): 603 self.lyrics.append(Lyric(v, number=i + 1)) 604 605 lyric = property(_getLyric, 606 _setLyric, 607 doc=r''' 608 The lyric property can 609 be used to get and set a lyric for this 610 Note, Chord, or Rest. This is a simplified version of the more general 611 :meth:`~music21.note.GeneralNote.addLyric` method. 612 613 >>> a = note.Note('A4') 614 >>> a.lyrics 615 [] 616 >>> a.lyric = 'hel-' 617 >>> a.lyric 618 'hel' 619 >>> a.lyrics 620 [<music21.note.Lyric number=1 syllabic=begin text='hel'>] 621 622 Eliminate Lyrics by setting a.lyric to None 623 624 >>> a.lyric = None 625 >>> a.lyric 626 >>> a.lyrics 627 [] 628 629 Set multiple lyrics with \n separated text: 630 631 >>> a.lyric = '1. Hi\n2. Bye' 632 >>> a.lyric 633 '1. Hi\n2. Bye' 634 >>> a.lyrics 635 [<music21.note.Lyric number=1 syllabic=single text='1. Hi'>, 636 <music21.note.Lyric number=2 syllabic=single text='2. Bye'>] 637 638 639 You can also set a lyric with a lyric object directly: 640 641 >>> b = note.Note('B5') 642 >>> ly = note.Lyric('bon-') 643 >>> b.lyric = ly 644 >>> b.lyrics 645 [<music21.note.Lyric number=1 syllabic=begin text='bon'>] 646 >>> b.lyric 647 'bon' 648 649 Changed in v6.7 -- added setting to a Lyric object. Removed undocumented 650 setting to False instead of setting to None 651 ''') 652 653 def addLyric(self, 654 text, 655 lyricNumber=None, 656 *, 657 applyRaw=False, 658 lyricIdentifier=None) -> None: 659 ''' 660 Adds a lyric, or an additional lyric, to a Note, Chord, or Rest's lyric list. 661 If `lyricNumber` is not None, a specific line of lyric text can be set. 662 The lyricIdentifier can also be set. 663 664 >>> n1 = note.Note() 665 >>> n1.addLyric('hello') 666 >>> n1.lyrics[0].text 667 'hello' 668 >>> n1.lyrics[0].number 669 1 670 671 An added option gives the lyric number, not the list position 672 673 >>> n1.addLyric('bye', 3) 674 >>> n1.lyrics[1].text 675 'bye' 676 >>> n1.lyrics[1].number 677 3 678 >>> for lyr in n1.lyrics: 679 ... print(lyr.text) 680 hello 681 bye 682 683 Replace an existing lyric by specifying the same number: 684 685 >>> n1.addLyric('ciao', 3) 686 >>> n1.lyrics[1].text 687 'ciao' 688 >>> n1.lyrics[1].number 689 3 690 691 Giving a lyric with a hyphen at either end will set whether it 692 is part of a multisyllable word: 693 694 >>> n1.addLyric('good-') 695 >>> n1.lyrics[2].text 696 'good' 697 >>> n1.lyrics[2].syllabic 698 'begin' 699 700 This feature can be overridden by specifying the keyword only argument "applyRaw=True": 701 702 >>> n1.addLyric('-5', applyRaw=True) 703 >>> n1.lyrics[3].text 704 '-5' 705 >>> n1.lyrics[3].syllabic 706 'single' 707 ''' 708 if not isinstance(text, str): 709 text = str(text) 710 if lyricNumber is None: 711 maxLyrics = len(self.lyrics) + 1 712 self.lyrics.append(Lyric(text, maxLyrics, 713 applyRaw=applyRaw, identifier=lyricIdentifier)) 714 else: 715 foundLyric = False 716 for thisLyric in self.lyrics: 717 if thisLyric.number == lyricNumber: 718 thisLyric.text = text 719 foundLyric = True 720 break 721 if foundLyric is False: 722 self.lyrics.append(Lyric(text, lyricNumber, 723 applyRaw=applyRaw, identifier=lyricIdentifier)) 724 725 def insertLyric(self, text, index=0, *, applyRaw=False, identifier=None): 726 ''' 727 Inserts a lyric into the Note, Chord, or Rest's lyric list in front of 728 the index specified (0 by default), using index + 1 as the inserted lyric's 729 line number. shifts line numbers of all following lyrics in list 730 731 >>> n1 = note.Note() 732 >>> n1.addLyric('second') 733 >>> n1.lyrics 734 [<music21.note.Lyric number=1 syllabic=single text='second'>] 735 >>> n1.insertLyric('first', 0) 736 >>> n1.lyrics 737 [<music21.note.Lyric number=1 syllabic=single text='first'>, 738 <music21.note.Lyric number=2 syllabic=single text='second'>] 739 740 OMIT_FROM_DOCS 741 742 test inserting in the middle. 743 744 >>> n1.insertLyric('newSecond', 1) 745 >>> n1.lyrics 746 [<music21.note.Lyric number=1 syllabic=single text='first'>, 747 <music21.note.Lyric number=2 syllabic=single text='newSecond'>, 748 <music21.note.Lyric number=3 syllabic=single text='second'>] 749 750 Test number as lyric... 751 752 >>> n1.insertLyric(0, 3) 753 >>> n1.lyrics 754 [<music21.note.Lyric number=1 syllabic=single text='first'>, 755 <music21.note.Lyric number=2 syllabic=single text='newSecond'>, 756 <music21.note.Lyric number=3 syllabic=single text='second'>, 757 <music21.note.Lyric number=4 syllabic=single text='0'>] 758 ''' 759 if not isinstance(text, str): 760 text = str(text) 761 for lyric in self.lyrics[index:]: 762 lyric.number += 1 763 self.lyrics.insert(index, Lyric(text, (index + 1), 764 applyRaw=applyRaw, identifier=identifier)) 765 766 # -------------------------------------------------------------------------- 767 # properties common to Notes, Rests, 768 769 # -------------------------------------------------------------------------- 770 def augmentOrDiminish(self, scalar, *, inPlace=False): 771 ''' 772 Given a scalar greater than zero, return a Note with a scaled Duration. 773 If `inPlace` is True, this is done in-place and the method returns None. 774 If `inPlace` is False [default], this returns a modified deepcopy. 775 776 Changed -- inPlace is now False as of version 5. 777 778 >>> n = note.Note('g#') 779 >>> n.quarterLength = 3 780 >>> n.augmentOrDiminish(2, inPlace=True) 781 >>> n.quarterLength 782 6.0 783 784 >>> c = chord.Chord(['g#', 'a#', 'd']) 785 >>> c.quarterLength = 2 786 >>> c.augmentOrDiminish(0.25, inPlace=True) 787 >>> c.quarterLength 788 0.5 789 790 >>> n = note.Note('g#') 791 >>> n.augmentOrDiminish(-1) 792 Traceback (most recent call last): 793 music21.note.NoteException: scalar must be greater than zero 794 795 >>> n = note.Note() 796 >>> n.quarterLength = 3 797 >>> n2 = n.augmentOrDiminish(1/3, inPlace=False) 798 >>> n2.quarterLength 799 1.0 800 >>> n.quarterLength 801 3.0 802 ''' 803 if not scalar > 0: 804 raise NoteException('scalar must be greater than zero') 805 806 if inPlace: 807 post = self 808 else: # slight speedup could happen by setting duration to Zero before copying. 809 post = copy.deepcopy(self) 810 811 # this is never True. 812 post.duration = post.duration.augmentOrDiminish(scalar) 813 814 if not inPlace: 815 return post 816 else: 817 return None 818 819 # -------------------------------------------------------------------------- 820 def getGrace(self, *, appoggiatura=False, inPlace=False): 821 ''' 822 Return a grace version of this GeneralNote 823 824 >>> n = note.Note('G4', quarterLength=2) 825 >>> n.duration.quarterLength 826 2.0 827 >>> n.duration.isGrace 828 False 829 >>> n.duration 830 <music21.duration.Duration 2.0> 831 >>> n.duration.type 832 'half' 833 >>> n.duration.components 834 (DurationTuple(type='half', dots=0, quarterLength=2.0),) 835 836 >>> ng = n.getGrace() 837 >>> ng.duration.quarterLength 838 0.0 839 >>> ng.duration.isGrace 840 True 841 >>> ng.duration 842 <music21.duration.GraceDuration unlinked type:half quarterLength:0.0> 843 >>> ng.duration.type 844 'half' 845 >>> ng.duration.components 846 (DurationTuple(type='half', dots=0, quarterLength=0.0),) 847 848 Appoggiaturas are still a work in progress... 849 Changed in v.6 -- corrected spelling of `appoggiatura` keyword. 850 851 >>> ng2 = n.getGrace(appoggiatura=True) 852 >>> ng2.duration 853 <music21.duration.AppoggiaturaDuration unlinked type:half quarterLength:0.0> 854 >>> ng2.duration.slash 855 False 856 857 Set inPlace to True to change the duration element on the Note. This can have 858 negative consequences if the Note is in a stream. 859 860 >>> r = note.Rest(quarterLength=0.5) 861 >>> r.getGrace(inPlace=True) 862 >>> r.duration 863 <music21.duration.GraceDuration unlinked type:eighth quarterLength:0.0> 864 ''' 865 if inPlace is False: 866 e = copy.deepcopy(self) 867 else: 868 e = self 869 870 e.duration = e.duration.getGraceDuration(appoggiatura=appoggiatura) 871 872 if inPlace is False: 873 return e 874 875 876# ------------------------------------------------------------------------------ 877class NotRest(GeneralNote): 878 ''' 879 Parent class for Note-like objects that are not rests; that is to say 880 they have a stem, can be tied, and volume is important. 881 Basically, that's a :class:`Note` or :class:`~music21.chord.Chord` 882 (or their subclasses such as :class:`~music21.harmony.ChordSymbol`), or 883 :class:`Unpitched` object. 884 ''' 885 # unspecified means that there may be a stem, but its orientation 886 # has not been declared. 887 888 _DOC_ATTR = { 889 'beams': ''' 890 A :class:`~music21.beam.Beams` object that contains 891 information about the beaming of this note.''', 892 } 893 894 def __init__(self, *arguments, **keywords): 895 super().__init__(**keywords) 896 self._notehead = 'normal' 897 self._noteheadFill = None 898 self._noteheadParenthesis = False 899 self._stemDirection = 'unspecified' 900 self._volume = None # created on demand 901 # replace 902 self.linkage = 'tie' 903 if 'beams' in keywords: 904 self.beams = keywords['beams'] 905 else: 906 self.beams = beam.Beams() 907 self._storedInstrument: Optional['music21.instrument.Instrument'] = None 908 909 # ============================================================================================== 910 # Special functions 911 # ============================================================================================== 912 def __eq__(self, other): 913 if super().__eq__(other) is NotImplemented: 914 return NotImplemented 915 if not super().__eq__(other): 916 return False 917 if not isinstance(other, NotRest): 918 return False 919 920 if self.notehead != other.notehead: 921 return False 922 if self.noteheadFill != other.noteheadFill: 923 return False 924 if self.noteheadParenthesis != other.noteheadParenthesis: 925 return False 926 # Q: should volume need to be equal? 927 if self.beams != other.beams: 928 return False 929 return True 930 931 def __deepcopy__(self, memo=None): 932 ''' 933 As NotRest objects have a Volume, objects, and Volume objects 934 store weak refs to the to client object, need to specialize deep copy handling 935 936 >>> import copy 937 >>> n = note.NotRest() 938 >>> n.volume = volume.Volume(50) 939 >>> m = copy.deepcopy(n) 940 >>> m.volume.client is m 941 True 942 ''' 943 # environLocal.printDebug(['calling NotRest.__deepcopy__', self]) 944 new = super().__deepcopy__(memo=memo) 945 # after copying, if a Volume exists, it is linked to the old object 946 # look at _volume so as not to create object if not already there 947 if self._volume is not None: 948 new.volume.client = new # update with new instance 949 return new 950 951 def __getstate__(self): 952 state = super().__getstate__() 953 if '_volume' in state and state['_volume'] is not None: 954 state['_volume'].client = None 955 return state 956 957 def __setstate__(self, state): 958 super().__setstate__(state) 959 if self._volume is not None: 960 self._volume.client = self 961 #### 962 963 def _getStemDirection(self) -> str: 964 return self._stemDirection 965 966 def _setStemDirection(self, direction): 967 if direction is None: 968 direction = 'unspecified' # allow setting to None meaning 969 elif direction == 'none': 970 direction = 'noStem' # allow setting to none or None 971 elif direction not in stemDirectionNames: 972 raise NotRestException(f'not a valid stem direction name: {direction}') 973 self._stemDirection = direction 974 975 stemDirection = property(_getStemDirection, 976 _setStemDirection, 977 doc=''' 978 Get or set the stem direction of this NotRest object. 979 Valid stem direction names are found in note.stemDirectionNames (see below). 980 981 >>> note.stemDirectionNames 982 ('double', 'down', 'noStem', 'none', 'unspecified', 'up') 983 >>> n = note.Note() 984 985 By default a Note's stemDirection is 'unspecified' 986 meaning that it is unknown: 987 988 >>> n.stemDirection 989 'unspecified' 990 991 >>> n.stemDirection = 'noStem' 992 >>> n.stemDirection 993 'noStem' 994 995 The alias 'none' (the string) is the same as 'noStem' 996 997 >>> n.stemDirection = 'none' 998 >>> n.stemDirection 999 'noStem' 1000 1001 >>> n.stemDirection = 'junk' 1002 Traceback (most recent call last): 1003 music21.note.NotRestException: not a valid stem direction name: junk 1004 1005 Stem direction can be set explicitly to None to remove 1006 any prior stem information, same as 'unspecified': 1007 1008 >>> n.stemDirection = None 1009 >>> n.stemDirection 1010 'unspecified' 1011 ''') 1012 1013 @property 1014 def notehead(self) -> str: 1015 ''' 1016 Get or set the notehead type of this NotRest object. 1017 Valid notehead type names are found in note.noteheadTypeNames (see below): 1018 1019 1020 >>> note.noteheadTypeNames 1021 ('arrow down', 'arrow up', 'back slashed', 'circle dot', 'circle-x', 'circled', 'cluster', 1022 'cross', 'diamond', 'do', 'fa', 'inverted triangle', 'la', 'left triangle', 1023 'mi', 'none', 'normal', 'other', 're', 'rectangle', 'slash', 'slashed', 'so', 1024 'square', 'ti', 'triangle', 'x') 1025 >>> n = note.Note() 1026 >>> n.notehead = 'diamond' 1027 >>> n.notehead 1028 'diamond' 1029 1030 >>> n.notehead = 'junk' 1031 Traceback (most recent call last): 1032 music21.note.NotRestException: not a valid notehead type name: 'junk' 1033 ''' 1034 return self._notehead 1035 1036 @notehead.setter 1037 def notehead(self, value): 1038 if value in ('none', None, ''): 1039 value = None # allow setting to none or None 1040 elif value not in noteheadTypeNames: 1041 raise NotRestException(f'not a valid notehead type name: {value!r}') 1042 self._notehead = value 1043 1044 @property 1045 def noteheadFill(self) -> str: 1046 ''' 1047 Get or set the note head fill status of this NotRest. Valid note head fill values are 1048 True, False, or None (meaning default). "yes" and "no" are converted to True 1049 and False. 1050 1051 >>> n = note.Note() 1052 >>> n.noteheadFill = 'no' 1053 >>> n.noteheadFill 1054 False 1055 >>> n.noteheadFill = 'filled' 1056 >>> n.noteheadFill 1057 True 1058 1059 >>> n.noteheadFill = 'jelly' 1060 Traceback (most recent call last): 1061 music21.note.NotRestException: not a valid notehead fill value: jelly 1062 ''' 1063 return self._noteheadFill 1064 1065 @noteheadFill.setter 1066 def noteheadFill(self, value): 1067 if value in ('none', None, 'default'): 1068 value = None # allow setting to none or None 1069 if value in ('filled', 'yes'): 1070 value = True 1071 elif value in ('notfilled', 'no'): 1072 value = False 1073 if value not in (True, False, None): 1074 raise NotRestException(f'not a valid notehead fill value: {value}') 1075 self._noteheadFill = value 1076 1077 @property 1078 def noteheadParenthesis(self) -> bool: 1079 ''' 1080 Get or set the note head parentheses for this Note/Unpitched/Chord object. 1081 1082 >>> n = note.Note() 1083 >>> n.noteheadParenthesis 1084 False 1085 >>> n.noteheadParenthesis = True 1086 >>> n.noteheadParenthesis 1087 True 1088 1089 'yes' or 1 equate to True; 'no' or 0 to False 1090 1091 >>> n.noteheadParenthesis = 'no' 1092 >>> n.noteheadParenthesis 1093 False 1094 1095 Anything else raises an exception: 1096 1097 >>> n.noteheadParenthesis = 'blah' 1098 Traceback (most recent call last): 1099 music21.note.NotRestException: notehead parentheses must be True or False, not 'blah' 1100 ''' 1101 return self._noteheadParenthesis 1102 1103 @noteheadParenthesis.setter 1104 def noteheadParenthesis(self, value): 1105 if value in (True, 'yes', 1): 1106 value = True 1107 elif value in (False, 'no', 0): 1108 value = False 1109 else: 1110 raise NotRestException(f'notehead parentheses must be True or False, not {value!r}') 1111 self._noteheadParenthesis = value 1112 1113 # -------------------------------------------------------------------------- 1114 def hasVolumeInformation(self) -> bool: 1115 ''' 1116 Returns bool whether volume was set -- saving some time for advanced 1117 users (such as MusicXML exporters) that only want to look at the volume 1118 if it is already there. 1119 1120 >>> n = note.Note() 1121 >>> n.hasVolumeInformation() 1122 False 1123 >>> n.volume 1124 <music21.volume.Volume realized=0.71> 1125 >>> n.hasVolumeInformation() 1126 True 1127 ''' 1128 if self._volume is None: 1129 return False 1130 else: 1131 return True 1132 1133 @property 1134 def pitches(self) -> Tuple[pitch.Pitch]: 1135 ''' 1136 Returns an empty tuple. (Useful for iterating over NotRests since they 1137 include Notes and Chords.) 1138 ''' 1139 return () 1140 1141 @pitches.setter 1142 def pitches(self, _value: Iterable[pitch.Pitch]): 1143 pass 1144 1145 1146 def _getVolume(self, forceClient: Optional[base.Music21Object] = None) -> volume.Volume: 1147 # DO NOT CHANGE TO @property because of optional attributes 1148 # lazy volume creation 1149 if self._volume is None: 1150 if forceClient is None: 1151 # when creating the volume object, set the client as self 1152 self._volume = volume.Volume(client=self) 1153 else: 1154 self._volume = volume.Volume(client=forceClient) 1155 return self._volume 1156 1157 def _setVolume(self, value, setClient=True): 1158 # DO NOT CHANGE TO @property because of optional attributes 1159 # setParent is only False when Chords bundling Notes 1160 # test by looking for method 1161 if value is None: 1162 self._volume = None 1163 elif hasattr(value, 'getDynamicContext'): 1164 if setClient: 1165 if value.client is not None: 1166 value = copy.deepcopy(value) 1167 value.client = self # set to self 1168 self._volume = value 1169 elif common.isNum(value) and setClient: 1170 # if we can define the client, we can set from numbers 1171 # call local getVolume will set client appropriately 1172 vol = self._getVolume() 1173 if value < 1: # assume a scalar 1174 vol.velocityScalar = value 1175 else: # assume velocity 1176 vol.velocity = value 1177 1178 else: 1179 raise Exception(f'this must be a Volume object, not {value}') 1180 1181 volume = property(_getVolume, 1182 _setVolume, 1183 doc=''' 1184 Get and set the :class:`~music21.volume.Volume` object of this object. 1185 Volume objects are created on demand. 1186 1187 >>> n1 = note.Note() 1188 >>> n1.volume.velocity = 120 1189 >>> n2 = note.Note() 1190 >>> n2.volume = 80 # can directly set a velocity value 1191 >>> s = stream.Stream() 1192 >>> s.append([n1, n2]) 1193 >>> [n.volume.velocity for n in s.notes] 1194 [120, 80] 1195 ''') 1196 1197 def _getStoredInstrument(self): 1198 return self._storedInstrument 1199 1200 def _setStoredInstrument(self, newValue): 1201 if not (hasattr(newValue, 'instrumentId') or newValue is None): 1202 raise TypeError(f'Expected Instrument; got {type(newValue)}') 1203 self._storedInstrument = newValue 1204 1205 storedInstrument = property(_getStoredInstrument, 1206 _setStoredInstrument, 1207 doc=''' 1208 Get and set the :class:`~music21.instrument.Instrument` that 1209 should be used to play this note, overriding whatever 1210 Instrument object may be active in the Stream. (See 1211 :meth:`getInstrument` for a means of retrieving `storedInstrument` 1212 if available before falling back to a context search to find 1213 the active instrument.) 1214 ''') 1215 1216 def getInstrument(self, 1217 *, 1218 returnDefault: bool = True 1219 ) -> Optional['music21.instrument.Instrument']: 1220 ''' 1221 Retrieves the `.storedInstrument` on this `NotRest` instance, if any. 1222 If one is not found, executes a context search (without following 1223 derivations) to find the closest (i.e., active) instrument in the 1224 stream hierarchy. 1225 1226 Returns a default instrument if no instrument is found in the context 1227 and `returnDefault` is True (default). 1228 1229 >>> n = note.Note() 1230 >>> m = stream.Measure([n]) 1231 >>> n.getInstrument(returnDefault=False) is None 1232 True 1233 >>> dulc = instrument.Dulcimer() 1234 >>> m.insert(0, dulc) 1235 >>> n.getInstrument() is dulc 1236 True 1237 1238 Overridden `.storedInstrument` is privileged: 1239 1240 >>> picc = instrument.Piccolo() 1241 >>> n.storedInstrument = picc 1242 >>> n.getInstrument() is picc 1243 True 1244 1245 Instruments in containing streams ARE found: 1246 1247 >>> n.storedInstrument = None 1248 >>> m.remove(dulc) 1249 >>> p = stream.Part([m]) 1250 >>> p.insert(0, dulc) 1251 >>> n.getInstrument() is dulc 1252 True 1253 1254 But not if the instrument is only found in a derived stream: 1255 1256 >>> derived = p.stripTies() 1257 >>> p.remove(dulc) 1258 >>> derived.getInstruments().first() 1259 <music21.instrument.Dulcimer 'Dulcimer'> 1260 >>> n.getInstrument(returnDefault=False) is None 1261 True 1262 1263 Electing to return a default generic `Instrument`: 1264 1265 >>> n.getInstrument(returnDefault=True) 1266 <music21.instrument.Instrument ''> 1267 ''' 1268 from music21 import instrument 1269 if self.storedInstrument is not None: 1270 return self.storedInstrument 1271 instrument_or_none = self.getContextByClass( 1272 instrument.Instrument, followDerivation=False) 1273 if returnDefault and instrument_or_none is None: 1274 return instrument.Instrument() 1275 return instrument_or_none 1276 1277 1278# ------------------------------------------------------------------------------ 1279class Note(NotRest): 1280 ''' 1281 One of the most important music21 classes, a Note 1282 stores a single note (that is, not a rest or an unpitched element) 1283 that can be represented by one or more notational units -- so 1284 for instance a C quarter-note and a D# eighth-tied-to-32nd are both 1285 a single Note object. 1286 1287 1288 A Note knows both its total duration and how to express itself as a set of 1289 tied notes of different lengths. For instance, a note of 2.5 quarters in 1290 length could be half tied to eighth or dotted quarter tied to quarter. 1291 1292 1293 The first argument to the Note is the pitch name (with or without 1294 octave, see the introduction to :class:`music21.pitch.Pitch`). 1295 Further arguments can be specified as keywords (such as type, dots, etc.) 1296 and are passed to the underlying :class:`music21.duration.Duration` element. 1297 1298 >>> n = note.Note() 1299 >>> n 1300 <music21.note.Note C> 1301 >>> n.pitch 1302 <music21.pitch.Pitch C4> 1303 1304 >>> n = note.Note('B-') 1305 >>> n.name 1306 'B-' 1307 >>> n.octave is None 1308 True 1309 >>> n.pitch.implicitOctave 1310 4 1311 1312 >>> n = note.Note(name='D#') 1313 >>> n.name 1314 'D#' 1315 >>> n = note.Note(nameWithOctave='D#5') 1316 >>> n.nameWithOctave 1317 'D#5' 1318 1319 Other ways of instantiating a Pitch object, such as by MIDI number or pitch class 1320 are also possible: 1321 1322 >>> note.Note(64).nameWithOctave 1323 'E4' 1324 1325 Two notes are considered equal if their most important attributes 1326 (such as pitch, duration, 1327 articulations, and ornaments) are equal. Attributes 1328 that might change based on the wider context 1329 of a note (such as offset) 1330 are not compared. This test does not look at lyrics in 1331 establishing equality. (It may in the future.) 1332 1333 >>> note.Note('C4') == note.Note('C4') 1334 True 1335 ''' 1336 isNote = True 1337 1338 # define order to present names in documentation; use strings 1339 _DOC_ORDER = ['duration', 'quarterLength', 'nameWithOctave'] 1340 # documentation for all attributes (not properties or methods) 1341 _DOC_ATTR = { 1342 'isNote': 'Boolean read-only value describing if this Note is a Note (True).', 1343 'isRest': 'Boolean read-only value describing if this Note is a Rest (False).', 1344 'pitch': '''A :class:`~music21.pitch.Pitch` object containing all the 1345 information about the note's pitch. Many `.pitch` properties and 1346 methods are also made `Note` properties also''', 1347 } 1348 1349 # Accepts an argument for pitch 1350 def __init__(self, pitchName=None, **keywords): 1351 super().__init__(**keywords) 1352 self._chordAttached: Optional['music21.chord.Chord'] = None 1353 1354 if 'pitch' in keywords and pitchName is None: 1355 pitchName = keywords['pitch'] 1356 del keywords['pitch'] 1357 1358 if pitchName is not None: 1359 if isinstance(pitchName, pitch.Pitch): 1360 self.pitch = pitchName 1361 else: # assume first argument is pitch 1362 self.pitch = pitch.Pitch(pitchName, **keywords) 1363 else: # supply a default pitch 1364 name = 'C4' 1365 if 'name' in keywords: 1366 name = keywords['name'] 1367 del keywords['name'] 1368 elif 'nameWithOctave' in keywords: 1369 name = keywords['nameWithOctave'] 1370 del keywords['nameWithOctave'] 1371 self.pitch = pitch.Pitch(name, **keywords) 1372 1373 # noinspection PyProtectedMember 1374 self.pitch._client = self 1375 1376 # -------------------------------------------------------------------------- 1377 # operators, representations, and transformations 1378 1379 def _reprInternal(self): 1380 return self.name 1381 1382 def __eq__(self, other): 1383 ''' 1384 Tests Equality. See docs under Note above 1385 (since __eq__'s docs don't display) 1386 1387 >>> n1 = note.Note() 1388 >>> n1.pitch.name = 'G#' 1389 >>> n2 = note.Note() 1390 >>> n2.pitch.name = 'A-' 1391 >>> n3 = note.Note() 1392 >>> n3.pitch.name = 'G#' 1393 >>> n1 == n2 1394 False 1395 >>> n1 == n3 1396 True 1397 >>> n3.duration.quarterLength = 3 1398 >>> n1 == n3 1399 False 1400 1401 >>> n1 == 5 1402 False 1403 ''' 1404 if other is None or not isinstance(other, Note): 1405 return NotImplemented 1406 1407 # checks pitch.octave, pitch.accidental, uses Pitch.__eq__ 1408 if self.pitch != other.pitch: 1409 return False 1410 1411 return super().__eq__(other) 1412 1413 def __lt__(self, other): 1414 ''' 1415 __lt__, __gt__, __le__, __ge__ all use a pitch comparison. 1416 1417 >>> highE = note.Note('E5') 1418 >>> lowF = note.Note('F2') 1419 >>> otherHighE = note.Note('E5') 1420 1421 >>> highE > lowF 1422 True 1423 >>> highE < lowF 1424 False 1425 >>> highE >= otherHighE 1426 True 1427 >>> highE <= otherHighE 1428 True 1429 1430 Notice you cannot compare Notes w/ ints or anything not pitched. 1431 1432 >>> highE < 50 1433 Traceback (most recent call last): 1434 TypeError: '<' not supported between instances of 'Note' and 'int' 1435 1436 Note also that two objects can be >= and <= without being equal, because 1437 only pitch-height is being compared in <, <=, >, >= but duration and other 1438 elements are compared in equality. 1439 1440 >>> otherHighE.duration.type = 'whole' 1441 >>> highE >= otherHighE 1442 True 1443 >>> highE <= otherHighE 1444 True 1445 >>> highE == otherHighE 1446 False 1447 ''' 1448 try: 1449 return self.pitch < other.pitch 1450 except AttributeError: 1451 return NotImplemented 1452 1453 # do not factor out into @total_ordering because of the difference between __eq__ and 1454 # the equal part of __le__ and __ge__ 1455 def __gt__(self, other): 1456 try: 1457 return self.pitch > other.pitch 1458 except AttributeError: 1459 return NotImplemented 1460 1461 def __le__(self, other): 1462 try: 1463 return self.pitch <= other.pitch 1464 except AttributeError: 1465 return NotImplemented 1466 1467 def __ge__(self, other): 1468 try: 1469 return self.pitch >= other.pitch 1470 except AttributeError: 1471 return NotImplemented 1472 1473 # -------------------------------------------------------------------------- 1474 # property access 1475 1476 def _getName(self) -> str: 1477 return self.pitch.name 1478 1479 def _setName(self, value: str): 1480 self.pitch.name = value 1481 1482 name = property(_getName, 1483 _setName, 1484 doc=''' 1485 Return or set the pitch name from the :class:`~music21.pitch.Pitch` object. 1486 See `Pitch`'s attribute :attr:`~music21.pitch.Pitch.name`. 1487 ''') 1488 1489 def _getNameWithOctave(self) -> str: 1490 return self.pitch.nameWithOctave 1491 1492 def _setNameWithOctave(self, value: str): 1493 self.pitch.nameWithOctave = value 1494 1495 nameWithOctave = property(_getNameWithOctave, 1496 _setNameWithOctave, 1497 doc=''' 1498 Return or set the pitch name with octave from the :class:`~music21.pitch.Pitch` object. 1499 See `Pitch`'s attribute :attr:`~music21.pitch.Pitch.nameWithOctave`. 1500 ''') 1501 1502 def _getStep(self) -> str: 1503 return self.pitch.step 1504 1505 def _setStep(self, value: str): 1506 self.pitch.step = value 1507 1508 step = property(_getStep, 1509 _setStep, 1510 doc=''' 1511 Return or set the pitch step from the :class:`~music21.pitch.Pitch` object. 1512 See :attr:`~music21.pitch.Pitch.step`. 1513 ''') 1514 1515 def _getOctave(self) -> int: 1516 return self.pitch.octave 1517 1518 def _setOctave(self, value: int): 1519 self.pitch.octave = value 1520 1521 octave = property(_getOctave, 1522 _setOctave, 1523 doc=''' 1524 Return or set the octave value from the :class:`~music21.pitch.Pitch` object. 1525 See :attr:`~music21.pitch.Pitch.octave`. 1526 ''') 1527 1528 @property 1529 def pitches(self) -> Tuple[pitch.Pitch]: 1530 ''' 1531 Return the single :class:`~music21.pitch.Pitch` object in a tuple. 1532 This property is designed to provide an interface analogous to 1533 that found on :class:`~music21.chord.Chord` so that `[c.pitches for c in s.notes]` 1534 provides a consistent interface for all objects. 1535 1536 >>> n = note.Note('g#') 1537 >>> n.nameWithOctave 1538 'G#' 1539 >>> n.pitches 1540 (<music21.pitch.Pitch G#>,) 1541 1542 Since this is a Note, not a chord, from the list or tuple, 1543 only the first one will be used: 1544 1545 >>> n.pitches = [pitch.Pitch('c2'), pitch.Pitch('g2')] 1546 >>> n.nameWithOctave 1547 'C2' 1548 >>> n.pitches 1549 (<music21.pitch.Pitch C2>,) 1550 1551 The value for setting must be a list or tuple: 1552 1553 >>> n.pitches = pitch.Pitch('C4') 1554 Traceback (most recent call last): 1555 music21.note.NoteException: cannot set pitches with provided object: C4 1556 1557 For setting a single one, use `n.pitch` instead. 1558 1559 Don't use strings, or you will get a string back! 1560 1561 >>> n.pitches = ('C4', 'D4') 1562 >>> n.pitch 1563 'C4' 1564 >>> n.pitch.diatonicNoteNum 1565 Traceback (most recent call last): 1566 AttributeError: 'str' object has no attribute 'diatonicNoteNum' 1567 ''' 1568 return (self.pitch,) 1569 1570 @pitches.setter 1571 def pitches(self, value: Iterable[pitch.Pitch]): 1572 if common.isListLike(value) and value: 1573 self.pitch = value[0] 1574 else: 1575 raise NoteException(f'cannot set pitches with provided object: {value}') 1576 1577 def transpose(self, value, *, inPlace=False): 1578 ''' 1579 Transpose the Note by the user-provided 1580 value. If the value is an integer, the transposition is treated in half steps. 1581 1582 If the value is a string, any Interval string specification can be provided. 1583 1584 >>> a = note.Note('g4') 1585 >>> b = a.transpose('m3') 1586 >>> b 1587 <music21.note.Note B-> 1588 >>> aInterval = interval.Interval(-6) 1589 >>> b = a.transpose(aInterval) 1590 >>> b 1591 <music21.note.Note C#> 1592 1593 >>> c = b.transpose(interval.GenericInterval(2)) 1594 >>> c 1595 <music21.note.Note D#> 1596 1597 >>> a.transpose(aInterval, inPlace=True) 1598 >>> a 1599 <music21.note.Note C#> 1600 1601 1602 If the transposition value is an integer, take the KeySignature or Key context 1603 into account... 1604 1605 >>> s = stream.Stream() 1606 >>> s.append(key.Key('D')) 1607 >>> s.append(note.Note('F')) 1608 >>> s.append(key.Key('b-', 'minor')) 1609 >>> s.append(note.Note('F')) 1610 >>> s.show('text') 1611 {0.0} <music21.key.Key of D major> 1612 {0.0} <music21.note.Note F> 1613 {1.0} <music21.key.Key of b- minor> 1614 {1.0} <music21.note.Note F> 1615 >>> for n in s.notes: 1616 ... n.transpose(1, inPlace=True) 1617 >>> s.show('text') 1618 {0.0} <music21.key.Key of D major> 1619 {0.0} <music21.note.Note F#> 1620 {1.0} <music21.key.Key of b- minor> 1621 {1.0} <music21.note.Note G-> 1622 1623 ''' 1624 if isinstance(value, interval.IntervalBase): 1625 intervalObj = value 1626 else: # try to process 1627 intervalObj = interval.Interval(value) 1628 1629 if not inPlace: 1630 post = copy.deepcopy(self) 1631 else: 1632 post = self 1633 1634 # use inPlace, b/c if we are inPlace, we operate on self; 1635 # if we are not inPlace, post is a copy 1636 post.pitch.transpose(intervalObj, inPlace=True) 1637 if (post.pitch.accidental is not None 1638 and isinstance(value, (int, interval.ChromaticInterval))): 1639 ksContext = self.getContextByClass('KeySignature') 1640 if ksContext is not None: 1641 for alteredPitch in ksContext.alteredPitches: 1642 if (post.pitch.pitchClass == alteredPitch.pitchClass 1643 and post.pitch.accidental.alter != alteredPitch.accidental.alter): 1644 post.pitch.getEnharmonic(inPlace=True) 1645 1646 if not inPlace: 1647 post.derivation.method = 'transpose' 1648 return post 1649 else: 1650 return None 1651 1652 @property 1653 def fullName(self) -> str: 1654 ''' 1655 Return the most complete representation of this Note, 1656 providing duration and pitch information. 1657 1658 1659 >>> n = note.Note('A-', quarterLength=1.5) 1660 >>> n.fullName 1661 'A-flat Dotted Quarter Note' 1662 1663 >>> n = note.Note('E~3', quarterLength=2) 1664 >>> n.fullName 1665 'E-half-sharp in octave 3 Half Note' 1666 1667 >>> n = note.Note('D', quarterLength=0.25) 1668 >>> n.pitch.microtone = 25 1669 >>> n.fullName 1670 'D (+25c) 16th Note' 1671 ''' 1672 msg = [] 1673 msg.append(self.pitch.fullName + ' ') 1674 msg.append(self.duration.fullName) 1675 msg.append(' Note') 1676 return ''.join(msg) 1677 1678 def pitchChanged(self): 1679 ''' 1680 Called by the underlying pitch if something changed there. 1681 ''' 1682 self._cache = {} 1683 if self._chordAttached is not None: 1684 self._chordAttached.clearCache() 1685 1686# ------------------------------------------------------------------------------ 1687# convenience classes 1688 1689 1690# ------------------------------------------------------------------------------ 1691class Unpitched(NotRest): 1692 ''' 1693 A General class of unpitched objects which appear at different places 1694 on the staff. Examples: percussion notation. 1695 1696 >>> unp = note.Unpitched() 1697 1698 Unpitched elements have :attr:`displayStep` and :attr:`displayOctave`, 1699 which shows where they should be displayed as if the staff were a 1700 5-line staff in treble clef, but they do not have pitch 1701 objects: 1702 1703 >>> unp.displayStep 1704 'B' 1705 >>> unp.displayOctave 1706 4 1707 >>> unp.displayStep = 'G' 1708 >>> unp.pitch 1709 Traceback (most recent call last): 1710 AttributeError: 'Unpitched' object has no attribute 'pitch' 1711 ''' 1712 1713 def __init__(self, displayName=None, **keywords): 1714 super().__init__(**keywords) 1715 1716 self.displayStep: str = 'B' 1717 self.displayOctave: int = 4 1718 if displayName: 1719 display_pitch = pitch.Pitch(displayName) 1720 self.displayStep = display_pitch.step 1721 self.displayOctave = display_pitch.octave 1722 1723 def __eq__(self, other): 1724 if super().__eq__(other) is NotImplemented: 1725 return NotImplemented 1726 if not super().__eq__(other): 1727 return False 1728 if not isinstance(other, Unpitched): 1729 return False 1730 if self.displayStep != other.displayStep: 1731 return False 1732 if self.displayOctave != other.displayOctave: 1733 return False 1734 return True 1735 1736 def _getStoredInstrument(self): 1737 return self._storedInstrument 1738 1739 def _setStoredInstrument(self, newValue): 1740 self._storedInstrument = newValue 1741 1742 storedInstrument = property(_getStoredInstrument, _setStoredInstrument) 1743 1744 def displayPitch(self) -> pitch.Pitch: 1745 ''' 1746 returns a pitch object that is the same as the displayStep and displayOctave. 1747 it will never have an accidental. 1748 1749 >>> unp = note.Unpitched() 1750 >>> unp.displayStep = 'E' 1751 >>> unp.displayOctave = 4 1752 >>> unp.displayPitch() 1753 <music21.pitch.Pitch E4> 1754 ''' 1755 p = pitch.Pitch() 1756 p.step = self.displayStep 1757 p.octave = self.displayOctave 1758 return p 1759 1760 @property 1761 def displayName(self) -> str: 1762 ''' 1763 Returns the `nameWithOctave` of the :meth:`displayPitch`. 1764 1765 >>> unp = note.Unpitched('B2') 1766 >>> unp.displayName 1767 'B2' 1768 ''' 1769 display_pitch = self.displayPitch() 1770 return display_pitch.nameWithOctave 1771 1772 1773# ------------------------------------------------------------------------------ 1774class Rest(GeneralNote): 1775 ''' 1776 Rests are represented in music21 as GeneralNote objects that do not have 1777 a pitch object attached to them. By default they have length 1.0 (Quarter Rest) 1778 1779 Calling :attr:`~music21.stream.Stream.notes` on a Stream does not get rests. 1780 However, the property :attr:`~music21.stream.Stream.notesAndRests` of Streams 1781 gets rests as well. 1782 1783 1784 >>> r = note.Rest() 1785 >>> r.isRest 1786 True 1787 >>> r.isNote 1788 False 1789 >>> r.duration.quarterLength = 2.0 1790 >>> r.duration.type 1791 'half' 1792 1793 All Rests have the name property 'rest': 1794 1795 >>> r.name 1796 'rest' 1797 ''' 1798 isRest = True 1799 name = 'rest' 1800 1801 _DOC_ATTR = { 1802 'isNote': 'Boolean read-only value describing if this Rest is a Note (False).', 1803 'isRest': 'Boolean read-only value describing if this Rest is a Rest (True, obviously).', 1804 'name': '''returns "rest" always. It is here so that you can get 1805 `x.name` on all `.notesAndRests` objects''', 1806 'stepShift': 'number of lines/spaces to shift the note upwards or downwards for display.', 1807 'fullMeasure': '''does this rest last a full measure (thus display as whole, center, etc.) 1808 Options are False, True, "always", "auto" (default) 1809 1810 False means do not set as full measure, no matter what. 1811 1812 True keeps the set duration, but will always display as a full measure rest. 1813 1814 "always" means the duration will (EVENTUALLY, not yet!) 1815 update automatically to match the time signature context; and is True. 1816 Does not work yet -- functions as True. 1817 1818 # TODO: get it to work. 1819 1820 "auto" is the default, where if the rest value happens to match the current 1821 time signature context, then display it as a whole rest, centered, etc. 1822 otherwise will display normally. 1823 1824 See examples in :meth:`music21.musicxml.m21ToXml.MeasureExporter.restToXml` 1825 ''', 1826 } 1827 1828 def __init__(self, *arguments, **keywords): 1829 super().__init__(**keywords) 1830 self.stepShift = 0 # display line 1831 self.fullMeasure = 'auto' # see docs; True, False, 'always', 1832 1833 def _reprInternal(self): 1834 duration_name = self.duration.fullName.lower() 1835 if len(duration_name) < 15: # dotted quarter = 14 1836 return duration_name.replace(' ', '-') 1837 else: 1838 ql = self.duration.quarterLength 1839 if ql == int(ql): 1840 ql = int(ql) 1841 ql_string = str(ql) 1842 return f'{ql_string}ql' 1843 1844 def __eq__(self, other): 1845 ''' 1846 A Music21 rest is equal to another object if that object is also a rest which 1847 has the same duration. 1848 1849 1850 >>> r1 = note.Rest() 1851 >>> r2 = note.Rest() 1852 >>> r1 == r2 1853 True 1854 >>> r1 != r2 1855 False 1856 1857 >>> r2.duration.quarterLength = 4.0/3 1858 >>> r1 == r2 1859 False 1860 >>> r1 == note.Note() 1861 False 1862 ''' 1863 if not isinstance(other, Rest): 1864 return NotImplemented 1865 1866 return super().__eq__(other) 1867 1868 @property 1869 def fullName(self) -> str: 1870 ''' 1871 Return the most complete representation of this Rest, 1872 providing duration information. 1873 1874 >>> r = note.Rest(quarterLength=1.5) 1875 >>> r.fullName 1876 'Dotted Quarter Rest' 1877 1878 >>> note.Rest(type='whole').fullName 1879 'Whole Rest' 1880 ''' 1881 return self.duration.fullName + ' Rest' 1882 1883 1884# ------------------------------------------------------------------------------ 1885# test methods and classes 1886 1887class TestExternal(unittest.TestCase): 1888 ''' 1889 These are tests that open windows and rely on external software 1890 ''' 1891 show = True 1892 1893 def testSingle(self): 1894 '''Need to test direct meter creation w/o stream 1895 ''' 1896 a = Note('d-3') 1897 a.quarterLength = 2.25 1898 if self.show: 1899 a.show() 1900 1901 def testBasic(self): 1902 from music21 import stream 1903 a = stream.Stream() 1904 1905 for pitchName, qLen in [('d-3', 2.5), ('c#6', 3.25), ('a--5', 0.5), 1906 ('f', 1.75), ('g3', 1.5), ('d##4', 1.25), 1907 ('d-3', 2.5), ('c#6', 3.25), ('a--5', 0.5), 1908 ('f#2', 1.75), ('g-3', (4 / 3)), ('d#6', (2 / 3)) 1909 ]: 1910 b = Note() 1911 b.quarterLength = qLen 1912 b.name = pitchName 1913 b.style.color = '#FF00FF' 1914 a.append(b) 1915 1916 if self.show: 1917 a.show() 1918 1919 1920# ------------------------------------------------------------------------------ 1921class Test(unittest.TestCase): 1922 1923 def testCopyAndDeepcopy(self): 1924 ''' 1925 Test copying all objects defined in this module 1926 ''' 1927 import sys 1928 import types 1929 for part in sys.modules[self.__module__].__dict__: 1930 match = False 1931 for skip in ['_', '__', 'Test', 'Exception']: 1932 if part.startswith(skip) or part.endswith(skip): 1933 match = True 1934 if match: 1935 continue 1936 name = getattr(sys.modules[self.__module__], part) 1937 # noinspection PyTypeChecker 1938 if callable(name) and not isinstance(name, types.FunctionType): 1939 try: # see if obj can be made w/ args 1940 obj = name() 1941 except TypeError: # pragma: no cover 1942 continue 1943 a = copy.copy(obj) 1944 b = copy.deepcopy(obj) 1945 self.assertNotEqual(id(a), id(b)) 1946 1947 def testLyricRepr(self): 1948 from music21 import note 1949 ly = note.Lyric() 1950 self.assertEqual(repr(ly), '<music21.note.Lyric number=1>') 1951 ly.text = 'hi' 1952 self.assertEqual(repr(ly), "<music21.note.Lyric number=1 text='hi'>") 1953 ly.identifier = 'verse' 1954 self.assertEqual(repr(ly), "<music21.note.Lyric number=1 identifier='verse' text='hi'>") 1955 ly.text = None 1956 self.assertEqual(repr(ly), "<music21.note.Lyric number=1 identifier='verse'>") 1957 1958 def testComplex(self): 1959 from music21 import note 1960 note1 = note.Note() 1961 note1.duration.clear() 1962 d1 = duration.DurationTuple('whole', 0, 4.0) 1963 d2 = duration.DurationTuple('quarter', 0, 1.0) 1964 note1.duration.addDurationTuple(d1) 1965 note1.duration.addDurationTuple(d2) 1966 self.assertEqual(note1.duration.quarterLength, 5.0) 1967 self.assertEqual(note1.duration.componentIndexAtQtrPosition(2), 0) 1968 self.assertEqual(note1.duration.componentIndexAtQtrPosition(4), 1) 1969 self.assertEqual(note1.duration.componentIndexAtQtrPosition(4.5), 1) 1970 note1.duration.sliceComponentAtPosition(1.0) 1971 1972 matchStr = "c'4~\nc'2.~\nc'4" 1973 from music21.lily.translate import LilypondConverter 1974 conv = LilypondConverter() 1975 conv.appendM21ObjectToContext(note1) 1976 outStr = str(conv.context).replace(' ', '').strip() 1977 # print(outStr) 1978 self.assertEqual(matchStr, outStr) 1979 i = 0 1980 for thisNote in note1.splitAtDurations(): 1981 matchSub = matchStr.split('\n')[i] # pylint: disable=use-maxsplit-arg 1982 conv = LilypondConverter() 1983 conv.appendM21ObjectToContext(thisNote) 1984 outStr = str(conv.context).replace(' ', '').strip() 1985 self.assertEqual(matchSub, outStr) 1986 i += 1 1987 1988 def testNote(self): 1989 note2 = Rest() 1990 self.assertTrue(note2.isRest) 1991 note3 = Note() 1992 note3.pitch.name = 'B-' 1993 # not sure how to test not None 1994 # self.assertFalse (note3.pitch.accidental, None) 1995 self.assertEqual(note3.pitch.accidental.name, 'flat') 1996 self.assertEqual(note3.pitch.pitchClass, 10) 1997 1998 a5 = Note() 1999 a5.name = 'A' 2000 a5.octave = 5 2001 self.assertAlmostEqual(a5.pitch.frequency, 880.0) 2002 self.assertEqual(a5.pitch.pitchClass, 9) 2003 2004 def testCopyNote(self): 2005 a = Note() 2006 a.quarterLength = 3.5 2007 a.name = 'D' 2008 b = copy.deepcopy(a) 2009 self.assertEqual(b.name, a.name) 2010 2011 def testMusicXMLFermata(self): 2012 from music21 import corpus 2013 a = corpus.parse('bach/bwv5.7') 2014 found = [] 2015 for n in a.flatten().notesAndRests: 2016 for obj in n.expressions: 2017 if isinstance(obj, expressions.Fermata): 2018 found.append(obj) 2019 self.assertEqual(len(found), 24) 2020 2021 def testNoteBeatProperty(self): 2022 from music21 import stream 2023 from music21 import meter 2024 2025 data = [ 2026 ['3/4', 0.5, 6, [1.0, 1.5, 2.0, 2.5, 3.0, 3.5], 2027 [1.0] * 6, ], 2028 ['3/4', 0.25, 8, [1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75], 2029 [1.0] * 8], 2030 ['3/2', 0.5, 8, [1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75], 2031 [2.0] * 8], 2032 2033 ['6/8', 0.5, 6, [1.0, 1.3333, 1.66666, 2.0, 2.3333, 2.666666], 2034 [1.5] * 6], 2035 ['9/8', 0.5, 6, [1.0, 1.3333, 1.66666, 2.0, 2.3333, 2.666666], 2036 [1.5] * 6], 2037 ['12/8', 0.5, 6, [1.0, 1.3333, 1.66666, 2.0, 2.3333, 2.666666], 2038 [1.5] * 6], 2039 2040 ['6/16', 0.25, 6, [1.0, 1.3333, 1.66666, 2.0, 2.3333, 2.666666], 2041 [0.75] * 6], 2042 2043 ['5/4', 1, 5, [1.0, 2.0, 3.0, 4.0, 5.0], 2044 [1.] * 5], 2045 2046 ['2/8+3/8+2/8', 0.5, 6, [1.0, 1.5, 2.0, 2.33333, 2.66666, 3.0], 2047 [1., 1., 1.5, 1.5, 1.5, 1.]], 2048 2049 ] 2050 2051 # one measure case 2052 for tsStr, nQL, nCount, matchBeat, matchBeatDur in data: 2053 n = Note() # need fully qualified name 2054 n.quarterLength = nQL 2055 m = stream.Measure() 2056 m.timeSignature = meter.TimeSignature(tsStr) 2057 m.repeatAppend(n, nCount) 2058 2059 self.assertEqual(len(m), nCount + 1) 2060 2061 # test matching beat proportion value 2062 post = [m.notesAndRests[i].beat for i in range(nCount)] 2063 for i in range(len(matchBeat)): 2064 self.assertAlmostEqual(post[i], matchBeat[i], 4) 2065 2066 # test getting beat duration 2067 post = [m.notesAndRests[i].beatDuration.quarterLength for i in range(nCount)] 2068 2069 for i in range(len(matchBeat)): 2070 self.assertAlmostEqual(post[i], matchBeatDur[i], 4) 2071 2072 # two measure case 2073 for tsStr, nQL, nCount, matchBeat, matchBeatDur in data: 2074 p = stream.Part() 2075 n = Note() 2076 n.quarterLength = nQL 2077 2078 # m1 has time signature 2079 m1 = stream.Measure() 2080 m1.timeSignature = meter.TimeSignature(tsStr) 2081 p.append(m1) 2082 2083 # m2 does not have time signature 2084 m2 = stream.Measure() 2085 m2.repeatAppend(n, nCount) 2086 self.assertEqual(len(m2), nCount) 2087 self.assertEqual(len(m2.notesAndRests), nCount) 2088 2089 p.append(m2) 2090 2091 # test matching beat proportion value 2092 post = [m2.notesAndRests[i].beat for i in range(nCount)] 2093 for i in range(len(matchBeat)): 2094 self.assertAlmostEqual(post[i], matchBeat[i], 4) 2095 # test getting beat duration 2096 post = [m2.notesAndRests[i].beatDuration.quarterLength for i in range(nCount)] 2097 for i in range(len(matchBeat)): 2098 self.assertAlmostEqual(post[i], matchBeatDur[i], 4) 2099 2100 def testNoteBeatPropertyCorpus(self): 2101 data = [['bach/bwv255', [4.0, 1.0, 2.5, 3.0, 4.0, 4.5, 1.0, 1.5]], 2102 ['bach/bwv153.9', [1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 3.0, 1.0]] 2103 ] 2104 2105 for work, match in data: 2106 from music21 import corpus 2107 s = corpus.parse(work) 2108 # always use tenor line 2109 found = [] 2110 for n in s.parts[2].flatten().notesAndRests: 2111 n.lyric = n.beatStr 2112 found.append(n.beat) 2113 2114 for i in range(len(match)): 2115 self.assertEqual(match[i], found[i]) 2116 2117 # s.show() 2118 2119 def testNoteEquality(self): 2120 from music21 import articulations 2121 2122 n1 = Note('a#') 2123 n2 = Note('g') 2124 n3 = Note('a-') 2125 n4 = Note('a#') 2126 2127 self.assertNotEqual(n1, n2) 2128 self.assertNotEqual(n1, n3) 2129 self.assertEqual(n1, n4) 2130 2131 # test durations with the same pitch 2132 for x, y, match in [ 2133 (1, 1, True), 2134 (1, 0.5, False), 2135 (1, 2, False), 2136 (1, 1.5, False) 2137 ]: 2138 n1.quarterLength = x 2139 n4.quarterLength = y 2140 self.assertEqual(n1 == n4, match) # sub1 2141 2142 # test durations with different pitch 2143 for x, y, match in [(1, 1, False), (1, 0.5, False), 2144 (1, 2, False), (1, 1.5, False)]: 2145 n1.quarterLength = x 2146 n2.quarterLength = y 2147 self.assertEqual(n1 == n2, match) # sub2 2148 2149 # same pitches different octaves 2150 n1.quarterLength = 1.0 2151 n4.quarterLength = 1.0 2152 for x, y, match in [(4, 4, True), (3, 4, False), (2, 4, False)]: 2153 n1.pitch.octave = x 2154 n4.pitch.octave = y 2155 self.assertEqual(n1 == n4, match) # sub4 2156 2157 # with and without ties 2158 n1.pitch.octave = 4 2159 n4.pitch.octave = 4 2160 t1 = tie.Tie() 2161 t2 = tie.Tie() 2162 for x, y, match in [(t1, None, False), (t1, t2, True)]: 2163 n1.tie = x 2164 n4.tie = y 2165 self.assertEqual(n1 == n4, match) # sub4 2166 2167 # with ties but different pitches 2168 for n in [n1, n2, n3, n4]: 2169 n.quarterLength = 1.0 2170 t1 = tie.Tie() 2171 t2 = tie.Tie() 2172 for a, b, match in [(n1, n2, False), (n1, n3, False), 2173 (n2, n3, False), (n1, n4, True)]: 2174 a.tie = t1 2175 b.tie = t2 2176 self.assertEqual(a == b, match) # sub5 2177 2178 # articulation groups 2179 a1 = [articulations.Accent()] 2180 a2 = [articulations.Accent(), articulations.StrongAccent()] 2181 a3 = [articulations.StrongAccent(), articulations.Accent()] 2182 a4 = [articulations.StrongAccent(), articulations.Accent(), 2183 articulations.Tenuto()] 2184 a5 = [articulations.Accent(), articulations.Tenuto(), 2185 articulations.StrongAccent()] 2186 2187 for a, b, c, d, match in [(n1, n4, a1, a1, True), 2188 (n1, n2, a1, a1, False), (n1, n3, a1, a1, False), 2189 # same pitch different orderings 2190 (n1, n4, a2, a3, True), (n1, n4, a4, a5, True), 2191 # different pitch same orderings 2192 (n1, n2, a2, a3, False), (n1, n3, a4, a5, False), 2193 ]: 2194 a.articulations = c 2195 b.articulations = d 2196 self.assertEqual(a == b, match) # sub6 2197 2198 def testMetricalAccent(self): 2199 from music21 import meter 2200 from music21 import stream 2201 data = [ 2202 ('4/4', 8, 0.5, [1.0, 0.125, 0.25, 0.125, 0.5, 0.125, 0.25, 0.125]), 2203 ('3/4', 6, 0.5, [1.0, 0.25, 0.5, 0.25, 0.5, 0.25]), 2204 ('6/8', 6, 0.5, [1.0, 0.25, 0.25, 0.5, 0.25, 0.25]), 2205 2206 ('12/32', 12, 0.125, [1.0, 0.125, 0.125, 0.25, 0.125, 0.125, 2207 0.5, 0.125, 0.125, 0.25, 0.125, 0.125]), 2208 2209 ('5/8', 10, 0.25, [1.0, 0.25, 0.5, 0.25, 0.5, 0.25, 0.5, 0.25, 0.5, 0.25]), 2210 2211 # test notes that do not have defined accents 2212 ('4/4', 16, 0.25, [1.0, 0.0625, 0.125, 0.0625, 0.25, 0.0625, 0.125, 0.0625, 2213 0.5, 0.0625, 0.125, 0.0625, 0.25, 0.0625, 0.125, 0.0625]), 2214 ('4/4', 32, 0.125, [1.0, 0.0625, 0.0625, 0.0625, 0.125, 0.0625, 0.0625, 0.0625, 2215 0.25, 0.0625, 0.0625, 0.0625, 0.125, 0.0625, 0.0625, 0.0625, 2216 0.5, 0.0625, 0.0625, 0.0625, 0.125, 0.0625, 0.0625, 0.0625, 2217 0.25, 0.0625, 0.0625, 0.0625, 0.125, 0.0625, 0.0625, 0.0625]), 2218 ] 2219 2220 for tsStr, nCount, dur, match in data: 2221 2222 m = stream.Measure() 2223 m.timeSignature = meter.TimeSignature(tsStr) 2224 n = Note() 2225 n.quarterLength = dur 2226 m.repeatAppend(n, nCount) 2227 2228 self.assertEqual([n.beatStrength for n in m.notesAndRests], match) 2229 2230 def testTieContinue(self): 2231 from music21 import stream 2232 2233 n1 = Note() 2234 n1.tie = tie.Tie() 2235 n1.tie.type = 'start' 2236 2237 n2 = Note() 2238 n2.tie = tie.Tie() 2239 n2.tie.type = 'continue' 2240 2241 n3 = Note() 2242 n3.tie = tie.Tie() 2243 n3.tie.type = 'stop' 2244 2245 s = stream.Stream() 2246 s.append([n1, n2, n3]) 2247 2248 # need to test that this gets us a continue tie, but hard to test 2249 # post musicxml processing 2250 # s.show() 2251 2252 def testVolumeA(self): 2253 from music21 import note 2254 v1 = volume.Volume() 2255 2256 n1 = note.Note() 2257 n2 = note.Note() 2258 2259 n1.volume = v1 # can set as v1 has no client 2260 self.assertEqual(n1.volume, v1) 2261 self.assertEqual(n1.volume.client, n1) 2262 2263 # object is created on demand 2264 self.assertIsNot(n2.volume, v1) 2265 self.assertIsNotNone(n2.volume) 2266 2267 def testVolumeB(self): 2268 from music21 import note 2269 # manage deepcopying properly 2270 n1 = note.Note() 2271 2272 n1.volume.velocity = 100 2273 self.assertEqual(n1.volume.velocity, 100) 2274 self.assertEqual(n1.volume.client, n1) 2275 2276 n1Copy = copy.deepcopy(n1) 2277 self.assertEqual(n1Copy.volume.velocity, 100) 2278 self.assertEqual(n1Copy.volume.client, n1Copy) 2279 2280 2281# ------------------------------------------------------------------------------ 2282# define presented order in documentation 2283_DOC_ORDER = [Note, Rest, Unpitched, NotRest, GeneralNote, Lyric] 2284 2285if __name__ == '__main__': 2286 # sys.arg test options will be used in mainTest() 2287 import music21 2288 music21.mainTest(Test) 2289 2290