def rsh_transform_unit_test(max_degree=3, grid=sg.t_design5200()):
    l, m = zip(*lm_generator(max_degree))
    Y = real_sph_harm_transform(l, m, grid.az, grid.el)
    # compute the grammian
    G = grid.w * Y @ Y.T.conj()
    # should be 1 on the diagonal, 0 off.
    max_error = np.max(np.abs(G - np.identity(G.shape[0])))
    return max_error < 1e-8, max_error, G
예제 #2
0
def allrad(degree,
           order,
           speakers_azimuth,
           speakers_elevation,
           speaker_is_real=None,
           v_az=None,
           v_el=None,
           vbap_norm=True):
    """
    Compute basic decoder matrix by the AllRAD method.

    Parameters
    ----------
    degree : TYPE
        DESCRIPTION.
    order : TYPE
        DESCRIPTION.
    speakers_azimuth : TYPE
        DESCRIPTION.
    speakers_elevation : TYPE
        DESCRIPTION.
    speaker_is_real : TYPE, optional
        DESCRIPTION. The default is None.
    v_az : TYPE, optional
        DESCRIPTION. The default is None.
    v_el : TYPE, optional
        DESCRIPTION. The default is None.
    vbap_norm : TYPE, optional
        DESCRIPTION. The default is True.

    Returns
    -------
    M : TYPE
        DESCRIPTION.

    """
    # defaults
    if v_az is None:
        td = sg.t_design5200()
        v_az = td.az
        v_el = td.el

    Su = np.array(sg.sph2cart(speakers_azimuth, speakers_elevation))
    Vu = np.array(sg.sph2cart(v_az, v_el))

    V2R, Vtri, Vxyz = allrad_v2rp(Su, Vu, vbap_norm=vbap_norm)

    Mv = inversion(degree, order, v_az, v_el)
    M = np.matmul(V2R, Mv)

    if speaker_is_real is not None:
        # get rid of rows corresponding to imaginary speakers
        M = M[speaker_is_real]

    return M
예제 #3
0
def allrad2(degree,
            order,
            spkrs_az,
            spkrs_el,
            v_az=None,
            v_el=None,
            vbap_norm=True):
    """
    Compute decoder by AllRAD2 method.  Not implemented.

    Parameters
    ----------
    degree : TYPE
        DESCRIPTION.
    order : TYPE
        DESCRIPTION.
    spkrs_az : TYPE
        DESCRIPTION.
    spkrs_el : TYPE
        DESCRIPTION.
    v_az : TYPE, optional
        DESCRIPTION. The default is None.
    v_el : TYPE, optional
        DESCRIPTION. The default is None.
    vbap_norm : TYPE, optional
        DESCRIPTION. The default is True.

    Returns
    -------
    M_allrad : TYPE
        DESCRIPTION.

    """
    # defaults
    if v_az is None:
        td = sg.t_design5200()
        v_az = td.az
        v_el = td.el

    # TODO understand and implement allrad2 :) :)
    M_allrad = allrad(degree,
                      order,
                      spkrs_az,
                      spkrs_el,
                      v_az=v_az,
                      v_el=v_el,
                      vbap_norm=vbap_norm)

    # TODO rest of AllRAD2 method

    return M_allrad
def real_sph_harm_inner_product_discrete(l1,
                                         m1,
                                         l2,
                                         m2,
                                         grid=sg.t_design5200()):
    if True:
        Y1 = real_sph_harm_transform(l1, m1, grid.az, grid.el)
        Y2 = real_sph_harm_transform(l2, m2, grid.az, grid.el)
        s = (grid.w * Y1 @ Y2.T)[0, 0]
    else:
        # take advantage of broadcasting
        Y = real_sph_harm_transform((l1, l2), (m1, m2), grid.az, grid.el)
        s = grid.w * Y[0, :] @ Y[1, :].T
    return s
예제 #5
0
def optimize_dome_LF(M_hf,
                     S,
                     ambisonic_order=3,
                     el_lim=-π/8):

    order_h, order_v, sh_l, sh_m, id_string = pc.ambisonic_channels(ambisonic_order)

    # the test directions
    T = sg.t_design5200()
    cap = sg.spherical_cap(T.u, (0, 0, 1), 5*np.pi/6)[0]
    W = np.where(cap, 0.1, 1)

    M_lf, res = optimize_LF(M_hf, S.u.T, sh_l, sh_m, W)

    return M_lf, res
