Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Real-time plotting example #106

Open
vyhyb opened this issue Apr 12, 2021 · 4 comments
Open

Real-time plotting example #106

vyhyb opened this issue Apr 12, 2021 · 4 comments

Comments

@vyhyb
Copy link

vyhyb commented Apr 12, 2021

Hi, thank you again for your great library. I have a bit problems with real time plotting of gathered audio data (and its spectrum). I guess it is probably some threading issue, is it? Don't you know how to deal with this? I've tried several ways but I don't have any luck with updating the graph. Thanks in advance!

Code here:

import numpy as np
import queue
import sys
import threading

buffersize = 3
clientname = "ImpedanceTubeTest2"
rec_time = 10
q = queue.Queue(maxsize=buffersize*10)
global rec_end
rec_end = False
event = threading.Event()

def print_error(*args):
    print(*args, file=sys.stderr)


def xrun(delay):
    print_error("An xrun occured, increase JACK's period size?")


def shutdown(status, reason):
    print_error('JACK shutdown!')
    print_error('status:', status)
    print_error('reason:', reason)
    event.set()


def stop_callback(msg=''):
    if msg:
        print_error(msg)
    event.set()
    raise jack.CallbackExit


def process(frames):
    if frames != blocksize:
        stop_callback('blocksize must not be changed, I quit!')
    if rec_end:
        stop_callback()  # Recording is finished
    try:
        q.put_nowait(client.inports[0].get_array()[:])
    except (queue.Full):
        print("Full Queue")
        stop_callback()

from IPython.display import clear_output
from time import sleep
from scipy.signal import stft
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
size = 8
olap = 2
olap_inv = size - olap

global fig, ax
fig, ax = plt.subplots()

def animate(i, spectrum):
    line.set_ydata(spectrum)  # update the data.
    fig.canvas.draw()
    return line,

