This code example is to animate VBAP, VBIP, MDAP interactively

Acoustic Holography and Holophony

Franz Zotter, 2016

This animation is about what the rE and rV measures are for VBAP, VBIP, MDAP, and MDIP.


In [6]:
import numpy as np
import scipy as sp
from numpy.linalg import inv

def vectorpan(xys,xyls,simplices):
    g=np.zeros(xyls.shape[1])
    for n in range(0,simplices.shape[0]):
        na=simplices[n,0]
        nb=simplices[n,1]
        M=np.array([xyls[:,na],xyls[:,nb]]).T
        gnm=np.dot(inv(M),xys)
        if np.sum(gnm<-1e-3)==0:
            g[na]=gnm[0]
            g[nb]=gnm[1]
            break
    return g


def r_vector(g,xyls):
    thx=xyls[0,:]
    thy=xyls[1,:]
    rx=np.dot(thx,g).T
    ry=np.dot(thy,g).T
    normalizer=sum(g,0)
    rx/=normalizer
    ry/=normalizer
    return np.array([rx,ry])

from scipy.spatial import ConvexHull
import math

L=5
phils=np.arange(0,L)*2*np.pi/L
xyls=np.array([np.cos(phils),np.sin(phils)])
phis=0
xys=np.array([np.cos(phis),np.sin(phis)])
qh=ConvexHull(xyls.T)
    
def get_panning_gains(xys,xyls,simplices,pantype):
    if (pantype == 'MDAP') or (pantype == 'MDIP'):
        alpha=np.arccos(np.dot(xyls[:,0],xyls[:,1]))
        R=np.array([np.cos(alpha/2), -np.sin(alpha/2), np.sin(alpha/2), np.cos(alpha/2)]).reshape(2,2)
        xys2=np.dot(R,xys)
        xys3=np.dot(R.T,xys)
        g=vectorpan(xys,xyls,simplices)+vectorpan(xys2,xyls,simplices)+vectorpan(xys3,xyls,simplices)
        if pantype == 'MDIP':
            g=np.abs(g)
            g=np.sqrt(g)
        g/=np.sqrt(np.sum(g**2))
    elif pantype == 'VBAP':
        g=vectorpan(xys,xyls,simplices)
    else:
        g=np.sqrt(np.abs(vectorpan(xys,xyls,simplices)))
    return g

from bokeh.plotting import figure, output_file, show
from bokeh.io import push_notebook, output_notebook
import ipywidgets as widgets
import time, threading

L_widget = widgets.IntSlider(min=4, max=12, step=1,value=L,description="L")
phis_widget= widgets.FloatSlider(min=-180.0,max=180.0,step=1.0,value=0.0,description="phi")
pantype_widget= widgets.SelectionSlider(options=['VBAP','VBIP','MDAP','MDIP'],value='VBAP',description="weight")
animate_widget=widgets.Checkbox(value=False,description="anim")

L=L_widget.value;
phils=np.mod((2*np.pi*np.arange(0,L))/L+np.pi,2*np.pi)-np.pi
xys=np.array([np.cos(phis),np.sin(phis)])
qh=ConvexHull(xyls.T)
simplices=qh.simplices
phis=phis_widget.value*np.pi/180.0
xys=np.array([np.cos(phis),np.sin(phis)])
pantype=pantype_widget.value
gls=get_panning_gains(xys,xyls,simplices,pantype)

output_notebook()
p = figure(title="2D VBAP, VBIP, MDAP, and MDIP Panning",plot_width=600, plot_height=270, x_range=(-180,180), y_range=(-.4,1.1))
ll=p.circle(phils*180/np.pi, gls , line_width=3, color="red")
pp=p.line(np.array([phis, phis])*180/np.pi, np.array([0, 1]), color="black")
rE=r_vector(gls**2,xyls)
dirE=np.arctan2(rE[1],rE[0])*180/np.pi;
lenE=np.sqrt(np.sum(rE**2))
prE=p.line(np.array([1, 1])*dirE, np.array([0, 1])*lenE, color="red",line_width=3,legend="rE")
rV=r_vector(gls,xyls)
dirV=np.arctan2(rV[1],rV[0])*180/np.pi;
lenV=np.sqrt(np.sum(rV**2))
prV=p.line(np.array([1, 1])*dirV, np.array([0, 1])*lenV, color="green",line_width=3,legend="rV")
show(p,notebook_handle=True)

def update_plot(xys,xyls,simplices,pantype):
    pp.data_source.data['x']=np.mod(np.array([1, 1])*np.arctan2(xys[1],xys[0])*180/np.pi+180,360)-180
    gls=get_panning_gains(xys,xyls,simplices,pantype)
    ll.data_source.data['y']=gls
    ll.data_source.data['x']=np.arctan2(xyls[1,:],xyls[0,:])*180/np.pi
    rE=r_vector(gls**2,xyls)
    dirE=np.arctan2(rE[1],rE[0])*180/np.pi;
    lenE=np.sqrt(np.sum(rE**2))
    prE.data_source.data['x']=np.array([1, 1])*dirE
    prE.data_source.data['y']=np.array([0, 1])*lenE
    rV=r_vector(gls,xyls)
    dirV=np.arctan2(rV[1],rV[0])*180/np.pi;
    lenV=np.sqrt(np.sum(rV**2))
    prV.data_source.data['x']=np.array([1, 1])*dirV
    prV.data_source.data['y']=np.array([0, 1])*lenV
    push_notebook()

def on_value_change(change):
    global simplices
    phis=phis_widget.value*np.pi/180.0
    xys=np.array([np.cos(phis),np.sin(phis)])
    pantype=pantype_widget.value
    L=L_widget.value
    phils=np.mod((2*np.pi*np.arange(0,L))/L+np.pi,2*np.pi)-np.pi
    xyls=np.array([np.cos(phils),np.sin(phils)])
    if change.owner == L_widget:
        qh=ConvexHull(xyls.T)
        simplices=qh.simplices
    update_plot(xys,xyls,simplices,pantype)

#widgets.jslink((play,'value'),(phis_widget,'value'))
#interactive(update_plot, L=L_widget, phis=phis_widget, weight=weights_widget)

phis_widget.observe(on_value_change,names='value')
L_widget.observe(on_value_change,names='value')
pantype_widget.observe(on_value_change,names='value')

def animate_plot(change):
    if animate_widget.value:
        phis=phis_widget.value
        phis=np.mod(phis+3+180,360)-180
        phis_widget.value=phis
        threading.Timer(0.001, animate_plot,[1]).start()

animate_widget.observe(animate_plot,names='value')
widgets.HBox([phis_widget,L_widget,pantype_widget,animate_widget])


Loading BokehJS ...