{:Code Redux}

Music Theory for (Non)Musicians and (Non)Programmers

Beats, Part One

If the Circle of Fifths is the foundation of the relationships between notes, beats are the fundamental building blocks of all music. You simply cannot have music without a sense of tempo, rhythm, or cadence without a consistent and flowing beat. This consistency is the difference between music and cacophony.

Some argue that pounding two stones together was the first music created by humans, and it is hard to deny that this may be the truth. With a beat and a human voice, you have a song; a simple song, perhaps primitive, but it is still music. Westerners are very much fascinated by the music that is created in more traditional cultures. We can appreciate the foreign sounds and the simplicity of traditional Asian music. We can appreciate the incredibly complex poly-rhythms heard in Sub-Saharan African music. In this article, we are going to start simple.

What is a beat?

Without pondering it, we think of a beat as a consistent and flat percussion sound, similar to the "tock tock tock" one would hear from a metronome. This couldn't be further from the truth.

A 4/4 Time Signature:

A time signature of 4/4 is the most common signature used in Western Music. It is so common, in fact, that it sometimes denoted with a C instead of the numbers.

The 4 on top denotes how many beats are in a measure, and the 4 on the bottom indicates that each beat is equal to one quarter note. If you don't know what a quarter note means, that is fine for now. That subject will be covered soon.

When musicians talk about beats, they are talking about where the accent occurs in the music. In the case of 4/4 time, the accented beat occurs on the first note of each bar. The percussion for 4/4 may sound like "tick tock tock tock" or "boom da da da."

Beats are not strictly for drums. All musicians place accents in their music. The accents are often more subtle, but learning how to play in time is a critical step for any musician, and, in the opinion of this author, is the first thing that seperates a professional from an amateur.

The most common beats in Western Music are 4/4, 3/4, and 8/12. The focus on this article is the top number of each time signature. Continuing on with the definition for 4/4, placing the accent for each bar on the first note, we can use some smaller notations for building out today's program. Throughout this article, I will use 4/x, 3/x, 8/x, and similar to denote time signatures. This is not standard notation, but I'm going to to use it to indicate that we are only talking about the top number.

The accent will be indicated by 'x', Each unaccented beat will be indicated by '.', and each bar will be indicated by '|'.

Examples:

4/x:
x . . . | x . . . | x . . .

3/x:
x . . | x . . | x . .

8/x:
x . . . . . . . | x . . . . . . . | x . . . . . . .

The program to create the above:

beat() takes two arguments: n will be the top number and bars is how many bars to print out

def beat(n, bars):
    accent = "x "
    unaccent = ". " * (n - 1)
    bar = "| "
    str = ""
    for i in range(bars):
        str += "{0}{1}{2}".format(accent, 
                                  unaccent, 
                                  bar)
    str = str[:-2]
    return str

print(beat(4, 2))
>>> x . . . | x . . . 

The program will work for any beat / bars you wish:

print(beat(12, 10))
>>> x . . . . . . . . . . . 
| x . . . . . . . . . . . 
| x . . . . . . . . . . . 
| x . . . . . . . . . . . 
| x . . . . . . . . . . . 
| x . . . . . . . . . . . 
| x . . . . . . . . . . . 
| x . . . . . . . . . . . 
| x . . . . . . . . . . . 
| x . . . . . . . . . . . 

Secondary Accents

In time signatures that are even, such as 4/x, 6/x, 8/x, there is often a secondary "on beat." This second accent is generally played at the half-way point, softer than the first accent. For example, in a count of 1, 2, 3, 4, the main accent is on "1" and the secondary accent is on "3". If the three was played at the same intensity as the first beat, this would sound like a 2/x piece, which probably isn't the intent of the composer.

§ To represent this secondary beat, I am going to add a new symbol, ";".

§ With this new symbolism, the output of each bar with 4 beats will look like this: "x . ; ."

§ 6 beats will look like this: "x . . ; . ."

§ Beats that are odd, such as 3, will not be affected.

To represent this new information, we will modify our program to check if the "n" parameter is even, if the beat is even, the minor_accent will be ";"; if the beat is odd, the minor_accent will be an empty string.