try:
    import jack

    client = jack.Client(clientname, no_start_server=True)
    blocksize = client.blocksize
    samplerate = client.samplerate
    FFT_size = size * blocksize
    overlap = olap * blocksize
    overlap_inv = olap_inv*blocksize
    client.set_xrun_callback(xrun)
    client.set_shutdown_callback(shutdown)
    client.set_process_callback(process)
    client.inports.register('in_1')
    data = np.zeros(FFT_size)
    init = stft(data, fs= samplerate, nperseg=FFT_size
    spectrum = init[2].T[0].real
    line, = ax.plot(init[0],spectrum)
   
    ani = FuncAnimation(
        fig, animate, interval=1, fargs=[spectrum])
    plt.show(block=False)
    sleep(2)
    with client:
        target_ports = client.get_ports(
            is_physical=True, is_output=True, is_audio=True)
        
        client.inports[0].connect(target_ports[0])
        timeout = blocksize * buffersize / samplerate
        for i in range(olap):
            data[i*blocksize:(i+1)*blocksize] = q.get(timeout=timeout)
        while True:
            try:
                for i in range(olap,size):
                    data[i*blocksize:(i+1)*blocksize] = q.get(timeout=timeout)
                
                stft_data = stft(data, fs= samplerate, nperseg=FFT_size)
                spectrum = stft_data[2]*np.conj(stft_data[2])/FFT_size
                # print(spectrum.max()) #peak output for reference
                # clear_output(wait=True) #reference
                data[0:overlap] = data[overlap_inv:FFT_size]
            except KeyboardInterrupt:
                print("Interrupted by user")
        rec_end = True
        event.wait()  # # # Wait until recording is finished
except (queue.Empty):
    # A timeout occured, i.e. there was an error in the callback
    print("Empty Queue")
@mgeier
Copy link
Member

mgeier commented Apr 13, 2021

Did you have a look at https://github.com/spatialaudio/python-sounddevice/blob/master/examples/plot_input.py?

I know it's not using the jack module and it's not calculating the spectrum, but it should be relatively easy to change that.

Regarding your code, I'm not sure if spectrum can be passed to the animate() function like this.
And it looks like spectrum may be accessed by multiple threads at the same time?

Is it possible to move your calculations (including reading from the q) into the animate() function?

@mgeier
Copy link
Member

mgeier commented May 25, 2021

@vyhyb Any news on this?

@vyhyb
Copy link
Author

vyhyb commented May 28, 2021

Yes, here is the final code. Thank you for your help.

import numpy as np
import queue
import sys
import threading

buffersize = 3
clientname = "ImpedanceTubeTest2"
q = queue.Queue(maxsize=buffersize*100)
global rec_end
rec_end = False
event = threading.Event()

def print_error(*args):
    print(*args, file=sys.stderr)


def xrun(delay):
    print_error("An xrun occured, increase JACK's period size?")


def shutdown(status, reason):
    print_error('JACK shutdown!')
    print_error('status:', status)
    print_error('reason:', reason)
    event.set()


def stop_callback(msg=''):
    if msg:
        print_error(msg)
    event.set()
    raise jack.CallbackExit


def process(frames):
    if frames != blocksize:
        stop_callback('blocksize must not be changed, I quit!')
    if rec_end:
        stop_callback()  # Recording is finished
    try:
        q.put_nowait(client.inports[0].get_array()[:])
    except (queue.Full):
        print("Full Queue")
        stop_callback()

from IPython.display import clear_output
from time import sleep
from scipy.signal import stft
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import matplotlib as mpl

mpl.use("Qt5Agg")

size = 4
olap = 0
olap_inv = size - olap

global fig, ax
fig, ax = plt.subplots()

sound = None
log_spec = None

def animate(i):
    global sound, spectrum, stft_data, log_spec
    while True:
        try:
            data = q.get_nowait()
        except queue.Empty:
            break
        shift = len(data)
        sound = np.roll(sound, -shift, axis=0)
        sound[-shift:] = data
    # arr = plt.mlab.specgram(sound, NFFT=blocksize, Fs=48000)[0]
    # print(arr.shape)
    # im.set_array(arr)
    stft_data = stft(sound, fs= samplerate, nperseg=FFT_size)
    spectrum = stft_data[2]*np.conj(stft_data[2])
    # print(spectrum.max())
    log_spec = (5*log_spec + 10*np.log10(spectrum.mean(axis=1)))/6
    line.set_ydata(log_spec)
    return line,

try:
    import jack

    client = jack.Client(clientname, no_start_server=True)
    
    blocksize = client.blocksize
    samplerate = client.samplerate
    FFT_size = size * blocksize
    overlap = olap * blocksize
    overlap_inv = olap_inv*blocksize
    
    client.set_xrun_callback(xrun)
    client.set_shutdown_callback(shutdown)
    client.set_process_callback(process)
    client.inports.register('in_1')

    timeout = blocksize * buffersize / samplerate
    sound = np.ones(blocksize*32)
    stft_data = stft(sound, fs= samplerate, nperseg=FFT_size)
    spectrum = stft_data[2]*np.conj(stft_data[2])
    log_spec = 10*np.log10(spectrum.mean(axis=1))
    line, = ax.plot(stft_data[0], log_spec)
    # line.set_ydata(spectrum.T[0])
    # fig = plt.figure(figsize=(10,5))
    # arr, freqs = plt.mlab.specgram(sound, NFFT=blocksize, Fs=samplerate)[0:2]
    # im = plt.imshow(arr, animated=True)
    ax.set_xlim(20, samplerate/2)
    ax.set_ylim(-140, 0)
    ax.set_xscale('log')
    ani = FuncAnimation(
        fig, animate, interval=timeout, blit=True)
    
    with client:
        target_ports = client.get_ports(
            is_physical=True, is_output=True, is_audio=True)
        client.inports[0].connect(target_ports[0])
        # plt.ion()
        plt.show()
        rec_end = True
        event.wait()  # # # Wait until recording is finished
except (queue.Empty):
    # A timeout occured, i.e. there was an error in the callback
    print("Empty Queue")

@mgeier
Copy link
Member

mgeier commented May 28, 2021

Cool, thanks!

Would you like to contribute this to the examples?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants