Not sure it is the right time, but I think I am going to try and play some of those progressions we looked at in the last post. Maybe more than one of the random ones. (I just need a bit of a change from sorting out musical concepts.)

But, I also want to mess around with playing multiple measures of the progression with varying durations for each chord. Not sure how small those durations should get, nor how many chords I should have in a measure. For now I will go with 4/4 time signature. And likely a tempo of 60 bpm. Amazing how many possibilities you can fool around with by doing a little reading.

Expect we are going to need a new function or three. Still working in my test/play module.

Sort Out Number of Chords and Duration Per Measure

I am thinking the first thing we perhaps need to sort out, for the number of chords in the progression, is the duration of each chord for one or more measures. I don’t believe we can always expect to or should play the full progression in a single measure. If the progression is 4 or less chords that is likely a reasonable thing to do. And, then we might also want to play the progression in a cyclical manner. I.E. forward followed by backward. But one step at a time.

I believe what we are looking at here is essentially determining the rhythm of the piece.

One of the reasons I am looking at all these chord generation concepts and code is because I eventually hope to combine chords with individual notes in the music the code generates. Not sure how or the best way to do so; but, I am still a long way from there. That makes me think that the chords themselves should not be of overly short duration. Perhaps 1/8s at the shortest for a tempo of 60 bpm. For individual notes, a duration as short as 1/16 should be fine. I also don’t think that in general the chords should be of equal durations. Though I expect in various musical genres that is the default for the bass clef.

And, I think (lots of thinking going on?) in any one measure the chord durations should not be too different in length. E.G. don’t think we want two 1/16s combined with a 1/2 and a 1/4 note. But you never know. For now I will stick with that assumption. Let’s see if we can code some of these ideas.

New Function

Okay, here’s my initial function signature. You know, I think this is going to be a lot tougher than I thought.

Not sure whether the returned list should be a single list or a list of lists. In the latter case, a sub-list for each measure. For now I am going with the former.

def make_chd_rhythm(cp_len:int, t_sig:tuple[int, int]=(4, 4), n_bars:int=3, tempo:int=60, retro:bool=False) -> list[float]:
  """ Generate a rhythm for the specified parameters.

    param cp_len: chord progression length,
      assumes progression does not end with the initial/root chord
    param t_sig: time signature, tuple(upper int, lower int)
    param n_bars: number of measures to generate
    param tempo: beats per minute
    param retro: do we want a retrograde progression of the original

    Return: a list of note durations in seconds
  """
  # for now I am going to ignore the retro paramater
  cp_rhythm = []
  
  return cp_rhythm

Let’s start with the defaults and see what we can do for differing progression lengths. So, added some code to get a set of initial note durations. My first bit of code ended with the first two print statements. Added the rest a day or so later.

Later, while continuing to work on this puzzle, I realized I needed to move the code generating a random sequence of notes into its own function. The following represents that later refactoring.

def make_chd_rhythm(cp_len:int, t_sig:tuple[int, int]=(4, 4), n_bars:int=3, tempo:int=60, retro:bool=False) -> list[float]:
... ....
  # for now I am going to ignore the retro paramater
  cp_rhythm = []
  m_nt = t_sig[0]               # number of notes/beats per measure
  b_nt = f"1/{t_sig[1]}"        # base note type
  m_full = (cp_len % m_nt) == 0 # does the progression use a number of full measures
  s_bt = 1/tempo * 60           # seconds per beat
  p_dur = cp_len * s_bt         # progression duration at 1 beat per chord
  
  # debug
  print(f"make_chd_rhythm({cp_len}, t_sig={t_sig}, n_bars={n_bars}, tempo={tempo}, retro={retro})")
  print(f"\tm_nt: {m_nt}, b_nt: {b_nt} ({s_bt} sec per base note type), m_full: {m_full}, m_dur: {m_dur}, p_dur: {p_dur}")

  m_nts, t_dur = make_bar(cp_len, t_sig=t_sig, s_bt=s_bt, nt_use=4)
  print(f"\t{m_nts} = {t_dur} seconds")

  return cp_rhythm


