0
# Musical Containers
1
2
Data structures for representing and manipulating musical information at different hierarchical levels - from individual notes to complete compositions. These classes store and organize musical data for analysis, manipulation, and playback.
3
4
## Capabilities
5
6
### Note Class
7
8
Represents a single musical note with pitch, octave, dynamics, and MIDI properties.
9
10
```python { .api }
11
class Note:
12
"""Single musical note with pitch and performance attributes."""
13
14
def __init__(self, name: str = "C", octave: int = 4, dynamics: Any = None, velocity: int = None, channel: int = None):
15
"""
16
Create Note object.
17
18
Parameters:
19
- name: Note name (e.g., "C", "F#", "Bb")
20
- octave: Octave number (typically 0-9)
21
- dynamics: Dynamic marking (pp, p, mp, mf, f, ff, etc.)
22
- velocity: MIDI velocity (0-127)
23
- channel: MIDI channel (1-16)
24
"""
25
26
@property
27
def name(self) -> str:
28
"""Note name (e.g., "C#")."""
29
30
@property
31
def octave(self) -> int:
32
"""Octave number."""
33
34
@property
35
def dynamics(self) -> Any:
36
"""Dynamic marking."""
37
38
@property
39
def velocity(self) -> int:
40
"""MIDI velocity (0-127)."""
41
42
@property
43
def channel(self) -> int:
44
"""MIDI channel (1-16)."""
45
46
def set_note(self, name: str = "C", octave: int = 4, dynamics: Any = None, velocity: int = None, channel: int = None) -> None:
47
"""
48
Set note properties.
49
50
Parameters:
51
- name: Note name
52
- octave: Octave number
53
- dynamics: Dynamic marking
54
- velocity: MIDI velocity
55
- channel: MIDI channel
56
"""
57
58
def empty(self) -> None:
59
"""Clear note to default values."""
60
61
def augment(self) -> None:
62
"""Augment note (raise by semitone)."""
63
64
def diminish(self) -> None:
65
"""Diminish note (lower by semitone)."""
66
67
def transpose(self, interval: str, up: bool = True) -> None:
68
"""
69
Transpose note by interval.
70
71
Parameters:
72
- interval: Interval to transpose (e.g., "3", "5", "b7")
73
- up: Direction (True for up, False for down)
74
"""
75
76
def change_octave(self, diff: int) -> None:
77
"""
78
Change octave by difference.
79
80
Parameters:
81
- diff: Octave difference (positive or negative)
82
"""
83
84
def octave_up(self) -> None:
85
"""Move up one octave."""
86
87
def octave_down(self) -> None:
88
"""Move down one octave."""
89
90
def to_hertz(self, standard_pitch: float = 440) -> float:
91
"""
92
Convert to frequency in Hz.
93
94
Parameters:
95
- standard_pitch: A4 frequency reference
96
97
Returns:
98
Frequency in Hz
99
"""
100
101
def from_hertz(self, hertz: float, standard_pitch: float = 440) -> None:
102
"""
103
Set note from frequency.
104
105
Parameters:
106
- hertz: Frequency in Hz
107
- standard_pitch: A4 frequency reference
108
"""
109
110
def measure(self, other: 'Note') -> int:
111
"""
112
Measure interval to other note in half steps.
113
114
Parameters:
115
- other: Other Note object
116
117
Returns:
118
Interval in half steps
119
"""
120
121
def to_shorthand(self) -> str:
122
"""
123
Convert to shorthand notation.
124
125
Returns:
126
Shorthand notation string
127
"""
128
129
def from_shorthand(self, shorthand: str) -> None:
130
"""
131
Set from shorthand notation.
132
133
Parameters:
134
- shorthand: Shorthand notation string
135
"""
136
```
137
138
### NoteContainer Class
139
140
Container for holding multiple notes, representing chords, intervals, or simultaneous note combinations.
141
142
```python { .api }
143
class NoteContainer:
144
"""Container for multiple notes (chords, intervals)."""
145
146
def __init__(self, notes: List[Union[str, Note]] = None):
147
"""
148
Create NoteContainer.
149
150
Parameters:
151
- notes: List of note names or Note objects
152
"""
153
154
def empty(self) -> None:
155
"""Empty container of all notes."""
156
157
def add_note(self, note: Union[str, Note], octave: int = None, dynamics: Any = None) -> bool:
158
"""
159
Add note to container.
160
161
Parameters:
162
- note: Note name or Note object
163
- octave: Octave for note name
164
- dynamics: Dynamic marking
165
166
Returns:
167
True if note was added successfully
168
"""
169
170
def add_notes(self, notes: List[Union[str, Note]]) -> None:
171
"""
172
Add multiple notes to container.
173
174
Parameters:
175
- notes: List of note names or Note objects
176
"""
177
178
def remove_note(self, note: Union[str, Note], octave: int = -1) -> bool:
179
"""
180
Remove note from container.
181
182
Parameters:
183
- note: Note name or Note object to remove
184
- octave: Specific octave to remove (-1 for any)
185
186
Returns:
187
True if note was removed
188
"""
189
190
def remove_notes(self, notes: List[Union[str, Note]]) -> None:
191
"""
192
Remove multiple notes from container.
193
194
Parameters:
195
- notes: List of notes to remove
196
"""
197
198
def from_chord(self, shorthand: str) -> bool:
199
"""
200
Create container from chord shorthand.
201
202
Parameters:
203
- shorthand: Chord shorthand (e.g., "CM7", "Am", "F#dim")
204
205
Returns:
206
True if chord was created successfully
207
"""
208
209
def from_interval(self, startnote: str, shorthand: str, up: bool = True) -> bool:
210
"""
211
Create container from interval.
212
213
Parameters:
214
- startnote: Starting note
215
- shorthand: Interval shorthand (e.g., "3", "5", "b7")
216
- up: Direction (True for up, False for down)
217
218
Returns:
219
True if interval was created successfully
220
"""
221
222
def determine(self, shorthand: bool = False) -> str:
223
"""
224
Determine chord name.
225
226
Parameters:
227
- shorthand: Return shorthand notation
228
229
Returns:
230
Chord name or description
231
"""
232
233
def sort(self) -> None:
234
"""Sort notes by pitch (lowest to highest)."""
235
236
def transpose(self, interval: str, up: bool = True) -> None:
237
"""
238
Transpose all notes by interval.
239
240
Parameters:
241
- interval: Interval to transpose
242
- up: Direction (True for up, False for down)
243
"""
244
245
def augment(self) -> None:
246
"""Augment all notes."""
247
248
def diminish(self) -> None:
249
"""Diminish all notes."""
250
251
def is_consonant(self, include_fourths: bool = True) -> bool:
252
"""
253
Test if container contains consonant intervals.
254
255
Parameters:
256
- include_fourths: Include perfect fourths as consonant
257
258
Returns:
259
True if all intervals are consonant
260
"""
261
262
def is_dissonant(self, include_fourths: bool = False) -> bool:
263
"""
264
Test if container contains dissonant intervals.
265
266
Parameters:
267
- include_fourths: Include perfect fourths as dissonant
268
269
Returns:
270
True if any intervals are dissonant
271
"""
272
273
def get_note_names(self) -> List[str]:
274
"""
275
Get list of note names.
276
277
Returns:
278
List of note names in container
279
"""
280
281
def remove_duplicate_notes(self) -> None:
282
"""Remove duplicate notes from container."""
283
```
284
285
### Bar Class
286
287
Represents a musical bar/measure containing notes with durations and timing information.
288
289
```python { .api }
290
class Bar:
291
"""Musical bar/measure with notes and timing."""
292
293
def __init__(self, key: str = "C", meter: Tuple[int, int] = (4, 4)):
294
"""
295
Create Bar object.
296
297
Parameters:
298
- key: Key signature
299
- meter: Time signature (numerator, denominator)
300
"""
301
302
@property
303
def key(self) -> str:
304
"""Key signature."""
305
306
@property
307
def meter(self) -> Tuple[int, int]:
308
"""Time signature tuple."""
309
310
def empty(self) -> None:
311
"""Empty bar of all notes."""
312
313
def set_meter(self, meter: Tuple[int, int]) -> None:
314
"""
315
Set time signature.
316
317
Parameters:
318
- meter: Time signature tuple
319
"""
320
321
def place_notes(self, notes: Union[str, List[str], Note, NoteContainer], duration: int) -> bool:
322
"""
323
Place notes with duration in bar.
324
325
Parameters:
326
- notes: Notes to place (various formats accepted)
327
- duration: Note duration (1=whole, 2=half, 4=quarter, etc.)
328
329
Returns:
330
True if notes were placed successfully
331
"""
332
333
def place_notes_at(self, notes: Union[str, List[str], Note, NoteContainer], at: float) -> bool:
334
"""
335
Place notes at specific position in bar.
336
337
Parameters:
338
- notes: Notes to place
339
- at: Position in bar (0.0 to 1.0)
340
341
Returns:
342
True if notes were placed successfully
343
"""
344
345
def place_rest(self, duration: int) -> bool:
346
"""
347
Place rest with duration.
348
349
Parameters:
350
- duration: Rest duration
351
352
Returns:
353
True if rest was placed successfully
354
"""
355
356
def is_full(self) -> bool:
357
"""
358
Check if bar is full.
359
360
Returns:
361
True if bar contains a full measure worth of notes/rests
362
"""
363
364
def space_left(self) -> float:
365
"""
366
Get remaining space in bar.
367
368
Returns:
369
Remaining space as fraction (0.0 to 1.0)
370
"""
371
372
def value_left(self) -> int:
373
"""
374
Get remaining value in bar.
375
376
Returns:
377
Remaining note value
378
"""
379
380
def remove_last_entry(self) -> bool:
381
"""
382
Remove last entry from bar.
383
384
Returns:
385
True if entry was removed
386
"""
387
388
def transpose(self, interval: str, up: bool = True) -> None:
389
"""
390
Transpose all notes in bar.
391
392
Parameters:
393
- interval: Interval to transpose
394
- up: Direction (True for up, False for down)
395
"""
396
397
def augment(self) -> None:
398
"""Augment all notes in bar."""
399
400
def diminish(self) -> None:
401
"""Diminish all notes in bar."""
402
403
def determine_chords(self, shorthand: bool = False) -> List[str]:
404
"""
405
Determine chords in bar.
406
407
Parameters:
408
- shorthand: Return shorthand notation
409
410
Returns:
411
List of chord names
412
"""
413
414
def determine_progression(self, shorthand: bool = False) -> List[str]:
415
"""
416
Determine chord progression in bar.
417
418
Parameters:
419
- shorthand: Return shorthand notation
420
421
Returns:
422
List of chord progressions
423
"""
424
425
def get_range(self) -> Tuple[Note, Note]:
426
"""
427
Get note range in bar.
428
429
Returns:
430
Tuple of (lowest_note, highest_note)
431
"""
432
433
def get_note_names(self) -> List[str]:
434
"""
435
Get all note names in bar.
436
437
Returns:
438
List of note names
439
"""
440
```
441
442
### Track Class
443
444
Represents a musical track containing a sequence of bars, typically for a single instrument or voice.
445
446
```python { .api }
447
class Track:
448
"""Musical track containing sequence of bars."""
449
450
def __init__(self, instrument: Any = None):
451
"""
452
Create Track object.
453
454
Parameters:
455
- instrument: Associated instrument object
456
"""
457
458
@property
459
def instrument(self) -> Any:
460
"""Associated instrument."""
461
462
@property
463
def name(self) -> str:
464
"""Track name."""
465
466
def add_bar(self, bar: Bar) -> None:
467
"""
468
Add bar to track.
469
470
Parameters:
471
- bar: Bar object to add
472
"""
473
474
def add_notes(self, note: Union[str, Note, NoteContainer], duration: int = None) -> None:
475
"""
476
Add notes with duration to track.
477
478
Parameters:
479
- note: Notes to add
480
- duration: Note duration
481
"""
482
483
def get_notes(self) -> List[Note]:
484
"""
485
Get all notes in track.
486
487
Returns:
488
List of all Note objects in track
489
"""
490
491
def from_chords(self, chords: List[str], duration: int = 1) -> None:
492
"""
493
Create track from chord list.
494
495
Parameters:
496
- chords: List of chord shorthands
497
- duration: Duration for each chord
498
"""
499
500
def transpose(self, interval: str, up: bool = True) -> None:
501
"""
502
Transpose entire track.
503
504
Parameters:
505
- interval: Interval to transpose
506
- up: Direction (True for up, False for down)
507
"""
508
509
def augment(self) -> None:
510
"""Augment all notes in track."""
511
512
def diminish(self) -> None:
513
"""Diminish all notes in track."""
514
515
def get_tuning(self) -> Any:
516
"""
517
Get instrument tuning.
518
519
Returns:
520
Tuning object if instrument has tuning
521
"""
522
523
def set_tuning(self, tuning: Any) -> None:
524
"""
525
Set instrument tuning.
526
527
Parameters:
528
- tuning: Tuning object
529
"""
530
531
def test_integrity(self) -> bool:
532
"""
533
Test track integrity.
534
535
Returns:
536
True if track structure is valid
537
"""
538
```
539
540
### Composition Class
541
542
Represents a complete musical composition containing multiple tracks with metadata.
543
544
```python { .api }
545
class Composition:
546
"""Complete musical composition with multiple tracks."""
547
548
def __init__(self):
549
"""Create Composition object."""
550
551
@property
552
def title(self) -> str:
553
"""Composition title."""
554
555
@property
556
def subtitle(self) -> str:
557
"""Composition subtitle."""
558
559
@property
560
def author(self) -> str:
561
"""Author name."""
562
563
@property
564
def email(self) -> str:
565
"""Author email."""
566
567
def empty(self) -> None:
568
"""Empty composition of all tracks."""
569
570
def reset(self) -> None:
571
"""Reset composition to initial state."""
572
573
def add_track(self, track: Track) -> None:
574
"""
575
Add track to composition.
576
577
Parameters:
578
- track: Track object to add
579
"""
580
581
def add_note(self, note: Union[str, Note]) -> None:
582
"""
583
Add note to first track.
584
585
Parameters:
586
- note: Note to add
587
"""
588
589
def set_title(self, title: str = "Untitled", subtitle: str = "") -> None:
590
"""
591
Set composition title and subtitle.
592
593
Parameters:
594
- title: Main title
595
- subtitle: Subtitle
596
"""
597
598
def set_author(self, author: str = "", email: str = "") -> None:
599
"""
600
Set author information.
601
602
Parameters:
603
- author: Author name
604
- email: Author email
605
"""
606
```
607
608
### Suite Class
609
610
Represents a suite containing multiple compositions.
611
612
```python { .api }
613
class Suite:
614
"""Suite containing multiple compositions."""
615
616
def __init__(self):
617
"""Create Suite object."""
618
619
def add_composition(self, composition: Composition) -> None:
620
"""
621
Add composition to suite.
622
623
Parameters:
624
- composition: Composition object to add
625
"""
626
627
def set_author(self, author: str, email: str = "") -> None:
628
"""
629
Set author information.
630
631
Parameters:
632
- author: Author name
633
- email: Author email
634
"""
635
636
def set_title(self, title: str, subtitle: str = "") -> None:
637
"""
638
Set suite title and subtitle.
639
640
Parameters:
641
- title: Main title
642
- subtitle: Subtitle
643
"""
644
```
645
646
### Instrument Classes
647
648
Classes representing various musical instruments with range validation and specific capabilities.
649
650
```python { .api }
651
class Instrument:
652
"""Base instrument class."""
653
654
def __init__(self):
655
"""Create Instrument object."""
656
657
def set_range(self, range: Tuple[Note, Note]) -> None:
658
"""
659
Set instrument range.
660
661
Parameters:
662
- range: Tuple of (lowest_note, highest_note)
663
"""
664
665
def note_in_range(self, note: Note) -> bool:
666
"""
667
Check if note is in instrument range.
668
669
Parameters:
670
- note: Note to check
671
672
Returns:
673
True if note is in range
674
"""
675
676
def notes_in_range(self, notes: List[Note]) -> bool:
677
"""
678
Check if all notes are in range.
679
680
Parameters:
681
- notes: List of notes to check
682
683
Returns:
684
True if all notes are in range
685
"""
686
687
def can_play_notes(self, notes: List[Note]) -> bool:
688
"""
689
Check if instrument can play notes.
690
691
Parameters:
692
- notes: List of notes to check
693
694
Returns:
695
True if instrument can play all notes
696
"""
697
698
class Piano(Instrument):
699
"""Piano instrument with 88-key range."""
700
701
def __init__(self):
702
"""Create Piano object with standard range."""
703
704
class Guitar(Instrument):
705
"""Guitar instrument with standard tuning."""
706
707
def __init__(self):
708
"""Create Guitar object with standard tuning."""
709
710
class MidiInstrument(Instrument):
711
"""Generic MIDI instrument."""
712
713
def __init__(self, name: str = ""):
714
"""
715
Create MIDI instrument.
716
717
Parameters:
718
- name: Instrument name
719
"""
720
721
class MidiPercussionInstrument(MidiInstrument):
722
"""MIDI percussion instrument with drum methods."""
723
724
def __init__(self):
725
"""Create MIDI percussion instrument."""
726
727
def acoustic_bass_drum(self) -> Note:
728
"""Return acoustic bass drum note."""
729
730
def snare(self) -> Note:
731
"""Return snare drum note."""
732
733
def hi_hat(self) -> Note:
734
"""Return hi-hat note."""
735
736
def crash_cymbal(self) -> Note:
737
"""Return crash cymbal note."""
738
739
def ride_cymbal(self) -> Note:
740
"""Return ride cymbal note."""
741
742
def open_hi_hat(self) -> Note:
743
"""Return open hi-hat note."""
744
745
def closed_hi_hat(self) -> Note:
746
"""Return closed hi-hat note."""
747
748
# Additional percussion methods available...
749
```
750
751
## Usage Examples
752
753
### Creating and Manipulating Notes
754
755
```python
756
from mingus.containers import Note
757
758
# Create notes
759
note1 = Note("C", 4) # Middle C
760
note2 = Note("F#", 5, velocity=80, channel=1)
761
762
# Manipulate notes
763
note1.transpose("5") # Transpose up a fifth to G
764
note1.octave_up() # Move to octave 5
765
766
# Convert to frequency
767
freq = note1.to_hertz() # Get frequency in Hz
768
print(f"G5 frequency: {freq} Hz")
769
```
770
771
### Working with Chords
772
773
```python
774
from mingus.containers import NoteContainer
775
776
# Create chord from shorthand
777
chord = NoteContainer()
778
chord.from_chord("CM7") # C major 7th chord
779
780
# Analyze chord
781
chord_name = chord.determine() # Returns chord name
782
print(f"Chord: {chord_name}")
783
784
# Manipulate chord
785
chord.transpose("2") # Transpose up a whole step
786
chord.sort() # Sort notes by pitch
787
```
788
789
### Building Musical Structures
790
791
```python
792
from mingus.containers import Bar, Track, Composition
793
794
# Create a bar with notes
795
bar = Bar("C", (4, 4)) # 4/4 time in C major
796
bar.place_notes("C", 4) # Quarter note C
797
bar.place_notes(["E", "G"], 4) # Quarter note E and G together
798
bar.place_rest(2) # Half rest
799
800
# Create track and composition
801
track = Track()
802
track.add_bar(bar)
803
804
composition = Composition()
805
composition.add_track(track)
806
composition.set_title("Simple Melody")
807
```