pretty-midi 0.2.11__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Colin Raffel
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,25 @@
1
+ Metadata-Version: 2.1
2
+ Name: pretty_midi
3
+ Version: 0.2.11
4
+ Summary: Functions and classes for handling MIDI data conveniently.
5
+ Home-page: https://github.com/craffel/pretty-midi
6
+ Author: Colin Raffel
7
+ Author-email: craffel@gmail.com
8
+ License: MIT
9
+ Keywords: audio music midi mir
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Topic :: Multimedia :: Sound/Audio :: MIDI
15
+ License-File: LICENSE.txt
16
+ Requires-Dist: numpy>=1.7.0
17
+ Requires-Dist: mido>=1.1.16
18
+ Requires-Dist: six
19
+ Requires-Dist: importlib_resources
20
+ Provides-Extra: fluidsynth
21
+ Requires-Dist: pyfluidsynth>=1.3.1; extra == "fluidsynth"
22
+
23
+ Functions and classes which make handling MIDI data easy in Python.
24
+ Provides methods for parsing, modifying, and analyzing MIDI files.
25
+
@@ -0,0 +1,54 @@
1
+ `pretty_midi` contains utility function/classes for handling MIDI data, so that it's in a format which is easy to modify and extract information from.
2
+
3
+ Documentation is available [here](http://craffel.github.io/pretty-midi/). You can also find a Jupyter notebook tutorial [here](https://nbviewer.org/github/craffel/pretty-midi/blob/main/Tutorial.ipynb) (click [here](https://colab.research.google.com/github/craffel/pretty-midi/blob/main/Tutorial.ipynb) to load in Colab).
4
+
5
+ `pretty_midi` is available via [pip](https://pypi.python.org/pypi/pretty_midi) or via the [setup.py](https://github.com/craffel/pretty-midi/blob/master/setup.py) script. In order to synthesize MIDI data using fluidsynth, you need the [fluidsynth](http://www.fluidsynth.org/) program and [pyfluidsynth](https://pypi.python.org/pypi/pyfluidsynth).
6
+
7
+ If you end up using `pretty_midi` in a published research project, please cite the following report:
8
+
9
+ Colin Raffel and Daniel P. W. Ellis. [_Intuitive Analysis, Creation and Manipulation of MIDI Data with pretty_midi_](http://colinraffel.com/publications/ismir2014intuitive.pdf). In Proceedings of the 15th International Conference on Music Information Retrieval Late Breaking and Demo Papers, 2014.
10
+
11
+
12
+ Example usage for analyzing, manipulating and synthesizing a MIDI file:
13
+
14
+ ```python
15
+ import pretty_midi
16
+ # Load MIDI file into PrettyMIDI object
17
+ midi_data = pretty_midi.PrettyMIDI('example.mid')
18
+ # Print an empirical estimate of its global tempo
19
+ print(midi_data.estimate_tempo())
20
+ # Compute the relative amount of each semitone across the entire song, a proxy for key
21
+ total_velocity = sum(sum(midi_data.get_chroma()))
22
+ print([sum(semitone)/total_velocity for semitone in midi_data.get_chroma()])
23
+ # Shift all notes up by 5 semitones
24
+ for instrument in midi_data.instruments:
25
+ # Don't want to shift drum notes
26
+ if not instrument.is_drum:
27
+ for note in instrument.notes:
28
+ note.pitch += 5
29
+ # Synthesize the resulting MIDI data using sine waves
30
+ audio_data = midi_data.synthesize()
31
+ ```
32
+
33
+ Example usage for creating a simple MIDI file:
34
+
35
+ ```python
36
+ import pretty_midi
37
+ # Create a PrettyMIDI object
38
+ cello_c_chord = pretty_midi.PrettyMIDI()
39
+ # Create an Instrument instance for a cello instrument
40
+ cello_program = pretty_midi.instrument_name_to_program('Cello')
41
+ cello = pretty_midi.Instrument(program=cello_program)
42
+ # Iterate over note names, which will be converted to note number later
43
+ for note_name in ['C5', 'E5', 'G5']:
44
+ # Retrieve the MIDI note number for this note name
45
+ note_number = pretty_midi.note_name_to_number(note_name)
46
+ # Create a Note instance for this note, starting at 0s and ending at .5s
47
+ note = pretty_midi.Note(velocity=100, pitch=note_number, start=0, end=.5)
48
+ # Add it to our cello instrument
49
+ cello.notes.append(note)
50
+ # Add the cello instrument to the PrettyMIDI object
51
+ cello_c_chord.instruments.append(cello)
52
+ # Write out the MIDI data
53
+ cello_c_chord.write('cello-C-chord.mid')
54
+ ```
@@ -0,0 +1,151 @@
1
+ """
2
+ ``pretty_midi`` contains utility function/classes for handling MIDI data,
3
+ so that it's in a format from which it is easy to modify and extract
4
+ information.
5
+ If you end up using ``pretty_midi`` in a published research project, please
6
+ cite the following report:
7
+
8
+ Colin Raffel and Daniel P. W. Ellis.
9
+ `Intuitive Analysis, Creation and Manipulation of MIDI Data with pretty_midi
10
+ <http://colinraffel.com/publications/ismir2014intuitive.pdf>`_.
11
+ In 15th International Conference on Music Information Retrieval Late Breaking
12
+ and Demo Papers, 2014.
13
+
14
+ Example usage for analyzing, manipulating and synthesizing a MIDI file:
15
+
16
+ .. code-block:: python
17
+
18
+ import pretty_midi
19
+ # Load MIDI file into PrettyMIDI object
20
+ midi_data = pretty_midi.PrettyMIDI('example.mid')
21
+ # Print an empirical estimate of its global tempo
22
+ print midi_data.estimate_tempo()
23
+ # Compute the relative amount of each semitone across the entire song,
24
+ # a proxy for key
25
+ total_velocity = sum(sum(midi_data.get_chroma()))
26
+ print [sum(semitone)/total_velocity for semitone in midi_data.get_chroma()]
27
+ # Shift all notes up by 5 semitones
28
+ for instrument in midi_data.instruments:
29
+ # Don't want to shift drum notes
30
+ if not instrument.is_drum:
31
+ for note in instrument.notes:
32
+ note.pitch += 5
33
+ # Synthesize the resulting MIDI data using sine waves
34
+ audio_data = midi_data.synthesize()
35
+
36
+ Example usage for creating a simple MIDI file:
37
+
38
+ .. code-block:: python
39
+
40
+ import pretty_midi
41
+ # Create a PrettyMIDI object
42
+ cello_c_chord = pretty_midi.PrettyMIDI()
43
+ # Create an Instrument instance for a cello instrument
44
+ cello_program = pretty_midi.instrument_name_to_program('Cello')
45
+ cello = pretty_midi.Instrument(program=cello_program)
46
+ # Iterate over note names, which will be converted to note number later
47
+ for note_name in ['C5', 'E5', 'G5']:
48
+ # Retrieve the MIDI note number for this note name
49
+ note_number = pretty_midi.note_name_to_number(note_name)
50
+ # Create a Note instance, starting at 0s and ending at .5s
51
+ note = pretty_midi.Note(
52
+ velocity=100, pitch=note_number, start=0, end=.5)
53
+ # Add it to our cello instrument
54
+ cello.notes.append(note)
55
+ # Add the cello instrument to the PrettyMIDI object
56
+ cello_c_chord.instruments.append(cello)
57
+ # Write out the MIDI data
58
+ cello_c_chord.write('cello-C-chord.mid')
59
+
60
+ Further examples can be found in the source tree's `examples directory
61
+ <https://github.com/craffel/pretty-midi/tree/master/examples>`_.
62
+
63
+ ``pretty_midi.PrettyMIDI``
64
+ ==========================
65
+
66
+ .. autoclass:: PrettyMIDI
67
+ :members:
68
+ :undoc-members:
69
+
70
+ ``pretty_midi.Instrument``
71
+ ==========================
72
+
73
+ .. autoclass:: Instrument
74
+ :members:
75
+ :undoc-members:
76
+
77
+ ``pretty_midi.Note``
78
+ ====================
79
+
80
+ .. autoclass:: Note
81
+ :members:
82
+ :undoc-members:
83
+
84
+ ``pretty_midi.PitchBend``
85
+ =========================
86
+
87
+ .. autoclass:: PitchBend
88
+ :members:
89
+ :undoc-members:
90
+
91
+ ``pretty_midi.ControlChange``
92
+ =============================
93
+
94
+ .. autoclass:: ControlChange
95
+ :members:
96
+ :undoc-members:
97
+
98
+ ``pretty_midi.TimeSignature``
99
+ =============================
100
+
101
+ .. autoclass:: TimeSignature
102
+ :members:
103
+ :undoc-members:
104
+
105
+ ``pretty_midi.KeySignature``
106
+ ============================
107
+
108
+ .. autoclass:: KeySignature
109
+ :members:
110
+ :undoc-members:
111
+
112
+ ``pretty_midi.Lyric``
113
+ =====================
114
+
115
+ .. autoclass:: Lyric
116
+ :members:
117
+ :undoc-members:
118
+
119
+ ``pretty_midi.Text``
120
+ =====================
121
+
122
+ .. autoclass:: Text
123
+ :members:
124
+ :undoc-members:
125
+
126
+ Utility functions
127
+ =================
128
+ .. autofunction:: key_number_to_key_name
129
+ .. autofunction:: key_name_to_key_number
130
+ .. autofunction:: mode_accidentals_to_key_number
131
+ .. autofunction:: key_number_to_mode_accidentals
132
+ .. autofunction:: qpm_to_bpm
133
+ .. autofunction:: note_number_to_hz
134
+ .. autofunction:: hz_to_note_number
135
+ .. autofunction:: note_name_to_number
136
+ .. autofunction:: note_number_to_name
137
+ .. autofunction:: note_number_to_drum_name
138
+ .. autofunction:: drum_name_to_note_number
139
+ .. autofunction:: program_to_instrument_name
140
+ .. autofunction:: instrument_name_to_program
141
+ .. autofunction:: program_to_instrument_class
142
+ .. autofunction:: pitch_bend_to_semitones
143
+ .. autofunction:: semitones_to_pitch_bend
144
+ """
145
+ from .pretty_midi import *
146
+ from .instrument import *
147
+ from .containers import *
148
+ from .utilities import *
149
+ from .constants import *
150
+
151
+ __version__ = '0.2.11'
@@ -0,0 +1,72 @@
1
+ """This file defines MIDI standard constants, which are useful for converting
2
+ between numeric MIDI data values and human-readable text.
3
+
4
+ """
5
+
6
+ # INSTRUMENT_MAP[program_number] maps the program_number to an instrument name
7
+ INSTRUMENT_MAP = ['Acoustic Grand Piano', 'Bright Acoustic Piano',
8
+ 'Electric Grand Piano', 'Honky-tonk Piano',
9
+ 'Electric Piano 1', 'Electric Piano 2', 'Harpsichord',
10
+ 'Clavinet', 'Celesta', 'Glockenspiel', 'Music Box',
11
+ 'Vibraphone', 'Marimba', 'Xylophone', 'Tubular Bells',
12
+ 'Dulcimer', 'Drawbar Organ', 'Percussive Organ',
13
+ 'Rock Organ', 'Church Organ', 'Reed Organ', 'Accordion',
14
+ 'Harmonica', 'Tango Accordion', 'Acoustic Guitar (nylon)',
15
+ 'Acoustic Guitar (steel)', 'Electric Guitar (jazz)',
16
+ 'Electric Guitar (clean)', 'Electric Guitar (muted)',
17
+ 'Overdriven Guitar', 'Distortion Guitar',
18
+ 'Guitar Harmonics', 'Acoustic Bass',
19
+ 'Electric Bass (finger)', 'Electric Bass (pick)',
20
+ 'Fretless Bass', 'Slap Bass 1', 'Slap Bass 2',
21
+ 'Synth Bass 1', 'Synth Bass 2', 'Violin', 'Viola', 'Cello',
22
+ 'Contrabass', 'Tremolo Strings', 'Pizzicato Strings',
23
+ 'Orchestral Harp', 'Timpani', 'String Ensemble 1',
24
+ 'String Ensemble 2', 'Synth Strings 1', 'Synth Strings 2',
25
+ 'Choir Aahs', 'Voice Oohs', 'Synth Choir', 'Orchestra Hit',
26
+ 'Trumpet', 'Trombone', 'Tuba', 'Muted Trumpet',
27
+ 'French Horn', 'Brass Section', 'Synth Brass 1',
28
+ 'Synth Brass 2', 'Soprano Sax', 'Alto Sax', 'Tenor Sax',
29
+ 'Baritone Sax', 'Oboe', 'English Horn', 'Bassoon',
30
+ 'Clarinet', 'Piccolo', 'Flute', 'Recorder', 'Pan Flute',
31
+ 'Blown bottle', 'Shakuhachi', 'Whistle', 'Ocarina',
32
+ 'Lead 1 (square)', 'Lead 2 (sawtooth)',
33
+ 'Lead 3 (calliope)', 'Lead 4 chiff', 'Lead 5 (charang)',
34
+ 'Lead 6 (voice)', 'Lead 7 (fifths)',
35
+ 'Lead 8 (bass + lead)', 'Pad 1 (new age)', 'Pad 2 (warm)',
36
+ 'Pad 3 (polysynth)', 'Pad 4 (choir)', 'Pad 5 (bowed)',
37
+ 'Pad 6 (metallic)', 'Pad 7 (halo)', 'Pad 8 (sweep)',
38
+ 'FX 1 (rain)', 'FX 2 (soundtrack)', 'FX 3 (crystal)',
39
+ 'FX 4 (atmosphere)', 'FX 5 (brightness)', 'FX 6 (goblins)',
40
+ 'FX 7 (echoes)', 'FX 8 (sci-fi)', 'Sitar', 'Banjo',
41
+ 'Shamisen', 'Koto', 'Kalimba', 'Bagpipe', 'Fiddle',
42
+ 'Shanai', 'Tinkle Bell', 'Agogo', 'Steel Drums',
43
+ 'Woodblock', 'Taiko Drum', 'Melodic Tom', 'Synth Drum',
44
+ 'Reverse Cymbal', 'Guitar Fret Noise', 'Breath Noise',
45
+ 'Seashore', 'Bird Tweet', 'Telephone Ring', 'Helicopter',
46
+ 'Applause', 'Gunshot']
47
+
48
+ # INSTRUMENT_CLASSES contains the classes present in INSTRUMENTS
49
+ INSTRUMENT_CLASSES = ['Piano', 'Chromatic Percussion', 'Organ', 'Guitar',
50
+ 'Bass', 'Strings', 'Ensemble', 'Brass', 'Reed', 'Pipe',
51
+ 'Synth Lead', 'Synth Pad', 'Synth Effects', 'Ethnic',
52
+ 'Percussive',
53
+ 'Sound Effects']
54
+
55
+ # List which maps MIDI note number - 35 to drum name
56
+ # from http://www.midi.org/techspecs/gm1sound.php
57
+ DRUM_MAP = ['Acoustic Bass Drum', 'Bass Drum 1', 'Side Stick',
58
+ 'Acoustic Snare', 'Hand Clap', 'Electric Snare',
59
+ 'Low Floor Tom', 'Closed Hi Hat', 'High Floor Tom',
60
+ 'Pedal Hi Hat', 'Low Tom', 'Open Hi Hat',
61
+ 'Low-Mid Tom', 'Hi-Mid Tom', 'Crash Cymbal 1',
62
+ 'High Tom', 'Ride Cymbal 1', 'Chinese Cymbal',
63
+ 'Ride Bell', 'Tambourine', 'Splash Cymbal',
64
+ 'Cowbell', 'Crash Cymbal 2', 'Vibraslap',
65
+ 'Ride Cymbal 2', 'Hi Bongo', 'Low Bongo',
66
+ 'Mute Hi Conga', 'Open Hi Conga', 'Low Conga',
67
+ 'High Timbale', 'Low Timbale', 'High Agogo',
68
+ 'Low Agogo', 'Cabasa', 'Maracas',
69
+ 'Short Whistle', 'Long Whistle', 'Short Guiro',
70
+ 'Long Guiro', 'Claves', 'Hi Wood Block',
71
+ 'Low Wood Block', 'Mute Cuica', 'Open Cuica',
72
+ 'Mute Triangle', 'Open Triangle']
@@ -0,0 +1,226 @@
1
+ """These classes simply hold MIDI data in a convenient form.
2
+
3
+ """
4
+ from __future__ import print_function
5
+
6
+ from .utilities import key_number_to_key_name
7
+
8
+
9
+ class Note(object):
10
+ """A note event.
11
+
12
+ Parameters
13
+ ----------
14
+ velocity : int
15
+ Note velocity.
16
+ pitch : int
17
+ Note pitch, as a MIDI note number.
18
+ start : float
19
+ Note on time, absolute, in seconds.
20
+ end : float
21
+ Note off time, absolute, in seconds.
22
+
23
+ """
24
+
25
+ def __init__(self, velocity, pitch, start, end):
26
+ if end < start:
27
+ raise ValueError("Note end time must be greater than start time")
28
+
29
+ self.velocity = velocity
30
+ self.pitch = pitch
31
+ self.start = start
32
+ self.end = end
33
+
34
+ def get_duration(self):
35
+ """Get the duration of the note in seconds."""
36
+ return self.end - self.start
37
+
38
+ @property
39
+ def duration(self):
40
+ return self.get_duration()
41
+
42
+ def __repr__(self):
43
+ return 'Note(start={:f}, end={:f}, pitch={}, velocity={})'.format(
44
+ self.start, self.end, self.pitch, self.velocity)
45
+
46
+
47
+ class PitchBend(object):
48
+ """A pitch bend event.
49
+
50
+ Parameters
51
+ ----------
52
+ pitch : int
53
+ MIDI pitch bend amount, in the range ``[-8192, 8191]``.
54
+ time : float
55
+ Time where the pitch bend occurs.
56
+
57
+ """
58
+
59
+ def __init__(self, pitch, time):
60
+ self.pitch = pitch
61
+ self.time = time
62
+
63
+ def __repr__(self):
64
+ return 'PitchBend(pitch={:d}, time={:f})'.format(self.pitch, self.time)
65
+
66
+
67
+ class ControlChange(object):
68
+ """A control change event.
69
+
70
+ Parameters
71
+ ----------
72
+ number : int
73
+ The control change number, in ``[0, 127]``.
74
+ value : int
75
+ The value of the control change, in ``[0, 127]``.
76
+ time : float
77
+ Time where the control change occurs.
78
+
79
+ """
80
+
81
+ def __init__(self, number, value, time):
82
+ self.number = number
83
+ self.value = value
84
+ self.time = time
85
+
86
+ def __repr__(self):
87
+ return ('ControlChange(number={:d}, value={:d}, '
88
+ 'time={:f})'.format(self.number, self.value, self.time))
89
+
90
+
91
+ class TimeSignature(object):
92
+ """Container for a Time Signature event, which contains the time signature
93
+ numerator, denominator and the event time in seconds.
94
+
95
+ Attributes
96
+ ----------
97
+ numerator : int
98
+ Numerator of time signature.
99
+ denominator : int
100
+ Denominator of time signature.
101
+ time : float
102
+ Time of event in seconds.
103
+
104
+ Examples
105
+ --------
106
+ Instantiate a TimeSignature object with 6/8 time signature at 3.14 seconds:
107
+
108
+ >>> ts = TimeSignature(6, 8, 3.14)
109
+ >>> print(ts)
110
+ 6/8 at 3.14 seconds
111
+
112
+ """
113
+
114
+ def __init__(self, numerator, denominator, time):
115
+ if not (isinstance(numerator, int) and numerator > 0):
116
+ raise ValueError(
117
+ '{} is not a valid `numerator` type or value'.format(
118
+ numerator))
119
+ if not (isinstance(denominator, int) and denominator > 0):
120
+ raise ValueError(
121
+ '{} is not a valid `denominator` type or value'.format(
122
+ denominator))
123
+ if not (isinstance(time, (int, float)) and time >= 0):
124
+ raise ValueError(
125
+ '{} is not a valid `time` type or value'.format(time))
126
+
127
+ self.numerator = numerator
128
+ self.denominator = denominator
129
+ self.time = time
130
+
131
+ def __repr__(self):
132
+ return "TimeSignature(numerator={}, denominator={}, time={})".format(
133
+ self.numerator, self.denominator, self.time)
134
+
135
+ def __str__(self):
136
+ return '{}/{} at {:.2f} seconds'.format(
137
+ self.numerator, self.denominator, self.time)
138
+
139
+
140
+ class KeySignature(object):
141
+ """Contains the key signature and the event time in seconds.
142
+ Only supports major and minor keys.
143
+
144
+ Attributes
145
+ ----------
146
+ key_number : int
147
+ Key number according to ``[0, 11]`` Major, ``[12, 23]`` minor.
148
+ For example, 0 is C Major, 12 is C minor.
149
+ time : float
150
+ Time of event in seconds.
151
+
152
+ Examples
153
+ --------
154
+ Instantiate a C# minor KeySignature object at 3.14 seconds:
155
+
156
+ >>> ks = KeySignature(13, 3.14)
157
+ >>> print(ks)
158
+ C# minor at 3.14 seconds
159
+ """
160
+
161
+ def __init__(self, key_number, time):
162
+ if not all((isinstance(key_number, int),
163
+ key_number >= 0,
164
+ key_number < 24)):
165
+ raise ValueError(
166
+ '{} is not a valid `key_number` type or value'.format(
167
+ key_number))
168
+ if not (isinstance(time, (int, float)) and time >= 0):
169
+ raise ValueError(
170
+ '{} is not a valid `time` type or value'.format(time))
171
+
172
+ self.key_number = key_number
173
+ self.time = time
174
+
175
+ def __repr__(self):
176
+ return "KeySignature(key_number={}, time={})".format(
177
+ self.key_number, self.time)
178
+
179
+ def __str__(self):
180
+ return '{} at {:.2f} seconds'.format(
181
+ key_number_to_key_name(self.key_number), self.time)
182
+
183
+
184
+ class Lyric(object):
185
+ """Timestamped lyric text.
186
+
187
+ Attributes
188
+ ----------
189
+ text : str
190
+ The text of the lyric.
191
+ time : float
192
+ The time in seconds of the lyric.
193
+ """
194
+
195
+ def __init__(self, text, time):
196
+ self.text = text
197
+ self.time = time
198
+
199
+ def __repr__(self):
200
+ return 'Lyric(text="{}", time={})'.format(
201
+ self.text.replace('"', r'\"'), self.time)
202
+
203
+ def __str__(self):
204
+ return '"{}" at {:.2f} seconds'.format(self.text, self.time)
205
+
206
+ class Text(object):
207
+ """Timestamped text event.
208
+
209
+ Attributes
210
+ ----------
211
+ text : str
212
+ The text.
213
+ time : float
214
+ The time it occurs in seconds.
215
+ """
216
+
217
+ def __init__(self, text, time):
218
+ self.text = text
219
+ self.time = time
220
+
221
+ def __repr__(self):
222
+ return 'Text(text="{}", time={})'.format(
223
+ self.text.replace('"', r'\"'), self.time)
224
+
225
+ def __str__(self):
226
+ return '"{}" at {:.2f} seconds'.format(self.text, self.time)
@@ -0,0 +1,72 @@
1
+ """Utility functions for handling fluidsynth
2
+
3
+ """
4
+
5
+ import os
6
+ import importlib_resources
7
+
8
+ try:
9
+ import fluidsynth
10
+ _HAS_FLUIDSYNTH = True
11
+ except ImportError:
12
+ _HAS_FLUIDSYNTH = False
13
+
14
+ DEFAULT_SF2 = 'TimGM6mb.sf2'
15
+ DEFAULT_SAMPLE_RATE = 44100
16
+
17
+
18
+ def get_fluidsynth_instance(synthesizer=None, sfid=0, fs=None):
19
+ """ Check if a valid fluidsynth.Synth instance is provided, and if not,
20
+ create one.
21
+
22
+ Parameters
23
+ ----------
24
+ synthesizer : fluidsynth.Synth or str
25
+ fluidsynth.Synth instance to use or a string with the path to a .sf2 file.
26
+ Default ``None``, which creates a new instance using the TimGM6mb.sf2 file
27
+ included with ``pretty_midi``.
28
+ sfid : int
29
+ Soundfont ID to use if an instance of fluidsynth.Synth is provided.
30
+ Default ``0``, which uses the first soundfont.
31
+ fs : int
32
+ Sampling rate to synthesize at.
33
+ Default ``None``, which falls back to ``pretty_midi.fluidsynth.DEFAULT_SAMPLE_RATE``
34
+ = 44100 if a new instance must be created.
35
+ If ``synthesizer`` is an existing instance and ``fs`` is specified, then
36
+ ValueError will be raised if the sample rates are not equal.
37
+
38
+ Returns
39
+ -------
40
+ synthesizer : fluidsynth.Synth
41
+ fluidsynth.Synth instance
42
+ sfid : int
43
+ Soundfont ID
44
+ new_instance_created : bool
45
+ Whether a new instance of fluidsynth.Synth was created.
46
+
47
+ """
48
+ if not _HAS_FLUIDSYNTH:
49
+ raise ImportError("fluidsynth() was called but pyfluidsynth is not installed.")
50
+
51
+ if synthesizer is None:
52
+ synthesizer = os.path.join(str(importlib_resources.files(__name__)), DEFAULT_SF2)
53
+
54
+ # Create a fluidsynth instance if one wasn't provided
55
+ if isinstance(synthesizer, str):
56
+ sf2_path = synthesizer
57
+ if not os.path.exists(synthesizer):
58
+ raise ValueError("No soundfont file found at the supplied path {}".format(sf2_path))
59
+ fs = fs or DEFAULT_SAMPLE_RATE
60
+ synthesizer = fluidsynth.Synth(samplerate=fs)
61
+ sfid = synthesizer.sfload(sf2_path)
62
+ new_instance_created = True
63
+ elif isinstance(synthesizer, fluidsynth.Synth):
64
+ synth_fs = synthesizer.get_setting('synth.sample-rate')
65
+ if fs and synth_fs != fs:
66
+ raise ValueError(
67
+ f"synthesizer sample rate of {synth_fs} doesn't match provided fs of {fs}")
68
+ new_instance_created = False
69
+ else:
70
+ raise ValueError("synthesizer must be a str or a fluidsynth.Synth instance")
71
+
72
+ return synthesizer, sfid, new_instance_created