def make_bar(cp_len:int, t_sig:tuple[int, int]=(4, 4), s_bt:float=1.0, nt_use:int=4) -> tuple[list, float]:
  # note durations and probability of selection
  n_dur = {"whl": t_sig[1]*s_bt, "hlf": t_sig[1]*s_bt/2, "qtr": t_sig[1]*s_bt/4,
           "8th": t_sig[1]*s_bt/8, "16th": t_sig[1]*s_bt/16, "32nd": t_sig[1]*s_bt/32}
  match t_sig[1]:
    case 4:
      n_prb = [1, 2, 6, 4, 1, 1]
    case 2:
      n_prb = [1, 5, 5, 1, 1, 1]
    case 8:
      n_prb = [1, 1, 2, 6, 4, 2]
    case _:
      n_prb = [1, 2, 6, 5, 1, 1]

  # how many measures for the progression?
  # only want to have 8th as smallest note
  t_avl = np.array(list(n_dur.keys())[:nt_use])
  t_prb = np.array(n_prb[:nt_use])
  sum_prb = sum(t_prb)
  t_prb = t_prb / sum_prb
  nts_t = rng.choice(t_avl, cp_len, p=t_prb)
  t_dur = 0
  for nt in nts_t:
    t_dur += n_dur[nt]

  return nts_t, t_dur

A wee test.

  if do_tst_get_rhythm:
    print(f"test generating random measures and chord durations for given parameters")
    
    cp_dur = make_chd_rhythm(4, t_sig=(4, 4), n_bars=4, tempo=60, retro=False)
    cp_dur = make_chd_rhythm(5, t_sig=(4, 4), n_bars=4, tempo=60, retro=False)
    print()
    cp_dur = make_chd_rhythm(3, t_sig=(3, 4), n_bars=4, tempo=60, retro=False)
    cp_dur = make_chd_rhythm(4, t_sig=(3, 4), n_bars=4, tempo=60, retro=False)
    print()
    cp_dur = make_chd_rhythm(3, t_sig=(4, 2), n_bars=4, tempo=60, retro=False)
    cp_dur = make_chd_rhythm(5, t_sig=(4, 2), n_bars=4, tempo=60, retro=False)

And, in the terminal the following was displayed for a couple of executions.

(base) PS R:\learn\e_music> uv run tst_chords.py
chord progression with overtones on each chord
test generating random measures and chord durations for given parameters
make_chd_rhythm(4, t_sig=(4, 4), n_bars=4, tempo=60, retro=False)
        m_nt: 4, b_nt: 1/4 (1.0 sec per base note type), m_full: True, m_dur: 4.0, p_dur: 4.0
        ['8th' 'hlf' 'qtr' 'qtr'] = 4.5 seconds
make_chd_rhythm(5, t_sig=(4, 4), n_bars=4, tempo=60, retro=False)
        m_nt: 4, b_nt: 1/4 (1.0 sec per base note type), m_full: False, m_dur: 4.0, p_dur: 5.0
        ['8th' '8th' '8th' 'qtr' 'qtr'] = 3.5 seconds

make_chd_rhythm(3, t_sig=(3, 4), n_bars=4, tempo=60, retro=False)
        m_nt: 3, b_nt: 1/4 (1.0 sec per base note type), m_full: True, m_dur: 3.0, p_dur: 3.0
        ['8th' 'qtr' 'qtr'] = 2.5 seconds
make_chd_rhythm(4, t_sig=(3, 4), n_bars=4, tempo=60, retro=False)
        m_nt: 3, b_nt: 1/4 (1.0 sec per base note type), m_full: False, m_dur: 3.0, p_dur: 4.0
        ['qtr' 'hlf' 'hlf' 'qtr'] = 6.0 seconds

make_chd_rhythm(3, t_sig=(4, 2), n_bars=4, tempo=60, retro=False)
        m_nt: 4, b_nt: 1/2 (1.0 sec per base note type), m_full: False, m_dur: 4.0, p_dur: 3.0
        ['qtr' 'qtr' 'qtr'] = 1.5 seconds
make_chd_rhythm(5, t_sig=(4, 2), n_bars=4, tempo=60, retro=False)
        m_nt: 4, b_nt: 1/2 (1.0 sec per base note type), m_full: False, m_dur: 4.0, p_dur: 5.0
        ['hlf' 'qtr' 'qtr' 'qtr' 'hlf'] = 3.5 seconds