예제 #6
0
def optimize_dome(S,  # the speaker array
                  ambisonic_order=3,
                  el_lim=-π/8,
                  tikhonov_lambda=1e-3,
                  sparseness_penalty=1,
                  do_report=False,
                  rE_goal='auto',
                  eval_order=None,
                  random_start=False
                  ):
    """Test optimizer with CCRMA Stage array."""
    #
    #
    order_h, order_v, sh_l, sh_m, id_string = pc.ambisonic_channels(ambisonic_order)
    order = max(order_h, order_v)  # FIXME
    is_3D = order_v > 0

    if eval_order is None:
        eval_order = ambisonic_order
        eval_order_given = False
    else:
        eval_order_given = True

    eval_order_h, eval_order_v, eval_sh_l, eval_sh_m, eval_id_string = \
        pc.ambisonic_channels(eval_order)

    mask_matrix = pc.mask_matrix(eval_sh_l, eval_sh_m, sh_l, sh_m)
    print(mask_matrix)

    # if True:
    #     S = esa.stage2017() + esa.nadir()#stage()
    #     spkr_array_name = S.name
    # else:
    #     # hack to enter Eric's array
    #     S = emb()
    #     spkr_array_name = 'EMB'

    spkr_array_name = S.name



    print('speaker array = ', spkr_array_name)

    S_u = np.array(sg.sph2cart(S.az, S.el, 1))

    gamma = shelf.gamma(sh_l, decoder_type='max_rE', decoder_3d=is_3D,
                        return_matrix=True)

    figs = []
    if not random_start:
        M_start = 'AllRAD'

        M_allrad = bd.allrad(sh_l, sh_m, S.az, S.el,
                             speaker_is_real=S.is_real)

        # remove imaginary speaker from S_u and Sr
        S_u = S_u[:, S.is_real] #S.Real.values]
        Sr = S[S.is_real] #S.Real.values]

        M_allrad_hf = M_allrad @ gamma

        # performance plots
        plot_title = "AllRAD, "
        if eval_order_given:
            plot_title += f"Design: {id_string}, Test: {eval_id_string}"
        else:
            plot_title += f"Signal set={id_string}"

        figs.append(
            lm.plot_performance(M_allrad_hf, S_u, sh_l, sh_m,
                                mask_matrix = mask_matrix,
                                title=plot_title))

        lm.plot_matrix(M_allrad_hf, title=plot_title)

        print(f"\n\n{plot_title}\nDiffuse field gain of each loudspeaker (dB)")
        for n, g in zip(Sr.ids,
                        10 * np.log10(np.sum(M_allrad ** 2, axis=1))):
            print(f"{n:3}:{g:8.2f} |{'=' * int(60 + g)}")

    else:
        M_start = 'Random'
        # let optimizer dream up a decoder on its own
        M_allrad = None
        # more mess from imaginary speakers
        S_u = S_u[:, S.is_real]
        Sr = S[S.is_real]

    # M_allrad = None

    # Objective for E
    T = sg.t_design5200()
    cap, *_ = sg.spherical_cap(T.u,
                               (0, 0, 1),  # apex
                               π/2 - el_lim)
    E0 = np.where(cap, 1.0, 0.1)  # inside, outside

    # np.array([0.1, 1.0])[cap.astype(np.int8)]

    # Objective for rE order+2 inside the cap, order-2 outside
    rE_goal = np.where(cap,
                       shelf.max_rE_3d(order+2), # inside the cap
                       shelf.max_rE_3d(max(order-2, 1)) # outside the cap
                       )

    #np.array([shelf.max_rE_3d(max(order-2, 1)),
    #                    shelf.max_rE_3d(order+2)])[cap.astype(np.int8)]

    M_opt, res = optimize(M_allrad, S_u, sh_l, sh_m, E_goal=E0,
                          iprint=50, tikhonov_lambda=tikhonov_lambda,
                          sparseness_penalty=sparseness_penalty,
                          rE_goal=rE_goal)

    plot_title = f"Optimized {M_start}, "
    if eval_order_given:
        plot_title += f"Design: {id_string}, Test: {eval_id_string}"
    else:
        plot_title += f"Signal set={id_string}"

    figs.append(
        lm.plot_performance(M_opt, S_u, sh_l, sh_m,
                            mask_matrix = mask_matrix,
                            title=plot_title
                            ))

    lm.plot_matrix(M_opt, title=plot_title)

    with io.StringIO() as f:
        print(f"ambisonic_order = {order}\n" +
              f"el_lim = {el_lim * 180 / π}\n" +
              f"tikhonov_lambda = {tikhonov_lambda}\n" +
              f"sparseness_penalty = {sparseness_penalty}\n",
              file=f)

        off = np.isclose(np.sum(M_opt ** 2, axis=1), 0, rtol=1e-6)  # 60dB down
        print("Using:\n", Sr.ids[~off.copy()], file=f)
        print("Turned off:\n", Sr.ids[off.copy()], file=f)

        print("\n\nDiffuse field gain of each loudspeaker (dB)", file=f)
        for n, g in zip(Sr.ids,
                        10 * np.log10(np.sum(M_opt ** 2, axis=1))):
            print(f"{n:3}:{g:8.2f} |{'=' * int(60 + g)}", file=f)
        report = f.getvalue()
        print(report)

    if do_report:
        reports.html_report(zip(*figs),
                            text=report,
                            directory=spkr_array_name,
                            name=f"{spkr_array_name}-{id_string}")

    return M_opt, dict(M_allrad=M_allrad, off=off, res=res)