def beat_with_minor_accent(n, bars):
    accent = "x "
    if n//2 * 2 == n:
        minor_accent = "; "
        unaccent = ". " * (n//2 - 1)
    else:
        minor_accent = ""
        unaccent = ". " * (n//2)
    bar = "| "
    str = ""
    for i in range(bars):
        str += "{0}{1}{2}{3}{4}".format(accent,
                                  unaccent,
                                  minor_accent,
                                  unaccent,
                                  bar)
    str = str[:-2]
    return str

print(beat_with_minor_accent(4, 2))
>>> x . ; . | x . ; .

print(beat_with_minor_accent(3, 2))
>>> x . . | x . . 

Tempo, or, Beats Per Minute (BPM)

People who listen to a lot of dance music have probably heard about BPM. Trance, for example, is usually 125 to 150 BPM. Drum & Bass is usually faster, at 160 to 180 BPM.

Of course, the concept of BPM predates dance music. In classical music, we are often given a metronome marker.

This image shows 100 BPM:

In other cases, composers use tempo markings, which are often in Italian.

This image shows the tempo marking for Adagio (66 to 76 BPM)

There is a relatively long list for all the tempo markings. The next program will take an input for bpm and return the correct tempo marking or markings. Some markings overlap.

def get_tempo_marking(bpm):
    t = []
    tempo_list = [(0, 24, "Larghissimo"),
                  (25, 45, "Grave"),
                  (40, 60, "Largo"),
                  (45, 60, "Lento"),
                  (60, 66, "Larghetto"),
                  (66, 76, "Adagio"),
                  (72, 76, "Adagietto"),
                  (76, 108, "Andante"),
                  (80, 108, "Andantino"),
                  (83, 85, "Marcia moderato"),
                  (92, 112, "Andante moderato"),
                  (108, 120, "Moderato"),
                  (112, 120, "Allegretto"),
                  (116, 120, "Allegro moderato"),
                  (120, 168, "Allegro"),
                  (168, 176, "Vivace"),
                  (172, 176, "Vivacissimo"),
                  (172, 176, "Allegrissimo"),
                  (172, 176, "Allegro vivace"),
                  (168, 200, "Presto"),
                  (200, 300, "Prestissimo")]
    for i in tempo_list:
        if i[0] <= bpm <= i[1]:
            t.append(i[2])
    return t

get_tempo_marking(26)
>>> ['Grave']

get_tempo_marking(172)
>>> ['Vivace', 'Vivacissimo', 'Allegrissimo', 
'Allegro vivace', 'Presto']


The above is hardly complete. There are many other words that are used. Some words are German, for example.

Why is there overlap? Some tempo markings overlap because they aren't only for tempos. They also attempt to express and emotion. This will be discussed in a later article.

We could do the inverse: call a function with a word and get the BPM.

def get_tempo_marking_bpm(tempo_marking):
    tempo_dict = {"Larghissimo": (0, 24),
                  "Grave": (25, 45),
                  "Largo": (40, 60),
                  "Lento": (45, 60),
                  "Larghetto": (60, 66),
                  "Adagio": (66, 76),
                  "Adagietto": (72, 76),
                  "Andante": (76, 108),
                  "Andantino": (80, 108),
                  "Marcia moderato": (83, 85),
                  "Andante moderato": (92, 112),
                  "Moderato": (108, 120),
                  "Allegretto": (112, 120),
                  "Allegro moderato": (116, 120),
                  "Allegro": (120, 168),
                  "Vivace": (168, 176),
                  "Vivacissimo": (172, 176),
                  "Allegrissimo": (172, 176),
                  "Allegro vivace": (172, 176),
                  "Presto": (168, 200),
                  "Prestissimo": (200, 300)}
    return tempo_dict[tempo_marking]

get_tempo_marking_bpm("Presto")
>>> (168, 200)

Combining Beat and Tempo

How can we represent the idea of a BPM and beats per bar using code? My idea is to represent tempo by writing out each new beat, with a sleep, so that the program prints the correct combination of "x", ".", ";" and "|" per minute.

warning: The time keeping in Python is not precise enough to generate beats exactly. It is better to think of the sleeping as a demonstration, as opposed to an attempt at precision.

The print_nbpm takes two arguments, a beat per minute (bpm) and the amount of x's to show (qty).

import time

def print_nbpm(bpm, qty):
    sleep_step = 60/bpm
    for i in range(qty):
        print("x", end = "", flush=True)
        time.sleep(sleep_step)

print_nbpm(100, 10)
>>> xxxxxxxxxx

When you run the above program, it will display "x" for each time increment, so that at the end of one minute, you have the correct amount of beats, so 120 BPM should print out 120 x's at the end of one minute.

This function will take an argument for a tempo marking and generate a string of x's. Since each tempo marking is a range and not a fixed number, I will call for a random number within the range of the tempo marking.

The print_tm_nbpm() function takes two arguments: tempo marking (tempo_marking) and the number of x's to print (qty). It will print out the BPM it chose and the quantity of x's, in tempo.

import random

def print_tm_nbpm(tempo_marking, qty):
    bpm = get_tempo_marking_bpm(tempo_marking)
    a, b = bpm[0], bpm[1]
    bpm = random.randint(a, b)
    print(bpm)
    print_nbpm(bpm, qty)

print_tm_nbpm("Presto", 10)
>>> 187


We can loop back to the very beginning and print out a beat in a tempo.

The function print_beat_tempo takes three arguments: beats per minute (bpm), beats per bar (bpb), and how many bars to print (bars). We also added a check to be sure there is no sleeping after a bar symbol ("|").

def print_beat_tempo(bpm, bpb, bars):
    p = beat(bpb, bars)
    sleep_step = 60/bpm
    for i in p:
        print(i, end = "", flush=True)
        if i != "|":
            time.sleep(sleep_step)


print_beat_tempo(100, 3, 2)
>>> x . . | x . .

print_beat_tempo(200, 3, 2)
>>> x . . | x . .

print_beat_tempo(200, 4, 2)
>>> x . . . | x . . .

In the above code, we also had a function for beats with a minor accent. We can add a flag to indicate we would like that, if possible:

def print_beat_tempo(bpm, bpb, bars, minor_beat):
    if minor_beat:
        p = beat_with_minor_accent(bpb, bars)
    else:
        p = beat(bpb, bars)
    sleep_step = 60/bpm
    for i in p:
        print(i, end = "", flush=True)
        if i != "|":
            time.sleep(sleep_step)


And finally, code to print out a beat based on a tempo_marking

def print_beat_tempo_marking(tempo_marking, bpb, bars, minor_beat):
    bpm = get_tempo_marking_bpm(tempo_marking)
    a, b = bpm[0], bpm[1]
    bpm = random.randint(a, b)

    print(bpm)
    
    if minor_beat:
        p = beat_with_minor_accent(bpb, bars)
    else:
        p = beat(bpb, bars)
    sleep_step = 60/bpm
    for i in p:
        print(i, end = "", flush=True)
        if i != "|":
            time.sleep(sleep_step)

Conclusion

This article on beats is very much introductory. We will come back to beats later, after covering notations.

If you would like to see these ideas and concepts in action, please visit my newest project: butternotes.com