(base) PS R:\learn\e_music> uv run tst_chords.py
chord progression with overtones on each chord
test generating random measures and chord durations for given parameters
make_chd_rhythm(4, t_sig=(4, 4), n_bars=4, tempo=60, retro=False)
        m_nt: 4, b_nt: 1/4 (1.0 sec per base note type), m_full: True, m_dur: 4.0, p_dur: 4.0
        ['8th' 'hlf' 'qtr' '8th'] = 4.0 seconds
make_chd_rhythm(5, t_sig=(4, 4), n_bars=4, tempo=60, retro=False)
        m_nt: 4, b_nt: 1/4 (1.0 sec per base note type), m_full: False, m_dur: 4.0, p_dur: 5.0
        ['8th' '8th' 'qtr' 'qtr' 'qtr'] = 4.0 seconds

make_chd_rhythm(3, t_sig=(3, 4), n_bars=4, tempo=60, retro=False)
        m_nt: 3, b_nt: 1/4 (1.0 sec per base note type), m_full: True, m_dur: 3.0, p_dur: 3.0
        ['qtr' 'qtr' 'qtr'] = 3.0 seconds
make_chd_rhythm(4, t_sig=(3, 4), n_bars=4, tempo=60, retro=False)
        m_nt: 3, b_nt: 1/4 (1.0 sec per base note type), m_full: False, m_dur: 3.0, p_dur: 4.0
        ['8th' 'whl' 'hlf' 'qtr'] = 7.5 seconds

make_chd_rhythm(3, t_sig=(4, 2), n_bars=4, tempo=60, retro=False)
        m_nt: 4, b_nt: 1/2 (1.0 sec per base note type), m_full: False, m_dur: 4.0, p_dur: 3.0
        ['whl' 'qtr' 'hlf'] = 3.5 seconds
make_chd_rhythm(5, t_sig=(4, 2), n_bars=4, tempo=60, retro=False)
        m_nt: 4, b_nt: 1/2 (1.0 sec per base note type), m_full: False, m_dur: 4.0, p_dur: 5.0
        ['qtr' 'hlf' 'hlf' 'hlf' 'hlf'] = 4.5 seconds

Now what? I ran the current function a few more times. Here’s the critical output for all the runs (including those above).

# 4/4 with 4 chord progression
  ['8th' 'hlf' 'qtr' 'qtr'] = 4.5 seconds
  ['8th' 'hlf' 'qtr' '8th'] = 4.0 seconds
  ['8th' 'qtr' '8th' 'qtr'] = 3.0 seconds
  ['qtr' 'whl' 'qtr' '8th'] = 6.5 seconds
  ['qtr' 'qtr' 'hlf' '8th'] = 4.5 seconds
  ['qtr' 'qtr' 'qtr' 'qtr'] = 4.0 seconds
  ['8th' '8th' '8th' '8th'] = 2.0 seconds

# 4/4 with 5 chord progression
  ['8th' '8th' '8th' 'qtr' 'qtr'] = 3.5 seconds
  ['8th' '8th' 'qtr' 'qtr' 'qtr'] = 4.0 seconds
  ['8th' 'qtr' 'qtr' '8th' '8th'] = 3.5 seconds
  ['qtr' '8th' '8th' 'qtr' 'qtr'] = 4.0 seconds
  ['whl' '8th' 'qtr' '8th' 'qtr'] = 7.0 seconds
  ['8th' 'hlf' '8th' 'qtr' 'qtr'] = 5.0 seconds
  ['qtr' 'qtr' 'qtr' 'whl' 'hlf'] = 9.0 seconds

# 3/4 with 3 chord progression
  ['8th' 'qtr' 'qtr'] = 2.5 seconds
  ['qtr' 'qtr' 'qtr'] = 3.0 seconds
  ['qtr' 'whl' 'qtr'] = 6.0 seconds
  ['qtr' '8th' '8th'] = 2.0 seconds