예제 #7
0
        s_el = (0, 0, 0, 0, pi / 2, -pi / 2)
    else:
        s = sg.t_design()
        s_az = s.az
        s_el = s.el

    Su = np.array(sg.sph2cart(s_az, s_el))

    tri = Delaunay(Su.transpose())
    H = tri.convex_hull

    # assemble the list of face vertices
    p0 = tri.points[H[:, 0], :]
    p1 = tri.points[H[:, 1], :]
    p2 = tri.points[H[:, 2], :]

    V = sg.t_design5200()

    origin = np.array([0, 0, 0])
    a = []
    Hr = np.arange(len(H))
    for i in range(5200):
        flag, u, v, t = rti_fun(origin, V.u[:, i], p0, p1, p2)
        valid = np.logical_and(flag, t > 0)
        face = Hr[valid][0]
        ur = u[valid][0]
        vr = v[valid][0]
        tr = t[valid][0]
        a.append((face, ur, vr, tr))
    return a
def optimize_LF(M, Su, sh_l, sh_m, W=1, raise_error_on_failure=False):

    M_shape = M.shape
    g_spkr, g_total = lm.diffuse_field_gain(M)

    # the test directions
    T = sg.t_design5200()
    cap = sg.spherical_cap(T.u, (0, 0, 1), 5 * np.pi / 6)[0]
    W = np.where(cap, 1, 1)

    Y_test = rsh.real_sph_harm_transform(sh_l, sh_m, T.az, T.el)
    rExyz, E = rE(M, Su, Y_test)
    rEu, rEr = xyz2ur(rExyz)

    # define the loss function
    def o(x):
        M = x.reshape(M_shape)
        rVxyz, P = rV(M, Su, Y_test)

        df_gain = np.sum(M * M)

        df_gain_loss = (g_total - df_gain)**2

        # Tikhonov regularization term - typical value = 1e-3
        tikhonov_regularization_term = np.sum(M**2) * 1e-2  #tikhonov_lambda

        # dir loss mag(rVxyz) should be 1
        direction_loss = np.sum(W * ((rVxyz - rEu)**2))
        P_loss = np.sum(W * ((P - 1)**2))
        return (direction_loss + df_gain_loss + P_loss / 100000 +
                tikhonov_regularization_term)

    val_and_grad_fn = jax.value_and_grad(o)
    val_and_grad_fn = jax.jit(val_and_grad_fn)

    def objective_and_gradient(x):
        v, g = val_and_grad_fn(x)
        g = onp.array(g, order='F')
        return v, g

    x0 = M.ravel()
    with Timer() as t:
        res = opt.minimize(
            objective_and_gradient,
            x0,
            bounds=opt.Bounds(-1, 1),
            method='L-BFGS-B',
            jac=True,
            options=dict(disp=50,
                         # maxls=50,
                         # maxcor=30,
                         # gtol=1e-8,
                         # ftol=1e-12
                         ),
            # callback=callback,
        )
    if True:
        print()
        print(f"Execution time: {t.interval:0.3f} sec.")
        print(res.message)
        print(res)
        print()

    if res.status != 0 and raise_error_on_failure:
        print('bummer:', res.message)
        raise RuntimeError(res.message)

    M_opt = res.x.reshape(M_shape)
    return M_opt, res
