.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "tutorials/subtractive_synthesis_tutorial.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note Click :ref:`here ` to download the full example code .. rst-class:: sphx-glr-example-title .. _sphx_glr_tutorials_subtractive_synthesis_tutorial.py: Subtractive synthesis ===================== **Author**: `Moto Hira `__ This tutorial is the continuation of `Filter Design Tutorial <./filter_design_tutorial.html>`__. This tutorial shows how to perform subtractive synthesis with TorchAudio's DSP functions. Subtractive synthesis creates timbre by applying filters to source waveform. .. warning:: This tutorial requires prototype DSP features, which are available in nightly builds. Please refer to https://pytorch.org/get-started/locally for instructions for installing a nightly build. .. GENERATED FROM PYTHON SOURCE LINES 22-29 .. code-block:: default import torch import torchaudio print(torch.__version__) print(torchaudio.__version__) .. rst-class:: sphx-glr-script-out .. code-block:: none 2.4.0.dev20240326 2.2.0.dev20240328 .. GENERATED FROM PYTHON SOURCE LINES 30-34 Overview -------- .. GENERATED FROM PYTHON SOURCE LINES 34-50 .. code-block:: default try: from torchaudio.prototype.functional import filter_waveform, frequency_impulse_response, sinc_impulse_response except ModuleNotFoundError: print( "Failed to import prototype DSP features. " "Please install torchaudio nightly builds. " "Please refer to https://pytorch.org/get-started/locally " "for instructions to install a nightly build." ) raise import matplotlib.pyplot as plt from IPython.display import Audio .. GENERATED FROM PYTHON SOURCE LINES 51-62 Filtered Noise -------------- Subtractive synthesis starts with a waveform and applies filters to some frequency components. For the first example of subtractive synthesis, we apply time-varying low pass filter to white noise. First, we create a white noise. .. GENERATED FROM PYTHON SOURCE LINES 62-70 .. code-block:: default SAMPLE_RATE = 16_000 duration = 4 num_frames = int(duration * SAMPLE_RATE) noise = torch.rand((num_frames,)) - 0.5 .. GENERATED FROM PYTHON SOURCE LINES 72-83 .. code-block:: default def plot_input(): fig, axes = plt.subplots(2, 1, sharex=True) t = torch.linspace(0, duration, num_frames) axes[0].plot(t, noise) axes[0].grid(True) axes[1].specgram(noise, Fs=SAMPLE_RATE) Audio(noise, rate=SAMPLE_RATE) plot_input() .. image-sg:: /tutorials/images/sphx_glr_subtractive_synthesis_tutorial_001.png :alt: subtractive synthesis tutorial :srcset: /tutorials/images/sphx_glr_subtractive_synthesis_tutorial_001.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 84-87 Windowed-sinc filter -------------------- .. GENERATED FROM PYTHON SOURCE LINES 89-96 Sweeping cutoff frequency ~~~~~~~~~~~~~~~~~~~~~~~~~ We use :py:func:`~torchaudio.prototype.functional.sinc_impulse_response` to create series of low pass filters, while changing the cut-off frequency from zero to Nyquist frequency. .. GENERATED FROM PYTHON SOURCE LINES 97-104 .. code-block:: default num_filters = 64 * duration window_size = 2049 f_cutoff = torch.linspace(0.0, 0.8, num_filters) kernel = sinc_impulse_response(f_cutoff, window_size) .. GENERATED FROM PYTHON SOURCE LINES 105-108 To apply time-varying filter, we use :py:func:`~torchaudio.prototype.functional.filter_waveform` .. GENERATED FROM PYTHON SOURCE LINES 109-112 .. code-block:: default filtered = filter_waveform(noise, kernel) .. GENERATED FROM PYTHON SOURCE LINES 113-115 Let's look at the spectrogram of the resulting audio and listen to it. .. GENERATED FROM PYTHON SOURCE LINES 116-137 .. code-block:: default def plot_sinc_ir(waveform, cutoff, sample_rate, vol=0.2): num_frames = waveform.size(0) duration = num_frames / sample_rate num_cutoff = cutoff.size(0) nyquist = sample_rate / 2 _, axes = plt.subplots(2, 1, sharex=True) t = torch.linspace(0, duration, num_frames) axes[0].plot(t, waveform) axes[0].grid(True) axes[1].specgram(waveform, Fs=sample_rate, scale="dB") t = torch.linspace(0, duration, num_cutoff) axes[1].plot(t, cutoff * nyquist, color="gray", linewidth=0.8, label="Cutoff Frequency", linestyle="--") axes[1].legend(loc="upper center") axes[1].set_ylim([0, nyquist]) waveform /= waveform.abs().max() return Audio(vol * waveform, rate=sample_rate, normalize=False) .. GENERATED FROM PYTHON SOURCE LINES 139-142 .. code-block:: default plot_sinc_ir(filtered, f_cutoff, SAMPLE_RATE) .. image-sg:: /tutorials/images/sphx_glr_subtractive_synthesis_tutorial_002.png :alt: subtractive synthesis tutorial :srcset: /tutorials/images/sphx_glr_subtractive_synthesis_tutorial_002.png :class: sphx-glr-single-img .. raw:: html


