In [1]:
import matplotlib.pyplot as plt
import numpy as np
from IPython.display import Audio, display

Generating a soundwave¶

In [2]:
SAMPLING_RATE = 2 ** 14
MIDDLE_C_FREQ = 261.63


def make_sound(
    *,
    duration=1,
    sound_frequency=MIDDLE_C_FREQ,
    sampling_rate=SAMPLING_RATE,
):
    x = np.arange(duration * sampling_rate)
    y = np.sin(x * sound_frequency / sampling_rate * 2 * np.pi)
    return y
    

def show_signal(signal, plot=True):
    if plot:
        plt.plot(signal[:5 * int(SAMPLING_RATE / MIDDLE_C_FREQ)])
    display(Audio(data=signal, rate=SAMPLING_RATE))


show_signal(make_sound())
Your browser does not support the audio element.
No description has been provided for this image

Shifting pitch¶

In [3]:
def shift_sound_frequency(*, halftones, sound_frequency=MIDDLE_C_FREQ):
    return sound_frequency * (2 ** (halftones / 12))


show_signal(make_sound(sound_frequency=shift_sound_frequency(halftones=7)))
Your browser does not support the audio element.
No description has been provided for this image

Overlaying signals¶

In [4]:
def overlay(*signals):
    return np.sum(signals, axis=0)


show_signal(overlay(
    make_sound(),
    make_sound(sound_frequency=shift_sound_frequency(halftones=7)),
))
Your browser does not support the audio element.
No description has been provided for this image

Distortion effect¶

In [5]:
def add_effect_distortion(y, level=1):
    abs_mean = np.abs(y).mean()
    distortion_clip = np.exp(-level)
    y = y.clip(-distortion_clip, distortion_clip)
    y *= abs_mean / np.abs(y).mean()
    return y

show_signal(add_effect_distortion(make_sound()))
Your browser does not support the audio element.
No description has been provided for this image

Sound concatenation¶

In [6]:
show_signal(np.concatenate([
    make_sound(duration=.5, sound_frequency=shift_sound_frequency(halftones=0)),
    make_sound(duration=.5, sound_frequency=shift_sound_frequency(halftones=5)),
]), plot=False)
Your browser does not support the audio element.

Sound blending¶

In [7]:
def linear_blend(start_signal, end_signal):
    assert start_signal.shape == end_signal.shape
    end_weight = np.linspace(0, 1, len(end_signal))
    start_weight = 1 - end_weight
    return start_signal * start_weight + end_signal * end_weight


show_signal(linear_blend(
    make_sound(duration=3),
    make_sound(duration=3, sound_frequency=shift_sound_frequency(halftones=5)),
), plot=False)
Your browser does not support the audio element.

Combining effects¶

In [8]:
def add_effect_linear_distortion(y, level=1):
    return linear_blend(y, add_effect_distortion(y, level=level))


show_signal(add_effect_linear_distortion(overlay(make_sound(duration=3)), level=10), plot=False)
Your browser does not support the audio element.

Composing¶

In [9]:
show_signal(np.concatenate([
    make_sound(duration=.25, sound_frequency=shift_sound_frequency(halftones=0)),
    make_sound(duration=.25, sound_frequency=shift_sound_frequency(halftones=4)),
    make_sound(duration=.25, sound_frequency=shift_sound_frequency(halftones=7)),
    make_sound(duration=.25, sound_frequency=shift_sound_frequency(halftones=12)),
    add_effect_linear_distortion(overlay(
        make_sound(duration=2, sound_frequency=shift_sound_frequency(halftones=0)),
        make_sound(duration=2, sound_frequency=shift_sound_frequency(halftones=4)),
        make_sound(duration=2, sound_frequency=shift_sound_frequency(halftones=7)),
        make_sound(duration=2, sound_frequency=shift_sound_frequency(halftones=12)),
    )),
]), plot=False)
Your browser does not support the audio element.