2018-03-08 08:09:39 -08:00
{
"cells": [
{
"cell_type": "code",
2018-03-08 20:47:32 -08:00
"execution_count": null,
2018-03-08 09:37:06 -08:00
"metadata": {},
2018-03-08 08:09:39 -08:00
"outputs": [],
"source": [
"import matplotlib\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"\n",
"# Show plots inline.\n",
"%matplotlib inline\n",
"\n",
"# Number of harmonics to include in subsequent plots.\n",
"Harmonics = 10\n",
"# Time in seconds.\n",
"Time = 2.0\n",
"# Plot resolution.\n",
2018-03-08 09:37:06 -08:00
"Steps = 200"
]
},
{
"cell_type": "code",
2018-03-08 20:47:32 -08:00
"execution_count": null,
2018-03-08 09:37:06 -08:00
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
2018-03-08 08:09:39 -08:00
"# Array of step values.\n",
"def time_space(sec=Time, samples=Steps):\n",
" '''An array of equally spaced samples over a period of time.'''\n",
" # One extra sample to also include the endpoint.\n",
" return np.linspace(0, sec, num=samples + 1)\n",
"\n",
"def sample_rate(sec=Time, samples=Steps):\n",
" '''Sampling rate in samples per second.'''\n",
2018-03-08 09:37:06 -08:00
" return float(samples + 1) / float(sec)\n",
"\n",
"def sample_spacing(sec=Time, samples=Steps):\n",
" '''Time between samples.'''\n",
" return float(sec) / float(samples + 1)\n",
2018-03-08 08:09:39 -08:00
"\n",
"def frequency_spectrum(data):\n",
2018-03-08 09:37:06 -08:00
" '''Perform an FFT on `data`.'''\n",
" # Get the signal frequencies.\n",
" fft = np.abs(np.fft.fft(data))\n",
2018-03-08 08:09:39 -08:00
" \n",
2018-03-08 09:37:06 -08:00
" # Get the frequencies for a DFT plot (this doesn't depend on the actual signal) based on the \n",
" # spacing of our samples.\n",
2018-03-08 20:47:32 -08:00
" freqs = np.fft.fftfreq(len(fft), d=sample_spacing())\n",
2018-03-08 08:09:39 -08:00
" \n",
2018-03-08 09:37:06 -08:00
" return freqs, fft\n",
"\n",
"def plot_signal_and_spectrum(domain, data):\n",
" '''Plot the signal and spectrum in a stacked plot.'''\n",
2018-03-08 13:41:31 -08:00
" plt.figure(figsize=(10,6))\n",
" plt.subplot(211)\n",
2018-03-08 17:09:07 -08:00
" plt.axhline(color='#666666', linewidth=0.75)\n",
2018-03-08 09:37:06 -08:00
" plt.plot(domain, data)\n",
2018-03-08 08:09:39 -08:00
" \n",
2018-03-08 13:41:31 -08:00
" num_freqs = Harmonics * 2\n",
2018-03-08 20:39:28 -08:00
" freqs, mags = (x[:num_freqs] for x in frequency_spectrum(data))\n",
2018-03-08 13:41:31 -08:00
" plt.subplot(212)\n",
" plt.xticks(range(num_freqs))\n",
2018-03-08 20:47:32 -08:00
" plt.bar(freqs, mags)"
2018-03-08 08:09:39 -08:00
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Basic Waves"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Sine Wave\n",
"\n",
"Sine waves are easy. 😁 The have a single frequency, and no harmonics."
]
},
{
"cell_type": "code",
2018-03-08 20:47:32 -08:00
"execution_count": null,
2018-03-08 08:09:39 -08:00
"metadata": {},
2018-03-08 20:47:32 -08:00
"outputs": [],
2018-03-08 08:09:39 -08:00
"source": [
"# Sine wave.\n",
"\n",
"def sine(domain, freq=1.0):\n",
" return np.sin(2.0 * np.pi * freq * domain)\n",
"\n",
"wave = sine(time_space())\n",
2018-03-08 09:37:06 -08:00
"plot_signal_and_spectrum(time_space(), wave)"
2018-03-08 08:09:39 -08:00
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Saw Wave\n",
"\n",
"Saw waves consist of every integer harmonic _n_ above the fundamental frequency, where that harmonic's amplitude is n<sup>-1</sup>."
]
},
{
"cell_type": "code",
2018-03-08 20:47:32 -08:00
"execution_count": null,
2018-03-08 08:09:39 -08:00
"metadata": {},
2018-03-08 20:47:32 -08:00
"outputs": [],
2018-03-08 08:09:39 -08:00
"source": [
"def saw(domain, freq=1):\n",
" harmonics = np.asarray([1.0 / h * sine(domain, freq=freq * h) for h in range(1, Harmonics)])\n",
" wave = np.sum(harmonics, axis=0)\n",
" return wave\n",
"\n",
"wave = saw(time_space())\n",
2018-03-08 09:37:06 -08:00
"plot_signal_and_spectrum(time_space(), wave)"
2018-03-08 08:09:39 -08:00
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You can invert a saw wave to get what many synths call a \"blade\" wave. Instead of gradually falling and then spiking up, blade waves gradually rise and the sharply fall."
]
},
{
"cell_type": "code",
2018-03-08 20:47:32 -08:00
"execution_count": null,
2018-03-08 08:09:39 -08:00
"metadata": {},
2018-03-08 20:47:32 -08:00
"outputs": [],
2018-03-08 08:09:39 -08:00
"source": [
2018-03-08 09:37:06 -08:00
"plot_signal_and_spectrum(time_space(), -1 * saw(time_space()))"
2018-03-08 08:09:39 -08:00
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Square Wave\n",
"\n",
"Square waves are made of every other harmonic above the fundamental frequency. Again, the amplitude of each harmonic _n_ is _1 / n_."
]
},
{
"cell_type": "code",
2018-03-08 20:47:32 -08:00
"execution_count": null,
2018-03-08 08:09:39 -08:00
"metadata": {},
2018-03-08 20:47:32 -08:00
"outputs": [],
2018-03-08 08:09:39 -08:00
"source": [
"def square(domain, freq=1):\n",
" harmonics = np.asarray([1.0 / h * sine(domain, freq=freq * h) for h in range(1, Harmonics, 2)])\n",
" wave = np.sum(harmonics, axis=0)\n",
" return wave\n",
"\n",
"wave = square(time_space())\n",
2018-03-08 09:37:06 -08:00
"plot_signal_and_spectrum(time_space(), wave)"
2018-03-08 08:09:39 -08:00
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Triangle Wave"
]
},
{
"cell_type": "code",
2018-03-08 20:47:32 -08:00
"execution_count": null,
2018-03-08 08:09:39 -08:00
"metadata": {},
2018-03-08 20:47:32 -08:00
"outputs": [],
2018-03-08 08:09:39 -08:00
"source": [
"def triangle(domain, freq=1):\n",
" def label(i):\n",
" return 2. * i + 1.\n",
" harmonics = np.asarray([((-1) ** h) * (label(h) ** -2) * sine(domain, freq=freq * label(h)) for h in range(Harmonics)])\n",
" wave = np.sum(harmonics, axis=0)\n",
" return wave\n",
"\n",
"wave = triangle(time_space())\n",
2018-03-08 09:37:06 -08:00
"plot_signal_and_spectrum(time_space(), wave)"
2018-03-08 08:09:39 -08:00
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.1"
}
},
"nbformat": 4,
"nbformat_minor": 2
}