Melody class#

This class is used for making/working with melodies..

class thebeat.music.Melody(rhythm, pitch_names, octave=None, key=None, is_played=None, name=None)[source]#

A Melody object contains a both a rhythm and pitch information. It does not contain sound. However, the Melody can be synthesized and played or written to disk, for instance using the synthesize_and_play() method.

See the __init__() to learn how a Melody object is constructed, or use one of the different class methods, such as the generate_random_melody() method.

Most of the functions require you to install abjad. Please note that the current version of abjad requires Python 3.10. The last version that supported Python 3.8-3.9 is this one. The correct version will be installed automatically when you install thebeat with pip install thebeat[music_notation]. For more details, see https://thebeat.readthedocs.io/en/latest/installation.html.

__init__(rhythm, pitch_names, octave=None, key=None, is_played=None, name=None)[source]#
Parameters:
  • rhythm (Rhythm) – A Rhythm object.

  • pitch_names (ndarray[Any, dtype[str]] | list[str] | str) – An array or list containing note names. They can be in a variety of formats, such as "G4" for a G note in the fourth octave, or "g'", or simply G. The names are processed by abjad.pitch.NamedPitch. Follow the link to find examples of the different formats. Alternatively it can be a string, but only in the formats: 'CCGGC' or 'C4C4G4G4C4'.

  • key (Optional[str], default: None) – Optionally, you can provide a key. This is for instance used when plotting a Melody object.

  • is_played (Optional[list], default: None) – Optionally, you can indicate if you want rests in the Melody. Provide an array or list of booleans, for instance: [True, True, False, True] would mean a rest in place of the third event. The default is True for each event.

  • name (Optional[str], default: None) – Optionally, the Melody object can have a name. This is saved to the Melody.name attribute.

Examples

>>> r = thebeat.music.Rhythm.from_note_values([4, 4, 4, 4, 4, 4, 2])
>>> mel = Melody(r, 'CCGGAAG')
copy(deep=True)[source]#

Returns a copy of itself. See copy.copy() for more information.

Parameters:

deep (bool, default: True) – If True, a deep copy is returned. If False, a shallow copy is returned.

classmethod generate_random_melody(n_bars=1, beat_ms=500, time_signature=(4, 4), key='C', octave=4, n_rests=0, allowed_note_values=None, rng=None, name=None)[source]#

Generate a random rhythm as well as a melody, based on the given parameters. Internally, for the rhythm, the Rhythm.generate_random_rhythm() method is used. The melody is a random selection of pitch values based on the provided key and octave.

Parameters:
  • n_bars (int, default: 1) – The desired number of musical bars.

  • beat_ms (int, default: 500) – The value (in milliseconds) for the beat, i.e. the duration of a \(\frac{1}{4}\) th note if the lower number in the time signature is 4.

  • time_signature (tuple, default: (4, 4)) – A musical time signature, for instance: (4, 4). As a reminder: the upper number indicates how many beats there are in a bar. The lower number indicates the denominator of the value that indicates one beat. So, in (4, 8) time, a bar would be filled if we have four \(\frac{1}{8}\) th notes.

  • key (str, default: 'C') – The musical key used for randomly selecting the notes. Only major keys are supported for now.

  • octave (int, default: 4) – The musical octave. The default is concert pitch, i.e. 4.

  • n_rests (int, default: 0) – If desired, one can provide a number of rests to be inserted at random locations. These are placed after the random selection of note values.

  • allowed_note_values (list, default: None) – A list or array containing the denominators of the allowed note values. A note value of 2 means a half note, a note value of 4 means a quarternote etc. Defaults to [4, 8, 16].

  • rng (Generator, default: None) – A numpy.random.Generator object. If not supplied numpy.random.default_rng() is used.

  • name (Optional[str], default: None) – If desired, one can give the melody a name. This is for instance used when printing the rhythm, or when plotting the rhythm. It can always be retrieved and changed via Rhythm.name.

Return type:

Melody

Examples

>>> generator = np.random.default_rng(seed=123)
>>> m = Melody.generate_random_melody(rng=generator)
>>> print(m.note_values)
[16 16 16 16 16 16 16  8  8 16  8 16 16]
>>> print(m.pitch_names)
["a'", "g'", "c'", "c''", "d'", "e'", "d'", "e'", "d'", "e'", "b'", "f'", "c''"]
property note_values#

This property returns the denominators of the note values in this sequence, calculated from the inter-onset intervals (IOIs). A note value of 2 means a half note. A note value of 4 means a quarternote, etc. One triplet of three notes would be [12, 12, 12].

Caution

Please note that this function is basic (e.g. there is no support for dotted notes etc.). That’s beyond the scope of this package.

Examples