def optimize(
    M,
    Su,
    sh_l,
    sh_m,
    E_goal=None,
    iprint=50,
    tikhonov_lambda=1.0e-3,
    sparseness_penalty=1,
    uniform_loudness_penalty=0.1,  #0.01
    rE_goal=1.0,
    maxcor=100,  # how accurate is the Hessian, more is better but slower
    raise_error_on_failure=True):
    """Optimize psychoacoustic criteria."""
    #
    # handle defaults
    if E_goal is None:
        E_goal = 1

    if rE_goal == 'auto' or rE_goal is None:
        #FIXME This assumes 3D arrays
        rE_goal = shelf.max_rE_3d(np.max(sh_l) + 2)

    print(f"rE_goal min={np.min(rE_goal)} max={np.max(rE_goal)}")

    # the test directions
    T = sg.t_design5200()
    Y_test = rsh.real_sph_harm_transform(sh_l, sh_m, T.az, T.el)

    if M is None:
        # infer M_shape from Su and Y
        M_shape = (
            Su.shape[1],  # number of loudspeakers
            Y_test.shape[0],  # number of program channels
        )
        M = random.uniform(key, shape=M_shape, minval=-1.0, maxval=1.0)
    else:
        M_shape = M.shape

    # the loss function
    def o(x) -> float:
        M = x.reshape(M_shape)
        rExyz, E = rE(M, Su, Y_test)

        # truncation loss due to finite order
        truncation_loss = np.sum((rExyz - T.u * rE_goal)**2)

        # uniform loudness loss
        uniform_loudness_loss = (np.sum(
            (E - E_goal)**2) * uniform_loudness_penalty)  # was 10

        # Tikhonov regularization term - typical value = 1e-3
        tikhonov_regularization_term = np.sum(M**2) * tikhonov_lambda

        # don't turn off speakers
        # pull diffuse-field gain for each speaker away from zero
        sparsness_term = (np.sum(np.abs(1 - np.sum(M**2, axis=1))) * 100 *
                          sparseness_penalty)

        # the entire loss function
        f = (truncation_loss + uniform_loudness_loss +
             tikhonov_regularization_term + sparsness_term)
        return f

    # consult the automatic differentiation oracle
    val_and_grad_fn = jax.value_and_grad(o)
    val_and_grad_fn = jax.jit(val_and_grad_fn)

    def objective_and_gradient(x, *args):
        v, g = val_and_grad_fn(x, *args)
        # I'm not to happy about having to copy g but L-BGFS needs it in
        # Fortran order and JAX only does C order.  Check with g.flags
        # NOTE: asarray() and asfortranarray() don't work correctly here
        g = onp.array(g, order='F')
        return v, g

    x0 = M.ravel()  # initial guess
    with Timer() as t:
        # https://docs.scipy.org/doc/scipy/reference/optimize.minimize-lbfgsb.html
        res = opt.minimize(
            objective_and_gradient,
            x0,
            bounds=opt.Bounds(-1, 1),
            method='L-BFGS-B',
            jac=True,
            options=dict(
                maxcor=maxcor,
                disp=iprint,
                maxls=500,
                # maxcor=30,
                gtol=1e-8,
                #ftol=1e-12
            ),
            # callback=callback,
        )
    if True:
        print()
        print(f"Execution time: {t.interval:0.3f} sec.")
        print(res.message)
        print(res)
        print()

    if res.status != 0 and raise_error_on_failure:
        print('bummer:', res.message)
        raise RuntimeError(res.message)

    M_opt = res.x.reshape(M_shape)
    return M_opt, res