.. GENERATED FROM PYTHON SOURCE LINES 143-149 Oscillating cutoff frequency ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ By oscillating the cutoff frequency, we can emulate an effect of Low-frequency oscillation (LFO). .. GENERATED FROM PYTHON SOURCE LINES 150-159 .. code-block:: default PI2 = torch.pi * 2 num_filters = 90 * duration f_lfo = torch.linspace(0.9, 0.1, num_filters) f_cutoff_osci = torch.linspace(0.01, 0.03, num_filters) * torch.sin(torch.cumsum(f_lfo, dim=0)) f_cutoff_base = torch.linspace(0.8, 0.03, num_filters) ** 1.7 f_cutoff = f_cutoff_base + f_cutoff_osci .. GENERATED FROM PYTHON SOURCE LINES 161-165 .. code-block:: default kernel = sinc_impulse_response(f_cutoff, window_size) filtered = filter_waveform(noise, kernel) .. GENERATED FROM PYTHON SOURCE LINES 167-170 .. code-block:: default plot_sinc_ir(filtered, f_cutoff, SAMPLE_RATE) .. image-sg:: /tutorials/images/sphx_glr_subtractive_synthesis_tutorial_003.png :alt: subtractive synthesis tutorial :srcset: /tutorials/images/sphx_glr_subtractive_synthesis_tutorial_003.png :class: sphx-glr-single-img .. raw:: html


.. GENERATED FROM PYTHON SOURCE LINES 171-176 Wah-wah effects ~~~~~~~~~~~~~~~ Wah-wah effects are applications of low-pass filter or band-pass filter. They change the cut-off freuqnecy or Q-factor quickly. .. GENERATED FROM PYTHON SOURCE LINES 177-181 .. code-block:: default f_lfo = torch.linspace(0.15, 0.15, num_filters) f_cutoff = 0.07 + 0.06 * torch.sin(torch.cumsum(f_lfo, dim=0)) .. GENERATED FROM PYTHON SOURCE LINES 183-187 .. code-block:: default kernel = sinc_impulse_response(f_cutoff, window_size) filtered = filter_waveform(noise, kernel) .. GENERATED FROM PYTHON SOURCE LINES 189-192 .. code-block:: default plot_sinc_ir(filtered, f_cutoff, SAMPLE_RATE) .. image-sg:: /tutorials/images/sphx_glr_subtractive_synthesis_tutorial_004.png :alt: subtractive synthesis tutorial :srcset: /tutorials/images/sphx_glr_subtractive_synthesis_tutorial_004.png :class: sphx-glr-single-img .. raw:: html