>>> r = thebeat.music.Rhythm([500, 1000, 250, 250], time_signature=(4, 4), beat_ms=500)
>>> m = Melody(r, pitch_names='CCGC')
>>> print(r.note_values)  
[4 2 8 8]
>>> r = thebeat.music.Rhythm([166.66666667, 166.66666667, 166.66666667, 500, 500, 500], beat_ms=500]  
>>> print(r.note_values)  
[12 12 12  4  4  4]
plot_melody(filepath=None, key=None, suppress_display=False, figsize=None, dpi=600)[source]#

Use this function to plot the melody in musical notes. It requires lilypond to be installed. See Rhythm.plot_rhythm() for installation instructions.

../../_images/plot_melody.png

An example of a melody plotted with this method.#

Parameters:
  • filepath (UnionType[PathLike, str, None], default: None) – Optionally, you can save the plot to a file. Supported file formats are only ‘.png’ and ‘.eps’. The desired file format will be selected based on what the filepath ends with.

  • key (Optional[str], default: None) – The musical key to plot in. Can differ from the key used to construct the Melody object. Say you want to emphasize the accidentals (sharp or flat note), you can choose to plot the melody in ‘C’. The default is to plot in the key that was used to construct the object.

  • suppress_display (bool, default: False) – If desired,you can choose to suppress displaying the plot in your IDE. This means that matplotlib.pyplot.show() is not called. This is useful when you just want to save the plot or use the returned matplotlib.figure.Figure and matplotlib.axes.Axes objects.

  • figsize (Optional[tuple[float, float]], default: None) – The figure size in inches, as a tuple of floats. This refers to the figsize argument in matplotlib.pyplot.figure().

  • dpi (int, default: 600) – The resolution of the plot in dots per inch.

Return type:

tuple[Figure, Axes]

Examples

>>> r = thebeat.music.Rhythm(iois=[250, 500, 250, 500], time_signature=(3, 4))
>>> m = Melody(r, 'CCGC')
>>> m.plot_melody()  
>>> m.plot_melody(filepath='mymelody.png', suppress_display=True)  
>>> fig, ax = m.plot_melody(key='C', suppress_display=True)  
synthesize_and_play(event_durations_ms=None, fs=48000, n_channels=1, amplitude=1.0, oscillator='sine', onramp_ms=0, offramp_ms=0, ramp_type='linear', metronome=False, metronome_amplitude=1.0)[source]#

Since Melody objects do not contain any sound information, you can use this method to first synthesize the sound, and subsequently have it played via the internally used sounddevice.play().

Note

Theoretically, four quarternotes played after each other constitute one long sound. This behaviour is the default here. However, in many cases it will probably be best to supply event_durations, which means the events are played in the rhythm of the melody (i.e. according to the inter-onset intervals of the rhythm), but using a supplied duration.

Parameters:
  • event_durations_ms (UnionType[list[int], ndarray[Any, dtype[int]], int, None], default: None) – Can be supplied as a single integer, which means that duration will be used for all events in the melody, or as an array of list containing individual durations for each event. That of course requires an array or list with a size equal to the number of notes in the melody.

  • fs (int, default: 48000) – The desired sampling frequency in hertz.

  • n_channels (int, default: 1) – The desired number of channels. Can be 1 (mono) or 2 (stereo).

  • amplitude (float, default: 1.0) – Factor with which sound is amplified. Values between 0 and 1 result in sounds that are less loud, values higher than 1 in louder sounds. Defaults to 1.0.

  • oscillator (str, default: 'sine') – The oscillator used for generating the sound. Either ‘sine’ (the default), ‘square’ or ‘sawtooth’.

  • onramp_ms (int, default: 0) – The sound’s ‘attack’ in milliseconds.

  • offramp_ms (int, default: 0) – The sound’s ‘decay’ in milliseconds.

  • ramp_type (str, default: 'linear') – The type of on- and offramp used. Either ‘linear’ (the default) or ‘raised-cosine’.

  • metronome (bool, default: False) – If True, a metronome sound is added for playback. It uses Melody.beat_ms as the inter-onset interval.

  • metronome_amplitude (float, default: 1.0) – If desired, when writing the object with a metronome sound you can adjust the metronome amplitude. A value between 0 and 1 means a less loud metronome, a value larger than 1 means a louder metronome sound.

Examples

>>> mel = Melody.generate_random_melody()
>>> mel.synthesize_and_play()  
>>> mel.synthesize_and_play(event_durations_ms=50)  
synthesize_and_return(event_durations_ms=None, fs=48000, n_channels=1, amplitude=1.0, oscillator='sine', onramp_ms=0, offramp_ms=0, ramp_type='linear', metronome=False, metronome_amplitude=1.0)[source]#