# 3/4 with 4 chord progression
  ['qtr' 'hlf' 'hlf' 'qtr'] = 6.0 seconds
  ['8th' 'whl' 'hlf' 'qtr'] = 7.5 seconds
  ['whl' 'hlf' '8th' 'qtr'] = 7.5 seconds
  ['qtr' 'hlf' 'qtr' 'qtr'] = 5.0 seconds

# 4/2 with 3 chord progression
  ['qtr' 'qtr' 'qtr'] = 1.5 seconds
  ['whl' 'qtr' 'hlf'] = 3.5 seconds
  ['qtr' '8th' 'hlf'] = 1.75 seconds
  ['qtr' 'hlf' 'qtr'] = 2.0 seconds

# 4/2 with 5 chord progression
  ['hlf' 'qtr' 'qtr' 'qtr' 'hlf'] = 3.5 seconds
  ['qtr' 'hlf' 'hlf' 'hlf' 'hlf'] = 4.5 seconds
  ['qtr' 'hlf' 'hlf' 'qtr' 'qtr'] = 3.5 seconds
  ['qtr' 'qtr' 'hlf' '8th' 'hlf'] = 3.25 seconds

This is going to get truly crazy. Wish I remembered more of the math I learned/studied. I have for now decided that if I am no greater than 1 beat under or over the beats per measure, I will attempt a fix. Otherwise, I will generate another set of note durations.

Change of Plan (Refactor)

However as I continued on working on the above and trying to figure out how I would fix bars of improper lengths, I realized this approach was nuts. So a bit of a refactoring. make_bar() has been rewritten to always return a set of note lengths that are a multiple of the desired bar length. So the supplied note values might cause the progression to be played over 1, 2, or 3 bars. The make_chd_rhythm() function will ensure that the returned chord rhythm is always of the specified number of bars.

While working on the prior approach, I added a new class. Don’t know that it is still needed, but I have kept it.

Here’s the new class and the refactored functions.

... ...
class Note_durations():
  def __init__(self, t_sig:tuple[int, int]=(4, 4), s_bt:float=1.0):
    self.n_dur = {"whl": t_sig[1]*s_bt, "hlf": t_sig[1]*s_bt/2, "qtr": t_sig[1]*s_bt/4,
          "8th": t_sig[1]*s_bt/8, "16th": t_sig[1]*s_bt/16, "32nd": t_sig[1]*s_bt/32}
    self.dur2nt = {v: k for k, v in self.n_dur.items()}
    self.n_dbl = {"whl": "dbl", "hlf": "whl", "qtr": "hlf", "8th": "qtr", "16th": "8th", "32nd": "16th"}
... ...
def make_chd_rhythm(cp_len:int, t_sig:tuple[int, int]=(4, 4), n_bars:int=3, tempo:int=60, retro:bool=False) -> list[float]:
  """ Generate a rhythm for the specified parameters.

    param cp_len: chord progression length,
      assumes progression does not end with the initial/root chord
    param t_sig: time signature, tuple(upper int, lower int)
    param n_bars: number of measures to generate
    param tempo: beats per minute
    param retro: do we want a retrograde progression of the original

    Return: a list of note durations in seconds
  """
  # for now I am going to ignore the retro paramater
  cp_rhythm = []
  cp_dur = 0
  m_nt = t_sig[0]               # number of notes/beats per measure
  b_nt = f"1/{t_sig[1]}"        # base note type
  s_bt = 1/tempo * 60           # seconds per beat
  m_dur = t_sig[0] * s_bt       # seconds per measure
  rqst_dur = m_dur * n_bars     # total duration of the requested number of bars            
  
  # debug/dev
  print(f"make_chd_rhythm({cp_len}, t_sig={t_sig}, n_bars={n_bars}, tempo={tempo}, retro={retro})")
  print(f"\tm_nt: {m_nt}, b_nt: {b_nt} ({s_bt} sec per base note type), m_dur: {m_dur}")

  while cp_dur < rqst_dur:
    m_nts, t_dur = make_bar(cp_len, t_sig=t_sig, s_bt=s_bt, nt_use=4)
    
    # don't exceed requested number of bars
    if (cp_dur + t_dur) <= rqst_dur:
      cp_rhythm.append(m_nts.tolist())
      cp_dur += t_dur

  return cp_rhythm


