Rainbow-Dreamer / musicpy

Musicpy is a music programming language in Python designed to write music in very handy syntax through music theory and algorithms.
https://musicpy.readthedocs.io/en/latest/
GNU Lesser General Public License v2.1
1.27k stars 121 forks source link

Question on using `build` and `interval` notation as pertains to `chord_analysis` #39

Closed akhilsadam closed 2 years ago

akhilsadam commented 2 years ago

I have a question regarding the interval description:

Starting from a JSON structure, I would like to convert to a musicpy piece and then do a chord analysis as follows :

import json as js
import musicpy as mp
json=js.loads("""[{
            "chd": "Cm7",
            "time": 0.5,
            "arp": 0.125,
            "start": 0,
            "inst": "Acoustic Grand Piano"
        },
        {
            "chd": "Dsus",
            "time": 0.5,
            "arp": 0.125,
            "start": 0.5,
            "inst": "Acoustic Grand Piano"
        },
        {
            "chd": "Caug7",
            "time": 0.5,
            "arp": 0.125,
            "start": 1,
            "inst": "Acoustic Grand Piano"
        },
        {
            "chd": "Dadd2",
            "time": 0.5,
            "arp": 0.125,
            "start": 1.5,
            "inst": "Acoustic Grand Piano"
        },
        {
            "chd": "Cm7",
            "time": 0.5,
            "arp": 0.125,
            "start": 2,
            "inst": "Acoustic Grand Piano"
        },
        {
            "chd": "Dsus",
            "time": 0.5,
            "arp": 0.125,
            "start": 2.5,
            "inst": "Acoustic Grand Piano"
        },
        {
            "chd": "Caug7",
            "time": 0.5,
            "arp": 0.125,
            "start": 3,
            "inst": "Acoustic Grand Piano"
        },
        {
            "chd": "D,G,A,A# / Dadd2",
            "time": 0.5,
            "arp": 0.25,
            "start": 3.5,
            "inst": "Acoustic Grand Piano"
        }
    ]""")
def convert_to_mp(chp : list, bpm : float=138, name: str='Progression 0') -> mp.piece:
    """Convert a chord-progression from a dict `chd` to a musicpy object

    Args:
        chp (list): chord progression
        bpm (float): BPM to play the song at.

    Returns:
        mp.piece: musicpy piece from chord progression
    """
    chord = [mp.C(i['chd']) % (i['time'], i['arp']) for i in chp]
    inst = [i['inst'] for i in chp]
    start = [i['start'] for i in chp]
    bpm, chdnotes, _ = mp.piece(chord,inst,bpm,start,['0']*len(chord)).merge()
    return mp.build(mp.track(chdnotes),bpm=bpm,name=name)

piece=convert_to_mp(chp=json, bpm=138, name='Progression 0')
mp.chord_analysis(piece.tracks[0])

But this returns ['Cmaj13#11 omit B, G sort as [1, 3, 2, 4, 5]']. I noticed that Track 0 never has any 0-intervals, which I see in other songs. (output from piece.tracks[0]):

[C4, D#4, G4, A#4, D4, G4, A4, C4, E4, G#4, A#4, D4, E4, F#4, A4, C4, D#4, G4, A#4, G4, C5, D5, G4, B4, D#5, F5, D4, E4, F#4, A4, D5, F#5, A5] with interval [0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.25, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.25, 0.125, 0.125, 0.125, 0.125, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.5]

Could you please explain what I am missing? Thank you so much!

Rainbow-Dreamer commented 2 years ago

For the line chord = [mp.C(i['chd']) % (i['time'], i['arp']) for i in chp], this sets the duration and interval for the chord, so I suggest the better naming for time and arp should be duration and interval. If you want to set the durations and intervals for each note in the chord, the value for duration and interval should both be a list, the lists contain the duration and interval for each note. If you set the value for duration and interval to be a float, which is in your case, this set all of the notes has the exactly same duration and interval to that float. So here is an example:

{
  "chd": "Cm7",
  "duration": [0.5, 0.5, 0.5, 0.5],
  "interval": [0.5, 0.5, 0, 0],
  "start": 0,
  "inst": "Acoustic Grand Piano"
}

In this example, the duration could also be just 0.5, which is equivalent. I hope this helps.

akhilsadam commented 2 years ago

Thank you. My question was more related to the chord_analysis function. So for the example I gave above, I only get a single chord when I run chord_analysis, is that expected behavior?

I assumed that I was not setting the intervals correctly, but by your clarification, that should not be the problem.

Thank you for the the naming suggestions; those names are much more informative.

Rainbow-Dreamer commented 2 years ago

I see, this is not the expected behavior, since you are writing a chord progression only, so you can set the parameter is_chord to True in the chord_analysis function, which will treat the input chord instance as chord only, instead of a whole piece (which means chords and melodies altogether), by default, the is_chord parameter is set to False, in this case, the function will firstly do a split melody and chord algorithm operation, then try to analysis the chord part. Here I have an example for you:

chord_progression1 = C('Cm7') | C('D') | C('E')
>>> chord_analysis(chord_progression1 , is_chord=True)
['Cm7', 'Dmajor', 'Emajor']
akhilsadam commented 2 years ago

That makes sense. Thank you so much!

Rainbow-Dreamer commented 2 years ago

You are welcome.