Since Melody objects do not contain any sound information, you can use this method to synthesize the sound. It returnes a tuple containing the sound samples as a NumPy 1-D array, and the sampling frequency.

Note

Theoretically, four quarternotes played after each other constitute one long sound. This behaviour is the default here. However, in many cases it will probably be best to supply event_durations, which means the events are played in the rhythm of the melody (i.e. according to the inter-onset intervals of the rhythm), but using a supplied duration.

Parameters:
  • event_durations_ms (UnionType[list[int], ndarray[Any, dtype[int]], int, None], default: None) – Can be supplied as a single integer, which means that duration will be used for all events in the melody, or as an array of list containing individual durations for each event. That of course requires an array or list with a size equal to the number of notes in the melody.

  • fs (int, default: 48000) – The desired sampling frequency in hertz.

  • n_channels (int, default: 1) – The desired number of channels. Can be 1 (mono) or 2 (stereo).

  • amplitude (float, default: 1.0) – Factor with which sound is amplified. Values between 0 and 1 result in sounds that are less loud, values higher than 1 in louder sounds. Defaults to 1.0.

  • oscillator (str, default: 'sine') – The oscillator used for generating the sound. Either ‘sine’ (the default), ‘square’ or ‘sawtooth’.

  • onramp_ms (int, default: 0) – The sound’s ‘attack’ in milliseconds.

  • offramp_ms (int, default: 0) – The sound’s ‘decay’ in milliseconds.

  • ramp_type (str, default: 'linear') – The type of on- and offramp_ms used. Either ‘linear’ (the default) or ‘raised-cosine’.

  • metronome (bool, default: False) – If True, a metronome sound is added to the samples. It uses Melody.beat_ms as the inter-onset interval.

  • metronome_amplitude (float, default: 1.0) – If desired, when synthesizing the object with a metronome sound you can adjust the metronome amplitude. A value between 0 and 1 means a less loud metronome, a value larger than 1 means a louder metronome sound.

Return type:

tuple[ndarray, int]

Examples

>>> mel = Melody.generate_random_melody()
>>> samples, fs = mel.synthesize_and_return()
synthesize_and_write(filepath, event_durations_ms=None, fs=48000, n_channels=1, amplitude=1.0, dtype=<class 'numpy.int16'>, oscillator='sine', onramp_ms=0, offramp_ms=0, ramp_type='linear', metronome=False, metronome_amplitude=1.0)[source]#

Since Melody objects do not contain any sound information, you can use this method to first synthesize the sound, and subsequently write it to disk as a wave file.

Note

Theoretically, four quarternotes played after each other constitute one long sound. This behaviour is the default here. However, in many cases it will probably be best to supply event_durations, which means the events are played in the rhythm of the melody (i.e. according to the inter-onset intervals of the rhythm), but using a supplied duration.

Parameters:
  • filepath (str | PathLike) – The output destination for the .wav file. Either pass e.g. a Path object, or a string. Of course be aware of OS-specific filepath conventions.

  • event_durations_ms (UnionType[list[int], ndarray[Any, dtype[int]], int, None], default: None) – Can be supplied as a single integer, which means that duration will be used for all events in the melody, or as an array of list containing individual durations for each event. That of course requires an array or list with a size equal to the number of notes in the melody.

  • fs (int, default: 48000) – The desired sampling frequency in hertz.

  • n_channels (int, default: 1) – The desired number of channels. Can be 1 (mono) or 2 (stereo).

  • amplitude (float, default: 1.0) – Factor with which sound is amplified. Values between 0 and 1 result in sounds that are less loud, values higher than 1 in louder sounds. Defaults to 1.0.

  • dtype (str | dtype, default: <class 'numpy.int16'>) – The desired data type for the output file. Defaults to np.int16. This means that the output file will be 16-bit PCM.

  • oscillator (str, default: 'sine') – The oscillator used for generating the sound. Either ‘sine’ (the default), ‘square’ or ‘sawtooth’.

  • onramp_ms (int, default: 0) – The sound’s ‘attack’ in milliseconds.

  • offramp_ms (int, default: 0) – The sound’s ‘decay’ in milliseconds.

  • ramp_type (str, default: 'linear') – The type of on- and offramp used. Either ‘linear’ (the default) or ‘raised-cosine’.

  • metronome (bool, default: False) – If True, a metronome sound is added to the output file. It uses Melody.beat_ms as the inter-onset interval.

  • metronome_amplitude (float, default: 1.0) – If desired, when playing the object with a metronome sound you can adjust the metronome amplitude. A value between 0 and 1 means a less loud metronome, a value larger than 1 means a louder metronome sound.

Examples

>>> mel = Melody.generate_random_melody()
>>> mel.synthesize_and_write(filepath='random_melody.wav')