def make_bar(cp_len:int, t_sig:tuple[int, int]=(4, 4), s_bt:float=1.0, nt_use:int=4) -> tuple[list[str], float]:
  """
    Generate a set of note values to for a sequence of chords of cp_len length.
    The list of note values may in fact have a duration of 1 or more full bars.

    param cp_len: chord progression length,
      assumes progression does not end with the initial/root chord
    param t_sig: time signature, tuple(upper int, lower int)
    param s_bt: number of seconds per beat
    param nt_use: number of note durations to use from list of available durations

    Return: a list of note values and total duration of sequence of note values
  """
  n_durs = Note_durations(t_sig, s_bt)
  # set probability of selecting note values depending on base note value
  match t_sig[1]:
    case 4:
      n_prb = [1, 2, 6, 4, 1, 1]
    case 2:
      n_prb = [1, 5, 5, 2, 1, 1]
    case 8:
      n_prb = [1, 1, 2, 6, 4, 2]
    case _:
      n_prb = [1, 2, 6, 5, 1, 1]

  # debug/dev
  print(f"\nmake_bar(cp_len, t_sig={t_sig}, s_bt={s_bt}, nt_use={nt_use})")
  m_dur = t_sig[0] * s_bt
  # select allowed note values
  t_avl = np.array(list(n_durs.n_dur.keys())[:nt_use])
  # convert probablilities to sereies floats summing to 1.0
  t_prb = np.array(n_prb[:nt_use])
  sum_prb = sum(t_prb)
  t_prb = t_prb / sum_prb

  while True:
    # keep sorting note values until we have a suitable duration
    nts_t = rng.choice(t_avl, cp_len-1, p=t_prb)
    t_dur = 0
    for nt in nts_t:
      t_dur += n_durs.n_dur[nt]
    # are we generating more than 1 bar?
    t_bars = (t_dur // m_dur) + 1
    f_n_dur = (m_dur * t_bars) - t_dur
    if f_n_dur not in n_durs.dur2nt.keys():
      continue
    nts_t = np.append(nts_t, n_durs.dur2nt[f_n_dur])
    t_dur += f_n_dur
    break

  # debug/dev
  print(f"\tnts_t -> {nts_t} ({t_dur})")

  return nts_t, t_dur

And a small test.

... ...
  if do_tst_get_rhythm:
    print(f"test generating random measures and chord durations for given parameters")
    print()
    cp_full = make_chd_rhythm(4, t_sig=(4, 4), n_bars=6, tempo=60, retro=False)
    print(f"\n\t{cp_full}\n")
    cp_full= make_chd_rhythm(5, t_sig=(4, 4), n_bars=6, tempo=60, retro=False)
    print(f"\n\t{cp_full}\n")
    print()
    cp_full = make_chd_rhythm(3, t_sig=(3, 4), n_bars=6, tempo=60, retro=False)
    print(f"\n\t{cp_full}\n")
    cp_full = make_chd_rhythm(4, t_sig=(3, 4), n_bars=6, tempo=60, retro=False)
    print(f"\n\t{cp_full}\n")
    print()
    cp_full = make_chd_rhythm(3, t_sig=(4, 2), n_bars=6, tempo=60, retro=False)
    print(f"\n\t{cp_full}\n")
    cp_full = make_chd_rhythm(5, t_sig=(4, 2), n_bars=6, tempo=60, retro=False)
    print(f"\n\t{cp_full}\n")

In the terminal, the test code output the following. A lot of info, but, I figured I’d show it all anyway. And, with a touch of effort you can see where a returned sequence from make_bar() was rejected by make_chd_rhythm(). As well as those cases where the progression was spread over more than 1 bar. I have not highlighted those cases.

(base) PS R:\learn\e_music> uv run tst_chords.py
chord progression with overtones on each chord
test generating random measures and chord durations for given parameters

make_chd_rhythm(4, t_sig=(4, 4), n_bars=6, tempo=60, retro=False)
        m_nt: 4, b_nt: 1/4 (1.0 sec per base note type), m_dur: 4.0
        cp_dur / rqst_dur: 0 / 24.0

make_bar(cp_len, t_sig=(4, 4), s_bt=1.0, nt_use=4)
        nts_t -> ['qtr' 'qtr' 'qtr' 'qtr'] (4.0)
        cp_dur / rqst_dur: 4.0 / 24.0

make_bar(cp_len, t_sig=(4, 4), s_bt=1.0, nt_use=4)
        nts_t -> ['qtr' '8th' 'hlf' '8th'] (4.0)
        cp_dur / rqst_dur: 8.0 / 24.0

make_bar(cp_len, t_sig=(4, 4), s_bt=1.0, nt_use=4)
        nts_t -> ['qtr' 'qtr' 'hlf' 'whl'] (8.0)
        cp_dur / rqst_dur: 16.0 / 24.0

make_bar(cp_len, t_sig=(4, 4), s_bt=1.0, nt_use=4)
        nts_t -> ['qtr' 'qtr' 'qtr' 'qtr'] (4.0)
        cp_dur / rqst_dur: 20.0 / 24.0

make_bar(cp_len, t_sig=(4, 4), s_bt=1.0, nt_use=4)
        nts_t -> ['hlf' 'qtr' 'qtr' 'whl'] (8.0)
        cp_dur / rqst_dur: 20.0 / 24.0

make_bar(cp_len, t_sig=(4, 4), s_bt=1.0, nt_use=4)
        nts_t -> ['qtr' 'qtr' 'qtr' 'qtr'] (4.0)

        [['qtr', 'qtr', 'qtr', 'qtr'], ['qtr', '8th', 'hlf', '8th'], ['qtr', 'qtr', 'hlf', 'whl'],
         ['qtr', 'qtr', 'qtr', 'qtr'], ['qtr', 'qtr', 'qtr', 'qtr']]

make_chd_rhythm(5, t_sig=(4, 4), n_bars=6, tempo=60, retro=False)
        m_nt: 4, b_nt: 1/4 (1.0 sec per base note type), m_dur: 4.0
        cp_dur / rqst_dur: 0 / 24.0

make_bar(cp_len, t_sig=(4, 4), s_bt=1.0, nt_use=4)
        nts_t -> ['qtr' 'qtr' 'whl' 'qtr' 'qtr'] (8.0)
        cp_dur / rqst_dur: 8.0 / 24.0

make_bar(cp_len, t_sig=(4, 4), s_bt=1.0, nt_use=4)
        nts_t -> ['qtr' 'qtr' '8th' '8th' 'qtr'] (4.0)
        cp_dur / rqst_dur: 12.0 / 24.0

make_bar(cp_len, t_sig=(4, 4), s_bt=1.0, nt_use=4)
        nts_t -> ['8th' 'qtr' '8th' 'hlf' 'whl'] (8.0)
        cp_dur / rqst_dur: 20.0 / 24.0

make_bar(cp_len, t_sig=(4, 4), s_bt=1.0, nt_use=4)
        nts_t -> ['qtr' 'hlf' 'whl' 'qtr' 'whl'] (12.0)
        cp_dur / rqst_dur: 20.0 / 24.0

make_bar(cp_len, t_sig=(4, 4), s_bt=1.0, nt_use=4)
        nts_t -> ['whl' 'qtr' 'qtr' 'qtr' 'qtr'] (8.0)
        cp_dur / rqst_dur: 20.0 / 24.0

make_bar(cp_len, t_sig=(4, 4), s_bt=1.0, nt_use=4)
        nts_t -> ['qtr' '8th' 'qtr' '8th' 'qtr'] (4.0)

        [['qtr', 'qtr', 'whl', 'qtr', 'qtr'], ['qtr', 'qtr', '8th', '8th', 'qtr'],
         ['8th', 'qtr', '8th', 'hlf', 'whl'], ['qtr', '8th', 'qtr', '8th', 'qtr']]


make_chd_rhythm(3, t_sig=(3, 4), n_bars=6, tempo=60, retro=False)
        m_nt: 3, b_nt: 1/4 (1.0 sec per base note type), m_dur: 3.0
        cp_dur / rqst_dur: 0 / 18.0

make_bar(cp_len, t_sig=(3, 4), s_bt=1.0, nt_use=4)
        nts_t -> ['hlf' 'hlf' 'hlf'] (6.0)
        cp_dur / rqst_dur: 6.0 / 18.0

make_bar(cp_len, t_sig=(3, 4), s_bt=1.0, nt_use=4)
        nts_t -> ['hlf' '8th' '8th'] (3.0)
        cp_dur / rqst_dur: 9.0 / 18.0

make_bar(cp_len, t_sig=(3, 4), s_bt=1.0, nt_use=4)
        nts_t -> ['qtr' 'qtr' 'qtr'] (3.0)
        cp_dur / rqst_dur: 12.0 / 18.0

make_bar(cp_len, t_sig=(3, 4), s_bt=1.0, nt_use=4)
        nts_t -> ['qtr' 'qtr' 'qtr'] (3.0)
        cp_dur / rqst_dur: 15.0 / 18.0

make_bar(cp_len, t_sig=(3, 4), s_bt=1.0, nt_use=4)
        nts_t -> ['qtr' 'whl' 'qtr'] (6.0)
        cp_dur / rqst_dur: 15.0 / 18.0

make_bar(cp_len, t_sig=(3, 4), s_bt=1.0, nt_use=4)
        nts_t -> ['qtr' 'whl' 'qtr'] (6.0)
        cp_dur / rqst_dur: 15.0 / 18.0

make_bar(cp_len, t_sig=(3, 4), s_bt=1.0, nt_use=4)
        nts_t -> ['whl' 'qtr' 'qtr'] (6.0)
        cp_dur / rqst_dur: 15.0 / 18.0

make_bar(cp_len, t_sig=(3, 4), s_bt=1.0, nt_use=4)
        nts_t -> ['hlf' 'hlf' 'hlf'] (6.0)
        cp_dur / rqst_dur: 15.0 / 18.0

make_bar(cp_len, t_sig=(3, 4), s_bt=1.0, nt_use=4)
        nts_t -> ['qtr' 'qtr' 'qtr'] (3.0)

        [['hlf', 'hlf', 'hlf'], ['hlf', '8th', '8th'], ['qtr', 'qtr', 'qtr'],
         ['qtr', 'qtr', 'qtr'], ['qtr', 'qtr', 'qtr']]

make_chd_rhythm(4, t_sig=(3, 4), n_bars=6, tempo=60, retro=False)
        m_nt: 3, b_nt: 1/4 (1.0 sec per base note type), m_dur: 3.0
        cp_dur / rqst_dur: 0 / 18.0

make_bar(cp_len, t_sig=(3, 4), s_bt=1.0, nt_use=4)
        nts_t -> ['qtr' 'hlf' 'hlf' 'qtr'] (6.0)
        cp_dur / rqst_dur: 6.0 / 18.0

make_bar(cp_len, t_sig=(3, 4), s_bt=1.0, nt_use=4)
        nts_t -> ['qtr' 'qtr' '8th' '8th'] (3.0)
        cp_dur / rqst_dur: 9.0 / 18.0

make_bar(cp_len, t_sig=(3, 4), s_bt=1.0, nt_use=4)
        nts_t -> ['qtr' '8th' '8th' 'qtr'] (3.0)
        cp_dur / rqst_dur: 12.0 / 18.0

make_bar(cp_len, t_sig=(3, 4), s_bt=1.0, nt_use=4)
        nts_t -> ['qtr' 'qtr' 'hlf' 'hlf'] (6.0)

        [['qtr', 'hlf', 'hlf', 'qtr'], ['qtr', 'qtr', '8th', '8th'],
         ['qtr', '8th', '8th', 'qtr'], ['qtr', 'qtr', 'hlf', 'hlf']]


make_chd_rhythm(3, t_sig=(4, 2), n_bars=6, tempo=60, retro=False)
        m_nt: 4, b_nt: 1/2 (1.0 sec per base note type), m_dur: 4.0
        cp_dur / rqst_dur: 0 / 24.0

make_bar(cp_len, t_sig=(4, 2), s_bt=1.0, nt_use=4)
        nts_t -> ['hlf' 'hlf' 'whl'] (4.0)
        cp_dur / rqst_dur: 4.0 / 24.0

make_bar(cp_len, t_sig=(4, 2), s_bt=1.0, nt_use=4)
        nts_t -> ['whl' 'hlf' 'hlf'] (4.0)
        cp_dur / rqst_dur: 8.0 / 24.0

make_bar(cp_len, t_sig=(4, 2), s_bt=1.0, nt_use=4)
        nts_t -> ['whl' 'hlf' 'hlf'] (4.0)
        cp_dur / rqst_dur: 12.0 / 24.0

make_bar(cp_len, t_sig=(4, 2), s_bt=1.0, nt_use=4)
        nts_t -> ['hlf' 'whl' 'hlf'] (4.0)
        cp_dur / rqst_dur: 16.0 / 24.0

make_bar(cp_len, t_sig=(4, 2), s_bt=1.0, nt_use=4)
        nts_t -> ['hlf' 'hlf' 'whl'] (4.0)
        cp_dur / rqst_dur: 20.0 / 24.0

make_bar(cp_len, t_sig=(4, 2), s_bt=1.0, nt_use=4)
        nts_t -> ['hlf' 'hlf' 'whl'] (4.0)

        [['hlf', 'hlf', 'whl'], ['whl', 'hlf', 'hlf'], ['whl', 'hlf', 'hlf'],
         ['hlf', 'whl', 'hlf'], ['hlf', 'hlf', 'whl'], ['hlf', 'hlf', 'whl']]

make_chd_rhythm(5, t_sig=(4, 2), n_bars=6, tempo=60, retro=False)
        m_nt: 4, b_nt: 1/2 (1.0 sec per base note type), m_dur: 4.0
        cp_dur / rqst_dur: 0 / 24.0

make_bar(cp_len, t_sig=(4, 2), s_bt=1.0, nt_use=4)
        nts_t -> ['qtr' 'hlf' 'hlf' 'qtr' 'hlf'] (4.0)
        cp_dur / rqst_dur: 4.0 / 24.0

make_bar(cp_len, t_sig=(4, 2), s_bt=1.0, nt_use=4)
        nts_t -> ['qtr' '8th' 'whl' 'hlf' '8th'] (4.0)
        cp_dur / rqst_dur: 8.0 / 24.0

make_bar(cp_len, t_sig=(4, 2), s_bt=1.0, nt_use=4)
        nts_t -> ['hlf' 'qtr' 'qtr' 'hlf' 'hlf'] (4.0)
        cp_dur / rqst_dur: 12.0 / 24.0

make_bar(cp_len, t_sig=(4, 2), s_bt=1.0, nt_use=4)
        nts_t -> ['qtr' 'hlf' 'hlf' 'qtr' 'hlf'] (4.0)
        cp_dur / rqst_dur: 16.0 / 24.0

make_bar(cp_len, t_sig=(4, 2), s_bt=1.0, nt_use=4)
        nts_t -> ['qtr' 'qtr' 'qtr' 'qtr' 'whl'] (4.0)
        cp_dur / rqst_dur: 20.0 / 24.0

make_bar(cp_len, t_sig=(4, 2), s_bt=1.0, nt_use=4)
        nts_t -> ['qtr' 'hlf' 'hlf' 'qtr' 'hlf'] (4.0)

        [['qtr', 'hlf', 'hlf', 'qtr', 'hlf'], ['qtr', '8th', 'whl', 'hlf', '8th'],
         ['hlf', 'qtr', 'qtr', 'hlf', 'hlf'], ['qtr', 'hlf', 'hlf', 'qtr', 'hlf'],
         ['qtr', 'qtr', 'qtr', 'qtr', 'whl'], ['qtr', 'hlf', 'hlf', 'qtr', 'hlf']]

And, the refactored code appears to work as desired. And without all sorts of messy fixing of things. We shall see what happens when I ask it to use more note values.

Fini

Well, still no chord progressions being played. But, I think this post is plenty long enough. Not to mention that the time I have spent messing with this is absurdedly long enough. Guess that’s what happens when you really don’t know where you are going or how to get there if you did.

Until next time, may you find success by planning things a lot better than I tend to do.

Resources