In [ ]:
import os, sys
import numpy as np
from TimeFreqAuditoryScene import *
from IPython.display import Audio, display, clear_output
from IPython.html import widgets
from IPython.html.widgets import interactive
%matplotlib inline

Reproducing standard shepard tones from (Shepard 64)

Shepard tone

A shepard tone is a chord containing all tones in octave relation to a base tone of frequency $f_b$ $$f_i = f_b 2^i, \quad i\in \mathbb{Z}$$

I will note a shepard tone as $SP(f_b)$

A log-gaussian spectral envelope is applied: $$a(f) \propto \exp\left( -\frac{1}{2\sigma^2}(\log f - \mu )^2 \right) $$

For construction purposes, only tones whose frequency lie in the audible range are included $$f_i \in [f_{min},f_{max}]$$

Properties

  • Scaling all tone frequencies is equivalent to scaling the base frequency: $$scale(FB(f_b),k) = FB(k\,f_b)$$

  • Te set of Shepard tones is stable to scaling frequencies

  • Ay pair of shepard tones are equal up to a scaling of frequencies#

  • Scaling is log periodic with octave period: $$scale(SP(f_b ),2) = SP(2 f_b) = SP(f_b)$$

Tritone

A tritone is a pair of two shepard tone in half-octave relationship $$SP(f_b), SP(f_b 2^{1/2})$$


In [ ]:
# Parameterization
# Global parameters
fs = 44100 # sampling frequency
# Shepard tones
delay = 1./8. # delay between the two tones
duration = 1./8. # duration of the two tones
# declare gaussian envelope on log frequency
mu_log=np.log(500)
sigma_log=2.
genv = GaussianSpectralEnvelope(mu_log=mu_log, sigma_log=sigma_log)

Here, two shepard tones are presented.

They are parameterized by

  • the base frequency of the first tone
  • the semi-tone interval between the tones $\in [0,12]$

In [ ]:
def shepard(fb1=15., st=6.):
    scene = Scene()

    # Constructing the scene
    run_time = 0
    tone1 = ShepardTone(fb=fb1, env=genv, delay=run_time, duration=duration)
    run_time += duration + delay
    tone2 = ShepardTone(fb=fb1*2.**(st/12.), env=genv, delay=run_time, duration=duration)
    scene.add([tone1, tone2]) 

    # draw spectrogram
    sd = SceneDrawer()
    sd.draw(scene)
    plt.show()
    
    # generate sound
    x = scene.generate(fs=fs)
    display(Audio(x, rate=fs, autoplay=True))
    
w = interactive(shepard, fb1=(10.,20.), st=(0.,12.))
display(w)

Observations

Independent of the base frequency, shift below 6 are percieved as upwards, shift above 6 as downwards


In [ ]: