[WIP] Add ability to play sound
Signed-off-by: Danila Fedorin <danila.fedorin@gmail.com>
This commit is contained in:
@@ -3,6 +3,7 @@ title: "Some Music Theory From (Computational) First Principles"
|
||||
date: 2025-09-20T18:36:28-07:00
|
||||
draft: true
|
||||
filters: ["./to-parens.lua"]
|
||||
custom_js: ["playsound.js"]
|
||||
---
|
||||
|
||||
Sound is a perturbation in air pressure that our ear recognizes and interprets.
|
||||
@@ -86,6 +87,17 @@ class Superimpose:
|
||||
width, height
|
||||
)
|
||||
|
||||
def hugo_shortcode(body):
|
||||
return "{{" + "< " + body + " >" + "}}"
|
||||
|
||||
class PlayNotes:
|
||||
def __init__(self, *hzs):
|
||||
self.hzs = hzs
|
||||
|
||||
def _repr_html_(self):
|
||||
toplay = ",".join([str(hz) for hz in self.hzs])
|
||||
return hugo_shortcode(f"playsound \"{toplay}\"")
|
||||
|
||||
class VerticalStack:
|
||||
def __init__(self, *args):
|
||||
self.args = args
|
||||
@@ -156,6 +168,11 @@ middleC = Frequency(261.63)
|
||||
middleC
|
||||
```
|
||||
|
||||
```{python}
|
||||
#| echo: false
|
||||
PlayNotes(middleC.hz)
|
||||
```
|
||||
|
||||
Great! Now, if you're a composer, you can play this note and make music out
|
||||
of it. Except, music made with just one note is a bit boring, just like saying
|
||||
the same word over and over again won't make for an interesting story.
|
||||
@@ -165,13 +182,23 @@ other frequency.
|
||||
```{python}
|
||||
g4 = Frequency(392.445)
|
||||
g4
|
||||
|
||||
```
|
||||
|
||||
```{python}
|
||||
#| echo: false
|
||||
PlayNotes(g4.hz)
|
||||
```
|
||||
|
||||
```{python}
|
||||
fSharp4 = Frequency(370.000694) # we write this F#
|
||||
fSharp4
|
||||
```
|
||||
|
||||
```{python}
|
||||
#| echo: false
|
||||
PlayNotes(fSharp4.hz)
|
||||
```
|
||||
|
||||
This is pretty cool. You can start making melodies with these notes, and sing
|
||||
some jingles. However, if your friend sings along with you, and happens to
|
||||
sing F# while you're singing the middle C, it's going to sound pretty awful.
|
||||
@@ -194,6 +221,10 @@ show you the bigger picture.
|
||||
```{python}
|
||||
Superimpose(Frequency(middleC.hz*4), Frequency(fSharp4.hz*4))
|
||||
```
|
||||
```{python}
|
||||
#| echo: false
|
||||
PlayNotes(middleC.hz, fSharp4.hz)
|
||||
```
|
||||
|
||||
Looking at this picture, we can see that it's far more disordered than the
|
||||
pure sine waves we've been looking at so far. There's not much of a pattern
|
||||
@@ -244,6 +275,11 @@ VerticalStack(
|
||||
)
|
||||
```
|
||||
|
||||
```{python}
|
||||
#| echo: false
|
||||
PlayNotes(middleC.hz, twiceMiddleC.hz)
|
||||
```
|
||||
|
||||
You can easily inspect the new graph to verify that it has a repeating pattern,
|
||||
and that this pattern repeats exactly as frequently as the lower-frequency
|
||||
note at the top. Indeed, these two notes sound quite good together. It turns
|
||||
@@ -293,18 +329,28 @@ VerticalStack(
|
||||
Superimpose(middleC, thriceMiddleC)
|
||||
)
|
||||
```
|
||||
```{python}
|
||||
#| echo: false
|
||||
PlayNotes(middleC.hz, thriceMiddleC.hz)
|
||||
```
|
||||
|
||||
That's not bad! These two sound good together as well, but they are not
|
||||
in the same pitch class. There's only one problem: these notes are a bit
|
||||
far apart in terms of pitch. Wait a minute --- weren't we just talking about
|
||||
singing notes that were too high at half their original frequency?
|
||||
We can do that here. The result is a note we've already seen:
|
||||
far apart in terms of pitch. That `triceMiddleC` note is really high!
|
||||
Wait a minute --- weren't we just talking about singing notes that were too
|
||||
high at half their original frequency? We can do that here. The result is a
|
||||
note we've already seen:
|
||||
|
||||
```{python}
|
||||
print(thriceMiddleC.hz/2)
|
||||
print(g4.hz)
|
||||
```
|
||||
|
||||
```{python}
|
||||
#| echo: false
|
||||
PlayNotes(middleC.hz, g4.hz)
|
||||
```
|
||||
|
||||
In the end, we got G4 by multiplying our original frequency by $3/2$. What if
|
||||
we keep applying this process to find more notes? Let's not even worry
|
||||
about the specific frequencies (like `261.63`) for a moment. We'll start
|
||||
@@ -325,12 +371,20 @@ while len(seen) < 6:
|
||||
|
||||
seen.add(new_note)
|
||||
note = new_note
|
||||
```
|
||||
|
||||
For an admittedly handwavy reason, let's also throw in one note that we
|
||||
get from going _backwards_: dividing by $2/3$ instead of multiplying.
|
||||
This division puts us below our original frequency, so let's double it.
|
||||
|
||||
```{python}
|
||||
# Throw in one more by going *backwards*. More on that in a bit.
|
||||
seen.add(Fraction(2/3) * 2)
|
||||
|
||||
fractions = sorted(list(seen))
|
||||
fractions
|
||||
```
|
||||
|
||||
```{python}
|
||||
frequencies = [middleC.hz * float(frac) for frac in fractions]
|
||||
frequencies
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user