.. GENERATED FROM PYTHON SOURCE LINES 193-200 Arbitrary frequence response ---------------------------- By using :py:func:`~torchaudio.prototype.functinal.frequency_impulse_response`, one can directly control the power distribution over frequency. .. GENERATED FROM PYTHON SOURCE LINES 200-206 .. code-block:: default magnitudes = torch.sin(torch.linspace(0, 10, 64)) ** 4.0 kernel = frequency_impulse_response(magnitudes) filtered = filter_waveform(noise, kernel.unsqueeze(0)) .. GENERATED FROM PYTHON SOURCE LINES 208-235 .. code-block:: default def plot_waveform(magnitudes, filtered, sample_rate): nyquist = sample_rate / 2 num_samples = filtered.size(-1) duration = num_samples / sample_rate # Re-organize magnitudes for overlay N = 10 # number of overlays interval = torch.linspace(0.05, 0.95, N) offsets = duration * interval # Select N magnitudes for overlays mags = torch.stack( [magnitudes for _ in range(N)] if magnitudes.ndim == 1 else [magnitudes[int(i * magnitudes.size(0))] for i in interval] ) mag_x = offsets.unsqueeze(-1) + 0.1 * mags mag_y = torch.linspace(0, nyquist, magnitudes.size(-1)).tile((N, 1)) _, ax = plt.subplots(1, 1, sharex=True) ax.vlines(offsets, 0, nyquist, color="gray", linestyle="--", linewidth=0.8) ax.plot(mag_x.T.numpy(), mag_y.T.numpy(), color="gray", linewidth=0.8) ax.specgram(filtered, Fs=sample_rate) return Audio(filtered, rate=sample_rate) .. GENERATED FROM PYTHON SOURCE LINES 237-239 .. code-block:: default plot_waveform(magnitudes, filtered, SAMPLE_RATE) .. image-sg:: /tutorials/images/sphx_glr_subtractive_synthesis_tutorial_005.png :alt: subtractive synthesis tutorial :srcset: /tutorials/images/sphx_glr_subtractive_synthesis_tutorial_005.png :class: sphx-glr-single-img .. raw:: html


.. GENERATED FROM PYTHON SOURCE LINES 240-241 It is also possible to make a non-stationary filter. .. GENERATED FROM PYTHON SOURCE LINES 242-246 .. code-block:: default magnitudes = torch.stack([torch.linspace(0.0, w, 1000) for w in torch.linspace(4.0, 40.0, 250)]) magnitudes = torch.sin(magnitudes) ** 4.0 .. GENERATED FROM PYTHON SOURCE LINES 248-251 .. code-block:: default kernel = frequency_impulse_response(magnitudes) filtered = filter_waveform(noise, kernel) .. GENERATED FROM PYTHON SOURCE LINES 253-255 .. code-block:: default plot_waveform(magnitudes, filtered, SAMPLE_RATE) .. image-sg:: /tutorials/images/sphx_glr_subtractive_synthesis_tutorial_006.png :alt: subtractive synthesis tutorial :srcset: /tutorials/images/sphx_glr_subtractive_synthesis_tutorial_006.png :class: sphx-glr-single-img .. raw:: html


.. GENERATED FROM PYTHON SOURCE LINES 256-257 Of course it is also possible to emulate simple low pass filter. .. GENERATED FROM PYTHON SOURCE LINES 258-261 .. code-block:: default magnitudes = torch.concat([torch.ones((32,)), torch.zeros((32,))]) .. GENERATED FROM PYTHON SOURCE LINES 263-266 .. code-block:: default kernel = frequency_impulse_response(magnitudes) filtered = filter_waveform(noise, kernel.unsqueeze(0)) .. GENERATED FROM PYTHON SOURCE LINES 268-270 .. code-block:: default plot_waveform(magnitudes, filtered, SAMPLE_RATE) .. image-sg:: /tutorials/images/sphx_glr_subtractive_synthesis_tutorial_007.png :alt: subtractive synthesis tutorial :srcset: /tutorials/images/sphx_glr_subtractive_synthesis_tutorial_007.png :class: sphx-glr-single-img .. raw:: html


.. GENERATED FROM PYTHON SOURCE LINES 271-277 References ---------- - https://en.wikipedia.org/wiki/Additive_synthesis - https://computermusicresource.com/Simple.bell.tutorial.html - https://computermusicresource.com/Definitions/additive.synthesis.html .. rst-class:: sphx-glr-timing **Total running time of the script:** ( 0 minutes 7.838 seconds) .. _sphx_glr_download_tutorials_subtractive_synthesis_tutorial.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: subtractive_synthesis_tutorial.py ` .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: subtractive_synthesis_tutorial.ipynb ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_