Repository: bokeh/bokeh

Issue:
Add synthetic data mode to spectrogram
The pyaudio package is not available on all platforms. An option mode to generate synthetic dat would allow the app to be run everywhere in some capacity. 

@philippjfr I think you might already have some code that could be re-used?


Discussion:
Sorry completely missed this, will try to dig it up, I used some basic frequency modulation with a random component.
@philippjfr do you still have that code handy?
Yes, here's what I did to generate the signal:

```python
import numpy as np
import scipy as sp
from scipy.integrate import simps

NUM_SAMPLES = 1024
SAMPLING_RATE = 44100
MAX_FREQ = SAMPLING_RATE / 2
FREQ_SAMPLES = NUM_SAMPLES / 8
TIMESLICE = 100  # ms
NUM_BINS = 16

X = 500
Y = 10

def get_data():
    global X, Y
    X += np.random.randn()
    Y += np.random.randn()
    raw_data = (np.sin(np.linspace(0, np.pi*X, 1000))*np.sin(np.linspace(0, np.pi*Y, 1000)))
    signal = raw_data
    fft = sp.fft(signal)
    spectrum = abs(fft)[:NUM_SAMPLES/2]
    power = spectrum**2
    bins = [simps(a) for a in np.split(power, NUM_BINS)]
    return signal, spectrum, bins

def update():
    signal, spectrum, bins = get_data()
    ...
```

Edit:

That's not particularly interesting though it's just what worked for me, I also had this lying around but didn't end up using it:

```python
def fm_modulation(f_carrier = 220, f_mod =220, Ind_mod = 1, length = 5, sampleRate = 44100):
    sampleInc = 1.0/sampleRate
    x = np.arange(0,length, sampleInc)
    y = np.sin(2*np.pi*f_carrier*x + Ind_mod*np.sin(2*np.pi*f_mod*x))
    return y
```

Resolving patch:
diff --git a/examples/app/spectrogram/audio.py b/examples/app/spectrogram/audio.py
--- a/examples/app/spectrogram/audio.py
+++ b/examples/app/spectrogram/audio.py
@@ -1,11 +1,6 @@
 from __future__ import print_function
 
-try:
-    import pyaudio
-except:
-    print("This demo requires pyaudio installed to function")
-    import sys
-    sys.exit(1)
+from time import sleep
 
 import numpy as np
 import scipy as sp
@@ -20,24 +15,51 @@
 
 data = {'values': None}
 
-def _get_audio_data():
-    pa = pyaudio.PyAudio()
-    stream = pa.open(
-        format=pyaudio.paInt16,
-        channels=1,
-        rate=SAMPLING_RATE,
-        input=True,
-        frames_per_buffer=NUM_SAMPLES
-    )
-
-    while True:
-        try:
-            raw_data  = np.fromstring(stream.read(NUM_SAMPLES), dtype=np.int16)
-            signal = raw_data / 32768.0
+try:
+    import pyaudio
+
+    def update_audio_data():
+        pa = pyaudio.PyAudio()
+        stream = pa.open(
+            format=pyaudio.paInt16,
+            channels=1,
+            rate=SAMPLING_RATE,
+            input=True,
+            frames_per_buffer=NUM_SAMPLES
+        )
+
+        while True:
+            try:
+                raw_data  = np.fromstring(stream.read(NUM_SAMPLES), dtype=np.int16)
+                signal = raw_data / 32768.0
+                fft = sp.fft(signal)
+                spectrum = abs(fft)[:int(NUM_SAMPLES/2)]
+                power = spectrum**2
+                bins = simps(np.split(power, NUM_BINS))
+                data['values'] = signal, spectrum, bins
+            except:
+                continue
+
+except:
+    print()
+    print(" *** Pyaudio package not installed, using synthesized audio data ***")
+    print()
+
+    # These are basically picked out of a hat to show something vaguely interesting
+    _t = np.arange(0, NUM_SAMPLES/SAMPLING_RATE, 1.0/SAMPLING_RATE)
+    _f = 2000 + 3000*(2+np.sin(4*np.linspace(0, 2*np.pi, 500)))
+    _i = 0
+
+    def update_audio_data():
+        while True:
+            global _i
+            A = 0.3 + 0.05 * np.random.random()
+            signal = A*np.sin(2*np.pi*_f[_i]*_t + np.sin(2*np.pi*200*_t))
+
             fft = sp.fft(signal)
-            spectrum = abs(fft)[:NUM_SAMPLES/2]
+            spectrum = abs(fft)[:int(NUM_SAMPLES/2)]
             power = spectrum**2
             bins = simps(np.split(power, NUM_BINS))
             data['values'] = signal, spectrum, bins
-        except:
-            continue
+            _i = (_i + 1) % len(_f)
+            sleep(1.0/12)
diff --git a/examples/app/spectrogram/server_lifecycle.py b/examples/app/spectrogram/server_lifecycle.py
--- a/examples/app/spectrogram/server_lifecycle.py
+++ b/examples/app/spectrogram/server_lifecycle.py
@@ -3,6 +3,6 @@
 import audio
 
 def on_server_loaded(server_context):
-    t = Thread(target=audio._get_audio_data, args=())
+    t = Thread(target=audio.update_audio_data, args=())
     t.setDaemon(True)
     t.start()


Test patch: