pax_global_header00006660000000000000000000000064141725640110014513gustar00rootroot0000000000000052 comment=a4d439e3e9302189e441fb84040c97c6ea2cf2b7 vbap-1.2.0/000077500000000000000000000000001417256401100124435ustar00rootroot00000000000000vbap-1.2.0/.gitignore000066400000000000000000000000671417256401100144360ustar00rootroot00000000000000# builds *.pd_linux *.pd_darwin *.dll *.o *.so *.dylib vbap-1.2.0/CHANGELOG.txt000066400000000000000000000014221417256401100144720ustar00rootroot000000000000002018 TBD version 1.2.0 * transitioned Makefile to use pd-lib-builder (Dan Wilcox) * added loudspeaker set definition and current active set info outlet (Chikashi Miyama) * lowered loudspeaker intersection epsilon for float-based math (Chikashi Miyama) * limit min spread size to avoid NaN explosion (Chikashi Miyama) * updated help files and removed pddp links for now (Dan Wilcox) * commented out a few more extraneous logposts * various formatting fixes 2014 Aug 14 version 1.1.0 * changed vbap to allocate memory dynamically, to eliminate crash-producing memory overwrites when larger speaker configurations were defined, e.g. on OSX for speakers > 13 in 3D (Z. Settel) * eliminated a post to the console reporting azimuth updates (Z. Settel) version 1.0.3.2 and earlier... vbap-1.2.0/LICENSE.txt000066400000000000000000000031641417256401100142720ustar00rootroot00000000000000Copyright (c) 1998-2010 Ville Pulkki 2002, Juha Vehviläinen 2003, Olaf Matthes 2006-2010, Hans-Christoph Steiner 2007, Frank Barknecht All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY VILLE PULKKI ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL VILLE PULKKI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of Ville Pulkki. vbap-1.2.0/Makefile000066400000000000000000000013261417256401100141050ustar00rootroot00000000000000# Makefile to build class 'vbap' for Pure Data. # Needs Makefile.pdlibbuilder as helper makefile for platform-dependent build # settings and rules. # library name lib.name = vbap # input source file (class name == source file basename) class.sources = vbap.c rvbap.c define_loudspeakers.c # all extra files to be included in binary distribution of the library datafiles = max2pd.h vbap.h \ README.md LICENSE.txt CHANGELOG.txt \ $(wildcard *.pd) $(wildcard examples/*.pd) # ignore unused object creation new function warnings suppress-wunused = yes # include Makefile.pdlibbuilder, allow override using PDLIBBUILDER_DIR variable PDLIBBUILDER_DIR=. include $(PDLIBBUILDER_DIR)/Makefile.pdlibbuilder vbap-1.2.0/Makefile.pdlibbuilder000066400000000000000000001270261417256401100165530ustar00rootroot00000000000000# Makefile.pdlibbuilder dated 2019-12-21 version = 0.6.0 # Helper makefile for Pure Data external libraries. # Written by Katja Vetter March-June 2015 for the public domain. No warranties. # Inspired by Hans Christoph Steiner's Makefile Template and Stephan Beal's # ShakeNMake. # # Grab the newest version of Makefile.pdlibbuilder from # https://github.com/pure-data/pd-lib-builder/ # # GNU make version >= 3.81 required. # # #=== characteristics =========================================================== # # # - defines build settings based on autodetected OS and architecture # - defines rules to build Pd class- or lib executables from C or C++ sources # - defines rules for libdir installation # - defines convenience targets for developer and user # - evaluates implicit dependencies for non-clean builds # # #=== basic usage =============================================================== # # # In your Makefile, define your Pd lib name and class files, and include # Makefile.pdlibbuilder at the end of the Makefile. Like so: # # ________________________________________________________________________ # # # Makefile for mylib # # lib.name = mylib # # class.sources = myclass1.c myclass2.c # # datafiles = myclass1-help.pd myclass2-help.pd README.txt LICENSE.txt # # include Makefile.pdlibbuilder # ________________________________________________________________________ # # # For files in class.sources it is assumed that class basename == source file # basename. The default target builds all classes as individual executables # with Pd's default extension for the platform. For anything more than the # most basic usage, continue reading. # # #=== list of Makefile.pdlibbuilder API variables =============================== # # # Variables available for definition in your library Makefile: # # - lib.name # - lib.setup.sources # - class.sources # - common.sources # - shared.sources # - .class.sources # - .class.ldflags # - .class.ldlibs # - cflags # - ldflags # - ldlibs # - datafiles # - datadirs # - makefiles # - makefiledirs # - externalsdir # # Optional multiline defines evaluated per operating system: # # - forLinux # - forDarwin # - forWindows # # Variables available for your makefile or make command line: # # - make-lib-executable # - suppress-wunused # # Path variables for make command line or environment: # # - PDDIR # - PDINCLUDEDIR # - PDBINDIR # - PDLIBDIR # # Standard make variables for make command line or environment: # # - CPPFLAGS # - CFLAGS # - LDFLAGS # - CC # - CXX # - INSTALL # - STRIP # - DESTDIR # # Optional user variables for make command line or environment: # # - PLATFORM # # Deprecated path variables: # # - pdincludepath # - pdbinpath # - objectsdir # # #=== descriptions of Makefile.pdlibbuilder API variables ======================= # # # lib.name: # Name of the library directory as it will be installed / distributed. Also the # name of the lib executable in the case where all classes are linked into # a single binary. # # lib.setup.sources: # Source file(s) (C or C++) which must be compiled only when linking all classes # into a single lib binary. # # class.sources: # All sources files (C or C++) for which the condition holds that # class name == source file basename. # # .class.sources: # Source file(s) (C or C++) specific to class . Use this for # multiple-source classes or when class name != source file basename. # # common.sources: # Source file(s) which must be statically linked to each class in the library. # # shared.sources: # Source file(s) (C or C++) to build a shared dynamic link lib, to be linked # with all class executables. # # cflags, ldflags, ldlibs: # Define cflags (preprocessor&compiler), ldflags (linker) and ldlibs (dynamic # link libs) for the whole library. These flags are added to platform-specific # flags defined by Makefile.pdlibbuilder. # # .class.ldflags and .class.ldlibs: # Define ldflags resp. ldlibs specific to class . These flags are # added to platform-specific flags defined by Makefile.pdlibbuilder, and flags # defined in your Makefile for the whole library. Note: cflags can not be # defined per class in the current implementation. # # datafiles and datadirs: # All extra files you want to include in binary distributions of the # library: abstractions and help patches, example patches, meta patch, readme # and license texts, manuals, sound files, etcetera. Use 'datafiles' for all # files that should go into your lib rootdir and 'datadirs' for complete # directories you want to copy from source to distribution. # # forLinux, forDarwin, forWindows: # Shorthand for 'variable definitions for Linux only' etc. Use like: # define forLinux # cflags += -DLINUX # class.sources += linuxthing.c # endef # # makefiles and makefiledirs: # Extra makefiles or directories with makefiles that should be made in sub-make # processes. # # make-lib-executable: # When this variable is defined 'yes' in your makefile or as command argument, # Makefile.pdlibbuilder will try to build all classes into a single library # executable (but it will force exit if lib.setup.sources is undefined). # If your makefile defines 'make-lib-executable=yes' as the library default, # this can still be overridden with 'make-lib-executable=no' as command argument # to build individual class executables (the Makefile.pdlibbuilder default.) # # suppress-wunused: # When this variable is defined ('yes' or any other value), -Wunused-variable, # -Wunused-parameter, -Wunused-value and -Wunused-function are suppressed, # but the other warnings from -Wall are retained. # # PDDIR: # Root directory of 'portable' pd package. When defined, PDINCLUDEDIR and # PDBINDIR will be evaluated as $(PDDIR)/src and $(PDDIR)/bin. # # PDINCLUDEDIR: # Directory where Pd API m_pd.h should be found, and other Pd header files. # Overrides the default search path. # # PDBINDIR: # Directory where pd.dll should be found for linking (Windows only). Overrides # the default search path. # # PDLIBDIR: # Root directory for installation of Pd library directories. Overrides the # default install location. # # DESTDIR: # Prepended path component for staged install. # # PLATFORM: # Target platform for cross compilation in the form of GNU triplet: # cpu-vendor-os. Example: x86_64-w64-mingw32. This specifies the tool chain that # pdlibbuilder will use, if installed and locatable. System and architecture # will then be autodefined accordingly. In most cases no other variables need to # be overridden. # # CPPFLAGS: # Preprocessor flags which are not strictly required for building. # # CFLAGS: # Compiler flags which are not strictly required for building. Compiler flags # defined by Makefile.pdlibbuilder for warning, optimization and architecture # specification are overriden by CFLAGS. # # LDFLAGS: # Linker flags which are not strictly required for building. Linker flags # defined by Makefile.pdlibbuilder for architecture specification are overriden # by LDFLAGS. # # CC and CXX: # C and C++ compiler programs as defined in your build environment. # # INSTALL # Definition of install program. # # STRIP # Name of strip program. Default 'strip' can be overridden in cross compilation # environments. # # objectsdir: # Root directory for installation of Pd library directories, like PDLIBDIR but # not overridable by environment. Supported for compatibility with pd-extended # central makefile, but deprecated otherwise. # # pdincludepath, pdbinpath: # As PDINCLUDEDIR and PDBINDIR but not overridable by environment. Deprecated # as user variables. # # #=== paths ===================================================================== # # # Source files in directories other than current working directory must be # prefixed with their relative path. Do not rely on VPATH or vpath. # Object (.o) files are built in the directory of their source files. # Executables are built in current working directory. # # Default search path for m_pd.h and other API header files is platform # dependent, and overridable by PDINCLUDEDIR: # # Linux: /usr/include/pd # # OSX: /Applications/Pd*.app/Contents/Resources/src # # Windows: %PROGRAMFILES%/Pd/src # %PROGRAMFILES(X86)%/Pd/src (32 bit builds on 64 bit Windows) # # Default search path for binary pd.dll (Windows), overridable by PDBINDIR # # %PROGRAMFILES%/Pd/bin # %PROGRAMFILES(X86)%/Pd/bin (32 bit builds on 64 bit Windows) # # Default location to install pd libraries is platform dependent, and # overridable by PDLIBDIR: # # Linux: /usr/local/lib/pd-externals # OSX: ~/Library/Pd # Windows: %APPDATA%/Pd # # https://puredata.info/docs/faq/how-do-i-install-externals-and-help-files # The rationale for not installing to ~/pd-externals by default on Linux # is that some people share the home dir between 32 and 64 bit installations. # # #=== targets =================================================================== # # # all: build $(executables) plus optional post target # post: target to build after $(executables) # alldebug: build all with -g option turned on for debug symbols # : force clean build of an individual class # .pre: make preprocessor output file in current working directory # .lst: make asm/source output file in current working directory # # install: install executables and data files # clean: remove build products from source tree # # help: print help text # vars: print makefile variables # allvars: print all variables # depend: print generated prerequisites # dumpmachine: print compiler output of option '-dumpmachine' # coffee: dummy target # # Variable $(executables) expands to class executables plus optional shared lib, # or alternatively to single lib executable when make-lib-executable=true. # Targets pre and post can be defined by library makefile. Make sure to include # Makefile.pdlibbuilder first so default target all will not be redefined. # # #=== Pd-extended libdir concept ================================================ # # # For libdir layout as conceived by Hans-Christoph Steiner, see: # # https://puredata.info/docs/developer/Libdir # # Files README.txt, LICENSE.txt and -meta.pd are part of the libdir # convention. Help patches for each class and abstraction are supposed to be # available. Makefile.pdlibbuilder does not force the presence of these files # however. It does not automatically include such files in libdir installations. # Data files you want to include in distributions must be defined explicitly in # your Makefile. # # #=== Makefile.pdlibbuilder syntax conventions ================================== # # # Makefile.pdlibbuilder variable names are lower case. Default make variables, # environment variables, and standard user variables (CC, CXX, CFLAGS, DESTDIR) # are upper case. Use target 'allvars' to print all variables and their values. # # 'Fields' in data variables are separated by dots, like in 'foo.class.sources'. # Words in variables expressing a function or command are separated by dashes, # like in 'make-lib-executable'. # # #=== useful make options ======================================================= # # # Use 'make -d ' to print debug details of the make process. # Use 'make -p ' to print make's database. # # #=== TODO ====================================================================== # # # - decide whether to use -static-libgcc or shared dll in MinGW # - cygwin support # - android support # - figure out how to handle '$' in filenames # - add makefile template targets dpkg-source dist libdir distclean tags? # # #=== end of documentation sections ============================================= # # ################################################################################ ################################################################################ ################################################################################ # GNU make version 3.81 (2006) or higher is required because of the following: # - function 'info' # - variable '.DEFAULT_GOAL' # force exit when make version is < 3.81 ifneq ($(firstword $(sort 3.81 $(MAKE_VERSION))), 3.81) $(error GNU make version 3.81 or higher is required) endif # Relative path to externals root dir in multi-lib source tree like # pd-extended SVN. Default is parent of current working directory. May be # defined differently in including makefile. externalsdir ?= .. # variable you can use to check if Makefile.pdlibbuilder is already included Makefile.pdlibbuilder = true ################################################################################ ### target platform detection ################################################## ################################################################################ #=== target platform =========================================================== # PLATFORM: optional user variable to define target platform for cross # compilation. Redefine build tools accordingly. PLATFORM should match # the exact target prefix of tools present in $PATH, like x86_64-w64-mingw32, # x86_64-apple-darwin12 etc. Tool definitions are exported to ensure submakes # will get the same. ifneq ($(PLATFORM),) ifneq ($(findstring darwin, $(PLATFORM)),) export CC = $(PLATFORM)-cc export CXX = $(PLATFORM)-c++ export CPP = $(PLATFORM)-cc else export CC = $(PLATFORM)-gcc export CXX = $(PLATFORM)-g++ export CPP = $(PLATFORM)-cpp endif STRIP = $(PLATFORM)-strip endif # Let (native or cross-) compiler report target triplet and isolate individual # words therein to facilitate later processing. target.triplet := $(subst -, ,$(shell $(CC) -dumpmachine)) #=== operating system ========================================================== # The following systems are defined: Linux, Darwin, Windows. GNU and # GNU/kFreeBSD are treated as Linux to get the same options. ifneq ($(filter linux gnu% kfreebsd, $(target.triplet)),) system = Linux endif ifneq ($(filter darwin%, $(target.triplet)),) system = Darwin endif ifneq ($(filter mingw% cygwin%, $(target.triplet)),) system = Windows endif # evaluate possible system-specific multiline defines from library makefile $(eval $(for$(system))) # TODO: Cygwin, Android #=== architecture ============================================================== # The following CPU names can be processed by pdlibbuilder: # i*86 Intel 32 bit # x86_64 Intel 64 bit # arm ARM 32 bit # aarch64 ARM 64 bit target.arch := $(firstword $(target.triplet)) ################################################################################ ### variables per platform ##################################################### ################################################################################ #=== flags per architecture ==================================================== # Set architecture-dependent cflags, mainly for Linux. For Mac and Windows, # arch.c.flags are overriden below. To see gcc's default architecture flags: # $ gcc -Q --help=target # ARMv6: Raspberry Pi 1st gen, not detectable from target.arch ifeq ($(shell uname), armv6l) arch.c.flags = -march=armv6 -mfpu=vfp -mfloat-abi=hard # ARMv7: Beagle, Udoo, RPi2 etc. else ifeq ($(target.arch), arm) arch.c.flags = -march=armv7-a -mfpu=vfpv3 -mfloat-abi=hard # ARMv8 64 bit, not tested yet else ifeq ($(target.arch), aarch64) arch.c.flags = -mcpu=cortex-a53 # Intel 32 bit, build with SSE and SSE2 instructions else ifneq ($(filter i%86, $(target.arch)),) arch.c.flags = -march=pentium4 -mfpmath=sse -msse -msse2 # Intel/AMD 64 bit, build with SSE, SSE2 and SSE3 instructions else ifeq ($(target.arch), x86_64) arch.c.flags = -march=core2 -mfpmath=sse -msse -msse2 -msse3 # if none of the above architectures detected else arch.c.flags = endif #=== flags and paths for Linux ================================================= ifeq ($(system), Linux) prefix = /usr/local libdir := $(prefix)/lib pkglibdir = $(libdir)/pd-externals pdincludepath := $(wildcard /usr/include/pd) extension = pd_linux cpp.flags := -DUNIX c.flags := -fPIC c.ldflags := -rdynamic -shared -fPIC -Wl,-rpath,"\$$ORIGIN",--enable-new-dtags c.ldlibs := -lc -lm cxx.flags := -fPIC -fcheck-new cxx.ldflags := -rdynamic -shared -fPIC -Wl,-rpath,"\$$ORIGIN",--enable-new-dtags cxx.ldlibs := -lc -lm -lstdc++ shared.extension = so shared.ldflags = -rdynamic -fPIC -shared -Wl,-soname,$(shared.lib) endif #=== flags and paths for Darwin ================================================ # LLVM-clang doesn't support -fcheck-new, therefore this flag is only used when # compiling with g++. ifeq ($(system), Darwin) pkglibdir = $(HOME)/Library/Pd pdincludepath := $(firstword $(wildcard \ /Applications/Pd*.app/Contents/Resources/src)) extension = pd_darwin cpp.flags := -DUNIX -DMACOSX -I /sw/include c.flags := c.ldflags := -undefined suppress -flat_namespace -bundle c.ldlibs := -lc cxx.ldflags := -undefined suppress -flat_namespace -bundle cxx.ldlibs := -lc shared.extension = dylib shared.ldflags = -dynamiclib -undefined dynamic_lookup \ -install_name @loader_path/$(shared.lib) \ -compatibility_version 1 -current_version 1.0 ifneq ($(filter %g++, $(CXX)),) cxx.flags := -fcheck-new endif ifeq ($(extension), d_fat) arch := i386 x86_64 else arch := $(target.arch) endif ifneq ($(filter -mmacosx-version-min=%, $(cflags)),) version.flag := $(filter -mmacosx-version-min=%, $(cflags)) else version.flag = -mmacosx-version-min=10.6 endif arch.c.flags := $(addprefix -arch , $(arch)) $(version.flag) arch.ld.flags := $(arch.c.flags) endif #=== flags and paths for Windows =============================================== # Standard paths on Windows contain spaces, and GNU make functions treat such # paths as lists, with unintended effects. Therefore we must use shell function # ls instead of make's wildcard when probing for a path, and use double quotes # when specifying a path in a command argument. # Default paths in Mingw / Mingw-w64 environments. 'PROGRAMFILES' is standard # location for builds with native architecture, 'ProgramFiles(x86)' for i686 # builds on x86_64 Windows (detection method by Lucas Cordiviola). Curly braces # required because of parentheses in variable name. ifeq ($(system), Windows) pkglibdir := $(APPDATA)/Pd ifeq ($(target.arch), i686) programfiles := ${ProgramFiles(x86)} else programfiles := $(PROGRAMFILES) endif pdbinpath := $(programfiles)/Pd/bin pdincludepath := $(programfiles)/Pd/src endif # Store default path to pd.dll in PDBINDIR if the latter is not user-defined. # For include path this is done in the platform-independent paths section below, # but for PDBINDIR it is done here so ld flags can be evaluated as immediate # variables. ifeq ($(system), Windows) ifdef PDDIR PDBINDIR := $(PDDIR)/bin endif PDBINDIR ?= $(pdbinpath) endif # TODO: decide whether -mms-bitfields should be specified. ifeq ($(system), Windows) cpp.flags := -DMSW -DNT ifeq ($(target.arch), i686) arch.c.flags := -march=pentium4 -msse -msse2 -mfpmath=sse else ifeq ($(target.arch), x86_64) cpp.flags := -DMSW -DNT -DPD_LONGINTTYPE=__int64 arch.c.flags := -march=core2 -msse -msse2 -msse3 -mfpmath=sse else arch.c.flags = endif extension = dll c.flags := c.ldflags := -static-libgcc -shared \ -Wl,--enable-auto-import "$(PDBINDIR)/pd.dll" c.ldlibs := cxx.flags := -fcheck-new cxx.ldflags := -static-libgcc -static-libstdc++ -shared \ -Wl,--enable-auto-import "$(PDBINDIR)/pd.dll" cxx.ldlibs := shared.extension = dll shared.ldflags := -static-libgcc -shared "$(PDBINDIR)/pd.dll" stripflags = --strip-all endif #=== paths ===================================================================== # Platform-dependent default paths are specified above, but overridable. # Path variables in upper case can be defined as make command argument or in the # environment. Variable 'objectsdir' is supported for compatibility with # the build system that pd-l2ork has inherited from pd-extended. PDINCLUDEDIR ?= $(pdincludepath) PDLIBDIR ?= $(firstword $(objectsdir) $(pkglibdir)) ifdef PDDIR PDINCLUDEDIR := $(wildcard $(PDDIR)/src) endif # base path where all components of the lib will be installed by default installpath := $(DESTDIR)$(PDLIBDIR)/$(lib.name) # check if include path contains spaces (as is often the case on Windows) # if so, store the path so we can later do checks with it pdincludepathwithspaces := $(if $(word 2, $(PDINCLUDEDIR)), $(PDINCLUDEDIR)) #=== accumulated build flags =================================================== # From GNU make docs: 'Users expect to be able to specify CFLAGS freely # themselves.' So we use CFLAGS to define options which are not strictly # required for compilation: optimizations, architecture specifications, and # warnings. CFLAGS can be safely overriden using a make command argument. # Variables cflags, ldflags and ldlibs may be defined in including makefile. optimization.flags = -O3 -ffast-math -funroll-loops -fomit-frame-pointer warn.flags = -Wall -Wextra -Wshadow -Winline -Wstrict-aliasing # suppress -Wunused-variable & Co if you don't want to clutter a build log ifdef suppress-wunused warn.flags += $(addprefix -Wno-unused-, function parameter value variable) endif CFLAGS = $(warn.flags) $(optimization.flags) $(arch.c.flags) # preprocessor flags cpp.flags := -DPD -I "$(PDINCLUDEDIR)" $(cpp.flags) $(CPPFLAGS) # flags for dependency checking (cflags from makefile may define -I options) depcheck.flags := $(cpp.flags) $(cflags) # architecture specifications for linker are overridable by LDFLAGS LDFLAGS := $(arch.ld.flags) # now add the same ld flags to shared dynamic lib shared.ldflags += $(LDFLAGS) # accumulated flags for C compiler / linker c.flags := $(cpp.flags) $(c.flags) $(cflags) $(CFLAGS) c.ldflags := $(c.ldflags) $(ldflags) $(LDFLAGS) c.ldlibs := $(c.ldlibs) $(ldlibs) # accumulated flags for C++ compiler / linker cxx.flags := $(cpp.flags) $(cxx.flags) $(cflags) $(CFLAGS) cxx.ldflags := $(cxx.ldflags) $(ldflags) $(LDFLAGS) cxx.ldlibs := $(cxx.ldlibs) $(ldlibs) ################################################################################ ### variables: library name and version ######################################## ################################################################################ # strip possibles spaces from lib.name, they mess up calculated file names lib.name := $(strip $(lib.name)) # if meta file exists, check library version metafile := $(wildcard $(lib.name)-meta.pd) ifdef metafile lib.version := $(shell sed -n \ 's|^\#X text [0-9][0-9]* [0-9][0-9]* VERSION \(.*\);|\1|p' \ $(metafile)) endif ################################################################################ ### variables: files ########################################################### ################################################################################ #=== sources =================================================================== # (re)define .class.sources using file names in class.sources define add-class-source $(notdir $(basename $v)).class.sources += $v endef $(foreach v, $(class.sources), $(eval $(add-class-source))) # derive class names from .class.sources variables sourcevariables := $(filter %.class.sources, $(.VARIABLES)) classes := $(basename $(basename $(sourcevariables))) # accumulate all source files specified in makefile classes.sources := $(sort $(foreach v, $(sourcevariables), $($v))) all.sources := $(classes.sources) $(lib.setup.sources) \ $(shared.sources) $(common.sources) #=== object files ============================================================== # construct object filenames from all C and C++ source file names classes.objects := $(addsuffix .o, $(basename $(classes.sources))) common.objects := $(addsuffix .o, $(basename $(common.sources))) shared.objects := $(addsuffix .o, $(basename $(shared.sources))) lib.setup.objects := $(addsuffix .o, $(basename $(lib.setup.sources))) all.objects = $(classes.objects) $(common.objects) $(shared.objects) \ $(lib.setup.objects) #=== executables =============================================================== # construct class executable names from class names classes.executables := $(addsuffix .$(extension), $(classes)) # Construct shared lib executable name if shared sources are defined. If # extension and shared extension are not identical, use both to facilitate co- # installation for different platforms, like .m_i386.dll and .m_amd64.dll. ifdef shared.sources ifeq ($(extension), $(shared.extension)) shared.lib = lib$(lib.name).$(shared.extension) else shared.lib = lib$(lib.name).$(extension).$(shared.extension) endif else shared.lib := endif ################################################################################ ### variables: tools ########################################################### ################################################################################ # aliases so we can later define 'compile-$1' and set 'c' or 'cxx' as argument compile-c := $(CC) compile-cxx := $(CXX) ################################################################################ ### checks ##################################################################### ################################################################################ # At this point most variables are defined. Now do some checks and info's # before rules begin. # print Makefile.pdlibbuilder version before possible termination $(info ++++ info: using Makefile.pdlibbuilder version $(version)) # Terminate if target triplet remained empty, to avoid all sorts of confusing # scenarios and spurious bugs. ifeq ($(target.triplet),) $(error Command "$(CC) -dumpmachine" did not return a target triplet, \ needed for a build. \ Is compiler "$(CC)" installed in your PATH? ($(PATH)). \ Does compiler "$(CC)" support option "-dumpmachine"?) endif # 'forward declaration' of default target, needed to do checks all: # To avoid unpredictable results, make sure the default target is not redefined # by including makefile. ifneq ($(.DEFAULT_GOAL), all) $(error Default target must be 'all'.) endif # find out which target(s) will be made ifdef MAKECMDGOALS goals := $(MAKECMDGOALS) else goals := all endif # store path to Pd API m_pd.h if it is found ifdef PDINCLUDEDIR mpdh := $(shell ls "$(PDINCLUDEDIR)/m_pd.h") endif # store path to pd.dll; if not found, ls will give a useful error ifeq ($(system), Windows) pddll := $(shell ls "$(PDBINDIR)/pd.dll") endif # when making target all, check if m_pd.h is found and print info about it ifeq ($(goals), all) $(if $(mpdh), \ $(info ++++ info: using Pd API $(mpdh)), \ $(warning Where is Pd API m_pd.h? Do 'make help' for info.)) endif # print target info $(info ++++ info: making target $(goals) $(if $(lib.name),in lib $(lib.name))) # when installing, print installpath info $(if $(filter install install-lib, $(goals)), $(info ++++ info: \ installpath is '$(installpath)')) #=== define executables ======================================================== # By default we build class executables, and optionally a shared dynamic link # lib. When make-lib-executable=yes we build all classes into a single lib # executable, on the condition that variable lib.setup.sources is defined. ifeq ($(make-lib-executable),yes) $(if $(lib.setup.sources), ,\ $(error Can not build library blob because lib.setup.sources is undefined)) executables := $(lib.name).$(extension) else executables := $(classes.executables) $(shared.lib) endif ################################################################################ ### rules: special targets ##################################################### ################################################################################ # Disable built-in rules. If some target can't be built with the specified # rules, it should not be built at all. MAKEFLAGS += --no-builtin-rules .PRECIOUS: .SUFFIXES: .PHONY: all post build-lib \ $(classes) $(makefiledirs) $(makefiles) \ install install-executables install-datafiles install-datadirs \ force clean vars allvars depend help ################################################################################ ### rules: build targets ####################################################### ################################################################################ # Target all forces the build of targets [$(executables) post] in # deterministic order. Target $(executables) builds class executables plus # optional shared lib or alternatively a single lib executable when # make-lib-executable=true. Target post is optionally defined by # library makefile. all: post post: $(executables) all: $(info ++++info: target all in lib $(lib.name) completed) # build all with -g option turned on for debug symbols alldebug: c.flags += -g alldebug: cxx.flags += -g alldebug: all #=== class executable ========================================================== # recipe for linking objects in class executable # argument $1 = compiler type (c or cxx) # argument $2 = class basename define link-class $(compile-$1) \ $($1.ldflags) $($2.class.ldflags) \ -o $2.$(extension) \ $(addsuffix .o, $(basename $($2.class.sources))) \ $(addsuffix .o, $(basename $(common.sources))) \ $($1.ldlibs) $($2.class.ldlibs) $(shared.lib) endef # general rule for linking object files in class executable %.$(extension): $(shared.lib) $(info ++++ info: linking objects in $@ for lib $(lib.name)) $(if $(filter %.cc %.cpp, $($*.class.sources)), \ $(call link-class,cxx,$*), \ $(call link-class,c,$*)) #=== library blob ============================================================== # build all classes into single executable build-lib: $(lib.name).$(extension) $(info ++++ info: library blob $(lib.name).$(extension) completed) # recipe for linking objects in lib executable # argument $1 = compiler type (c or cxx) define link-lib $(compile-$1) \ $($1.ldflags) $(lib.ldflags) \ -o $(lib.name).$(extension) $(all.objects) \ $($1.ldlibs) $(lib.ldlibs) endef # rule for linking objects in lib executable # declared conditionally to avoid name clashes ifeq ($(make-lib-executable),yes) $(lib.name).$(extension): $(all.objects) $(if $(filter %.cc %.cpp, $(all.sources)), \ $(call link-lib,cxx), \ $(call link-lib,c)) endif #=== shared dynamic lib ======================================================== # recipe for linking objects in shared executable # argument $1 = compiler type (c or cxx) define link-shared $(compile-$1) \ $(shared.ldflags) \ -o $(shared.lib) $(shared.objects) \ $($1.ldlibs) $(shared.ldlibs) endef # rule for linking objects in shared executable # build recipe is in macro 'link-shared' $(shared.lib): $(shared.objects) $(info ++++ info: linking objects in shared lib $@) $(if $(filter %.cc %.cpp, $(shared.sources)), \ $(call link-shared,cxx), \ $(call link-shared,c)) #=== object files ============================================================== # recipe to make .o file from source # argument $1 is compiler type (c or cxx) define make-object-file $(info ++++ info: making $@ in lib $(lib.name)) $(compile-$1) \ $($1.flags) \ -o $@ -c $< endef # Three rules to create .o files. These are double colon 'terminal' rules, # meaning they are the last in a rules chain. %.o:: %.c $(call make-object-file,c) %.o:: %.cc $(call make-object-file,cxx) %.o:: %.cpp $(call make-object-file,cxx) #=== explicit prerequisites for class executables ============================== # For class executables, prerequisite rules are declared in run time. Target # 'depend' prints these rules for debugging purposes. # declare explicit prerequisites rule like 'class: class.extension' # argument $v is class basename define declare-class-target $v: $v.$(extension) endef # declare explicit prerequisites rule like 'class.extension: object1.o object2.o' # argument $v is class basename define declare-class-executable-target $v.$(extension): $(addsuffix .o, $(basename $($v.class.sources))) \ $(addsuffix .o, $(basename $(common.sources))) endef # evaluate explicit prerequisite rules for all classes $(foreach v, $(classes), $(eval $(declare-class-target))) $(foreach v, $(classes), $(eval $(declare-class-executable-target))) #=== implicit prerequisites for class executables ============================== # Evaluating implicit prerequisites (header files) with help from the # preprocessor is 'expensive' so this is done conditionally and selectively. # Note that it is also possible to trigger a build via install targets, in # which case implicit prerequisites are not checked. # When the Pd include path contains spaces it will mess up the implicit # prerequisites rules. disable-dependency-tracking := $(strip $(pdincludepathwithspaces)) ifndef disable-dependency-tracking must-build-everything := $(filter all, $(goals)) must-build-class := $(filter $(classes), $(goals)) must-build-sources := $(foreach v, $(must-build-class), $($v.class.sources)) endif # declare implicit prerequisites rule like 'object.o: header1.h header2.h ...' # argument $1 is input source file(s) # dir is explicitly added because option -MM strips it by default define declare-object-target $(dir $1)$(filter %.o: %.h, $(shell $(CPP) $(depcheck.flags) -MM $1)) $(MAKEFILE_LIST) endef # evaluate implicit prerequisite rules when rebuilding everything ifdef must-build-everything $(if $(wildcard $(all.objects)), \ $(info ++++ info: evaluating implicit prerequisites in lib $(lib.name).....) \ $(foreach v, $(all.sources), $(eval $(call declare-object-target, $v)))) endif # evaluate implicit prerequisite rules when selectively building classes ifdef must-build-class $(foreach v, $(must-build-sources), \ $(eval $(call declare-object-target, $v))) $(foreach v, $(shared.sources), \ $(eval $(call declare-object-target, $v))) endif ################################################################################ ### rules: preprocessor and assembly files ##################################### ################################################################################ # Preprocessor and assembly output files for bug tracing etc. They are not part # of the build processes for executables. By default these files are created in # the current working directory. Dependency tracking is not performed, the build # is forced instead to make sure it's up to date. force: #=== preprocessor file ========================================================= # make preprocessor output file with extension .pre # argument $1 = compiler type (c or cxx) define make-preprocessor-file $(info ++++ info: making preprocessor output file $(notdir $*.pre) \ in current working directory) $(compile-$1) -E $< $(c.flags) $($1.flags) -o $(notdir $*.pre) endef %.pre:: %.c force $(call make-preprocessor-file,c) %.pre:: %.cc force $(call make-preprocessor-file,cxx) %.pre:: %.cpp force $(call make-preprocessor-file,cxx) #=== assembly file ============================================================= # make C / assembly interleaved output file with extension .lst # argument $1 = compiler type (c or cxx) define make-assembly-file $(info ++++ info: making assembly output file $(notdir $*.lst) \ in current working directory) $(compile-$1) \ -c -Wa,-a,-ad -fverbose-asm \ $($1.flags) \ $< > $(notdir $*.lst) endef %.lst:: %.c force $(call make-assembly-file,c) %.lst:: %.cc force $(call make-assembly-file,cxx) %.lst:: %.cpp force $(call make-assembly-file,cxx) ################################################################################ ### rules: installation targets ################################################ ################################################################################ #=== strip ===================================================================== # Stripping of installed binaries will only be done when variable 'stripflags' # is defined non-empty. No default definition is provided except for Windows # where the unstripped binaries are large, especially in the case of Mingw-w64. # Note: while stripping all symbols ('-s' or '--strip-all') is possible for # Linux and Windows, in the case of OSX only non-global symbols can be stripped # (option '-x' or '--discard-all'). # Make definition of strip command overridable so it can be defined in an # environment for cross-compilation. STRIP ?= strip # Commands in 'strip-executables' will be executed conditionally in the rule for # target 'install-executables'. strip-executables = cd "$(installpath)" && \ $(foreach v, $(executables), $(STRIP) $(stripflags) '$v';) #=== install =================================================================== # Install targets depend on successful exit status of target all because nothing # must be installed in case of a build error. # -p = preserve time stamps # -m = set permission mode (as in chmod) # -d = create all components of specified directories INSTALL = install INSTALL_PROGRAM := $(INSTALL) -p -m 644 INSTALL_DATA := $(INSTALL) -p -m 644 INSTALL_DIR := $(INSTALL) -m 755 -d # strip spaces from file names executables := $(strip $(executables)) datafiles := $(strip $(datafiles)) datadirs := $(strip $(datadirs)) # Do not make any install sub-target with empty variable definition because the # install program would exit with an error. install: $(if $(executables), install-executables) install: $(if $(datafiles), install-datafiles) install: $(if $(datadirs), install-datadirs) install-executables: all $(INSTALL_DIR) -v "$(installpath)" $(foreach v, $(executables), \ $(INSTALL_PROGRAM) '$v' "$(installpath)";) $(info ++++ info: executables of lib $(lib.name) installed \ from $(CURDIR) to $(installpath)) $(if $(stripflags), $(strip-executables),) install-datafiles: all $(INSTALL_DIR) -v "$(installpath)" $(foreach v, $(datafiles), \ $(INSTALL_DATA) '$(v)' "$(installpath)";) $(info ++++ info: data files of lib $(lib.name) installed \ from $(CURDIR) to $(installpath)) install-datadirs: all $(foreach v, $(datadirs), $(INSTALL_DIR) "$(installpath)/$v";) $(foreach v, $(datadirs), \ $(INSTALL_DATA) $(wildcard $v/*) "$(installpath)/$v";) $(info ++++ info: data directories of lib $(lib.name) installed \ from $(CURDIR) to $(installpath)) ################################################################################ ### rules: distribution targets ################################################ ################################################################################ # TODO # These targets are implemented in Makefile Template, but I have to figure out # how to do it under the not-so-strict conditions of Makefile.pdlibbuilder. # make source package dist: @echo "target dist not yet implemented" # make Debian source package dpkg-source: @echo "target dpkg-source not yet implemented" $(ORIGDIR): $(DISTDIR): ################################################################################ ### rules: clean targets ####################################################### ################################################################################ # delete build products from build tree clean: rm -f $(all.objects) rm -f $(classes.executables) $(lib.name).$(extension) $(shared.lib) rm -f *.pre *.lst # remove distribution directories and tarballs from build tree distclean: clean @echo "target distclean not yet implemented" ################################################################################ ### rules: submake targets ##################################################### ################################################################################ # Iterate over sub-makefiles or makefiles in other directories. # When 'continue-make=yes' is set, sub-makes will report 'true' to the parent # process regardless of their real exit status. This prevents the parent make # from being aborted by a sub-make error. Useful when you want to quickly find # out which sub-makes from a large set will succeed. ifeq ($(continue-make),yes) continue = || true endif # These targets will trigger sub-make processes for entries in 'makefiledirs' # and 'makefiles'. all alldebug install clean distclean dist dkpg-source: \ $(makefiledirs) $(makefiles) # this expands to identical rules for each entry in 'makefiledirs' $(makefiledirs): $(MAKE) --directory=$@ $(MAKECMDGOALS) $(continue) # this expands to identical rules for each entry in 'makefiles' $(makefiles): $(MAKE) --directory=$(dir $@) --makefile=$(notdir $@) $(MAKECMDGOALS) $(continue) ################################################################################ ### rules: convenience targets ################################################# ################################################################################ #=== show variables ============================================================ # Several 'function' macro's cause errors when expanded within a rule or without # proper arguments. Variables which are set with the define directive are only # shown by name for that reason. functions = \ add-class-source \ declare-class-target \ declare-class-executable-target \ declare-object-target \ link-class \ link-lib \ link-shared \ make-object-file \ make-preprocessor-file \ make-assembly-file # show variables from makefiles vars: $(info ++++ info: showing makefile variables:) $(foreach v,\ $(sort $(filter-out $(functions) functions, $(.VARIABLES))),\ $(if $(filter file, $(origin $v)),\ $(info variable $v = $($v)))) $(foreach v, $(functions), $(info 'function' name: $v)) @echo # show all variables allvars: $(info ++++ info: showing default, automatic and makefile variables:) $(foreach v, \ $(sort $(filter-out $(functions) functions, $(.VARIABLES))), \ $(info variable ($(origin $v)) $v = $($v))) $(foreach v, $(functions), $(info 'function' name: $v)) @echo #=== show dependencies ========================================================= # show generated prerequisites rules depend: $(info ++++ info: generated prerequisite rules) $(foreach v, $(classes), $(info $(declare-class-target))) $(foreach v, $(classes), $(info $(declare-class-executable-target))) $(foreach v, $(all.sources), $(info $(call declare-object-target, $v))) @echo #=== show help text ============================================================ # brief info about targets and paths ifdef mpdh mpdhinfo := $(mpdh) else mpdhinfo := m_pd.h was not found. Is Pd installed? endif help: @echo @echo " Main targets:" @echo " all: build executables (default target)" @echo " install: install all components of the library" @echo " vars: print makefile variables for troubleshooting" @echo " allvars: print all variables for troubleshooting" @echo " help: print this help text" @echo @echo " Pd API m_pd.h:" @echo " $(mpdhinfo)" @echo " You may specify your preferred Pd include directory as argument" @echo " to the make command, like 'PDINCLUDEDIR=path/to/pd/src'." @echo @echo " Path for installation of your libdir(s):" @echo " $(PDLIBDIR)" @echo " Alternatively you may specify your path for installation as argument" @echo " to the make command, like 'PDLIBDIR=path/to/pd-externals'." @echo @echo " Default paths are listed in the doc sections in Makefile.pdlibbuilder." @echo #=== platform test ============================================================= # This target can be used to test if the compiler for specified PLATFORM is # correctly defined and available. dumpmachine: @$(CC) -dumpmachine #=== dummy target ============================================================== coffee: @echo "Makefile.pdlibbuilder: Can not make coffee. Sorry." ################################################################################ ### end of rules sections ###################################################### ################################################################################ # for syntax highlighting in vim and github # vim: set filetype=make: vbap-1.2.0/README.txt000066400000000000000000000042601417256401100141430ustar00rootroot00000000000000# vbap A Pure Data port of the VBAP object for Max/MSP Originally written by Ville Pulkki Helsinki University of Technology Laboratory of Acoustics and Audio Signal Processing http://www.acoustics.hut.fi/research/cat/vbap/ ## Description This is a library for Pure Data for sound spatialization using the vector base amplitude panning (VBAP) method. VBAP is an amplitude panning method to position virtual sources in arbitrary 2-D or 3-D loudspeaker setups. In amplitude panning the same sound signal is applied to a number of loudspeakers with appropriate non-zero amplitudes. With 2-D setups VBAP is a reformulation of the existing pair-wise panning method. However, differing from earlier solutions it can be generalized for 3-D loudspeaker setups as a triplet-wise panning method. A sound signal is then applied to one, two, or three loudspeakers simultaneously. VBAP has certain advantages compared to earlier virtual source positioning methods in arbitrary layouts. Previous methods either used all loudspeakers to produce virtual sources, which results in some artefacts, or they used loudspeaker triplets with a non-generalizable 2-D user interface. The directional qualities of virtual sources generated with VBAP can be stated as follows. Directional coordinates used for this purpose are the angle between a position vector and the median plane (θcc), and the angle between a projection of a position vector to the median plane and frontal direction (Φcc). The perceived θcc direction of a virtual source coincides well with the VBAP panning direction when a loudspeaker set is near the median plane. When the loudspeaker set is moved towards a side of a listener, the perceived θcc direction is biased towards the median plane. The perceived Φcc direction of an amplitude-panned virtual source is individual and cannot be predicted with any panning law. ## Build This is a pure C external and can be compiled simply with: make ## Usage To load this external in Pd, include the directory containing the following files in your Pd search paths: * vbap.pd_*, rvbap.pd-*, define_loudspeakers.pd_*: compiled external objects * vbap-help.pd, rvbap-help.pd, define_loudspeakers-help.pd: help files vbap-1.2.0/define_loudspeakers-help.pd000066400000000000000000000027611417256401100177370ustar00rootroot00000000000000#N canvas 95 333 828 340 10; #X obj 48 201 define_loudspeakers 3 -45 0 45 0 0 45 180 45; #X msg 48 180 bang; #X obj 148 253 define_loudspeakers 2 -45 45 0 180; #X obj 112 295 vbap 0 0; #X text 17 19 [define_loudspeakers] works with; #X msg 442 189 ls-directions 3 -30 0 30 0 -90 0 90 0 0 0 -150 0 150 0 180 45 -45 45 45 45; #X msg 431 94 ls-triplets 1 2 7 2 7 8 1 3 7; #X obj 431 295 define_loudspeakers 2 -40 0 40 80 130 180 -130 -80; #X text 447 260 2-D setup -> only loudspeaker azimuths specified:; #X text 446 275 (2 azi1 azi2 azi3 azi4...); #X text 443 155 3-D setup -> (azimuth elevation) pairs are specified: ; #X text 443 170 (3 azi1 ele1 azi2 ele2 ...); #X msg 148 232 bang; #X text 190 232 a 2D setup; #X text 93 185 a 3D setup; #X text 18 50 Use [define_loudspeakers] to list the speaker positions. The example here defines loudspeakers in three dimensions (the first parameter). For each speaker \, define its azimuth and elevation. Here we have speakers front left and right with no elevation (-45 0 45 0) and front and back with 45 degrees of elevation (0 45 180 45).; #X text 18 137 In two dimensions \, only specify the azimuth. For example: "define_loudspeakers 2 -45 45 0 180"; #X obj 249 19 vbap; #X text 179 297 See [vbap] for more details; #X text 427 49 If you are not happy with the triangle selections of define_loudspeakers \, you can define your own. (For experts only.) ; #X connect 0 0 3 0; #X connect 1 0 0 0; #X connect 2 0 3 0; #X connect 5 0 7 0; #X connect 6 0 7 0; #X connect 12 0 2 0; vbap-1.2.0/define_loudspeakers.c000066400000000000000000000665441417256401100166410ustar00rootroot00000000000000/* define_loudspeakers.c 1.00b1----> x-max4.2 written by Ville Pulkki 1999-2003 Helsinki University of Technology and Unversity of California at Berkeley See copyright in file with name LICENSE.txt */ #include "vbap.h" static t_class *def_ls_class; /* so max can identify your object */ static void def_ls_bang(t_def_ls *x); //static void def_ls_int(t_def_ls *x, long n); static void def_ls_read_directions(t_def_ls *x, t_symbol *s, int ac, Atom *av); static void def_ls_read_triplets(t_def_ls *x, t_symbol *s, int ac, Atom *av); static void *def_ls_new(t_symbol *s, int ac, Atom *av); //static void def_ls(float g[3], long ls[3], t_def_ls *x); static void ls_angles_to_cart(t_ls *ls); static void choose_ls_triplets(t_def_ls *x); static int any_ls_inside_triplet(int a, int b, int c,t_ls lss[MAX_LS_AMOUNT],int ls_amount); static void add_ldsp_triplet(int i, int j, int k, t_def_ls *x); static t_float vec_angle(t_ls v1, t_ls v2); static t_float vec_length(t_ls v1); static t_float vec_prod(t_ls v1, t_ls v2); static t_float vec_prod(t_ls v1, t_ls v2); static t_float vol_p_side_lgth(int i, int j,int k, t_ls lss[MAX_LS_AMOUNT] ); static void ls_cross_prod(t_ls v1,t_ls v2, t_ls *res); static int lines_intersect(int i,int j,int k,int l,t_ls lss[MAX_LS_AMOUNT]); static void calculate_3x3_matrixes(t_def_ls *x); static void choose_ls_tuplets(t_def_ls *x); static int calc_2D_inv_tmatrix(t_float azi1,t_float azi2, t_float inv_mat[4],t_float mat[4]); static void sort_2D_lss(t_ls lss[MAX_LS_AMOUNT], int sorted_lss[MAX_LS_AMOUNT], int ls_amount); static void initContent_ls_directions(t_def_ls *x,int ac,Atom*av); void vbap_def_ls(t_def_ls *x, t_symbol *s, int ac, Atom *av); #include #if (defined PD_FLOATSIZE) && (PD_FLOATSIZE == 64) && (defined DBL_EPSILON) # define TFLT_EPSILON DBL_EPSILON #else # define TFLT_EPSILON FLT_EPSILON # define fabs fabsf #define acos acosf #endif #ifndef VBAP_OBJECT # ifdef PD // If we are within VBAP (which includes define_loudspeakers), then don't create a main for define_loudspeakres void define_loudspeakers_setup(void) { def_ls_class = class_new(gensym("define_loudspeakers"), (t_newmethod)def_ls_new, 0, (short)sizeof(t_def_ls), 0, A_GIMME, 0); /* def_ls_new = creation function, A_DEFLONG = its (optional) argument is a long (32-bit) int */ class_addbang(def_ls_class, (t_method)def_ls_bang); /* the procedure it uses when it gets a bang in the left inlet */ class_addmethod(def_ls_class, (t_method)def_ls_read_directions, gensym("ls-directions"), A_GIMME, 0); class_addmethod(def_ls_class, (t_method)def_ls_read_triplets, gensym("ls-triplets"), A_GIMME, 0); post(DFLS_VERSION); } # else /* Max */ void main(void) { setup((t_messlist **)&def_ls_class, (method)def_ls_new, 0L, (short)sizeof(t_def_ls), 0L, A_GIMME, 0); /* def_ls_new = creation function, A_DEFLONG = its (optional) argument is a long (32-bit) int */ addbang((method)def_ls_bang); /* the procedure it uses when it gets a bang in the left inlet */ addmess((method)def_ls_read_directions, "ls-directions", A_GIMME, 0); addmess((method)def_ls_read_triplets, "ls-triplets", A_GIMME, 0); addmess((method)traces, "enabletrace", A_LONG, 0); post(DFLS_VERSION); } # endif /* PD */ #endif /* ! VBAP_OBJECT */ static void def_ls_bang(t_def_ls *x) /* x = reference to this instance of the object */ { // calculate and print out chosen loudspeaker sets and corresponding matrices if(x->x_ls_read == 1) { if(x->x_def_ls_amount < x->x_def_ls_dimension) { error("define-loudspeakers: Too few loudspeakers!"); return; } else { if(x->x_def_ls_dimension == 3) { if(x->x_triplets_specified == 0) choose_ls_triplets(x); calculate_3x3_matrixes(x); } else if(x->x_def_ls_dimension == 2) { choose_ls_tuplets(x); } else { error("define-loudspeakers: Error in loudspeaker direction data"); error("dimension azimuth1 [elevation1] azimuth2 [elevation2]..."); error("dimension == 2 for horizontal ls arrays"); error("dimension == 3 for 3-D ls arrays (speakers also upward and/or downward "); } } } else { error("define-loudspeakers: Error in loudspeaker direction data"); error("dimension azimuth1 [elevation1] azimuth2 [elevation2]..."); error("dimension == 2 for horizontal ls arrays"); error("dimension == 3 for 3-D ls arrays (speakers also upward and/or downward "); } } /*--------------------------------------------------------------------------*/ /* void def_ls_int(t_def_ls *x, long n) // x = the instance of the object, n = the int received in the right inlet { // do something if an int comes in the left inlet??? } */ static void def_ls_read_triplets(t_def_ls *x, t_symbol *s, int ac, Atom *av) // when loudspeaker triplets come in a message { t_ls_set *trip_ptr, *tmp_ptr; (void)s; if(x->x_ls_read == 0) { error("define_loudspeakers: Define loudspeaker directions first!"); return; } if(x->x_def_ls_dimension == 2) { error("define_loudspeakers: Can't specify loudspeaker triplets in 2-D setup!"); return; } trip_ptr = x->x_ls_set; while(trip_ptr != NULL) { tmp_ptr = trip_ptr; trip_ptr = trip_ptr->next; freebytes(tmp_ptr, sizeof(struct t_ls_set)); } x->x_ls_set = NULL; int i; for(i = 0; i < ac; i += 3) { long l1 = 0, l2 = 0, l3 = 0; /* if(av[i].a_type == A_LONG) l1 = av[i].a_w.w_long; else */ if(av[i].a_type == A_FLOAT) l1 = (long) av[i].a_w.w_float; /* if(av[i+1].a_type == A_LONG) l2 = av[i+1].a_w.w_long; else */ if(av[i+1].a_type == A_FLOAT) l2 = (long) av[i+1].a_w.w_float; /* if(av[i+2].a_type == A_LONG) l3 = av[i+2].a_w.w_long; else */ if(av[i+2].a_type == A_FLOAT) l3 = (long) av[i+2].a_w.w_float; add_ldsp_triplet(l1-1, l2-1, l3-1, x); } x->x_triplets_specified=1; } static void def_ls_read_directions(t_def_ls *x, t_symbol *s, int ac, Atom *av) // when loudspeaker directions come in a message { (void)s; if(x->x_ls_read) { // Remove old matrices t_ls_set* trip_ptr = x->x_ls_set; while(trip_ptr != NULL) { // remove old matrices t_ls_set* tmp_ptr = trip_ptr; trip_ptr = trip_ptr->next; freebytes(tmp_ptr, sizeof(struct t_ls_set)); } } x->x_ls_set = NULL; initContent_ls_directions(x, ac, av); } /*--------------------------------------------------------------------------*/ static void ls_angles_to_cart(t_ls *ls) // convert angular direction to cartesian { t_float azi = ls->azi; t_float ele = ls->ele; ls->x = cos((t_float) azi * atorad) * cos((t_float) ele * atorad); ls->y = sin((t_float) azi * atorad) * cos((t_float) ele * atorad); ls->z = sin((t_float) ele * atorad); } /* create new instance of object... MUST send it an int even if you do nothing with this int!! */ static void *def_ls_new(t_symbol *s, int ac, Atom *av) { (void)s; //post("def_ls_new: AC = %d", ac); // s is object name (we ignore it) t_def_ls *x = (t_def_ls *)newobject(def_ls_class); #ifdef PD x->x_outlet0 = outlet_new(&x->x_obj, gensym("list")); /* create a (list) outlet */ #else /* Max */ x->x_outlet0 = outlet_new(x, 0L); /* create a (list) outlet */ #endif /* PD */ initContent_ls_directions(x, ac, av); // Initialize object internal data from a ls-directions list return x; /* return a reference to the object instance */ } /* define-loudspeakers message integrated into vbap object */ void vbap_def_ls(t_def_ls *x, t_symbol *s, int ac, Atom *av) { (void)s; initContent_ls_directions(x, ac, av); // Initialize object internal data from a ls-directions list //logpost(NULL, 3, "vbap_def_ls: %ld-D configuration with %ld speakers", x->x_def_ls_dimension, x->x_def_ls_amount ); def_ls_bang(x); // calculate and send matrix to vbap } /** Initialize the object content from parameters : ls-directions list */ static void initContent_ls_directions(t_def_ls *x, int ac, Atom*av) { x->x_ls_read = 0; long d = 0; /* if (av[0].a_type == A_LONG) d = av[0].a_w.w_long; else */ if(av[0].a_type == A_FLOAT) d = (long)av[0].a_w.w_float; else { error("define-loudspeakers: dimension NaN"); return; } if(d==2 || d==3) { x->x_def_ls_dimension= d; x->x_ls_read = 1; } else { x->x_def_ls_dimension = 0; error("define-loudspeakers: Dimension has to be 2 or 3!"); return; } int pointer = 1; x->x_def_ls_amount= (ac-1) / (x->x_def_ls_dimension - 1); // read loudspeaker direction angles int i; for(i = 0; i < x->x_def_ls_amount; i++) { t_float azi = 0; /* if(av[pointer].a_type == A_LONG) azi = (float) av[pointer].a_w.w_long; else */ if(av[pointer].a_type == A_FLOAT) azi = av[pointer].a_w.w_float; else { error("define-loudspeakers: direction angle #%d NaN", i+1); x->x_ls_read = 0; return; } x->x_ls[i].azi = azi; pointer++; t_float ele = 0; // in 2d elevation is zero if(x->x_def_ls_dimension == 3) { // 3-D /* if(av[pointer].a_type == A_LONG) ele = (float) av[pointer].a_w.w_long; else */ if(av[pointer].a_type == A_FLOAT) ele = av[pointer].a_w.w_float; else { error("define-loudspeakers: elevation #%d NaN", i+1); x->x_ls_read = 0; return; } pointer++; } x->x_ls[i].ele = ele; } if(x->x_ls_read == 1) { int j; for(j = 0; jx_def_ls_amount; j++) { ls_angles_to_cart(&x->x_ls[j]); } } x->x_triplets_specified=0; x->x_ls_set = NULL; } static void choose_ls_triplets(t_def_ls *x) /* Selects the loudspeaker triplets, and calculates the inversion matrices for each selected triplet. A line (connection) is drawn between each loudspeaker. The lines denote the sides of the triangles. The triangles should not be intersecting. All crossing connections are searched and the longer connection is erased. This yields non-intesecting triangles, which can be used in panning. See theory in paper Pulkki, V. Lokki, T. "Creating Auditory Displays with Multiple Loudspeakers Using VBAP: A Case Study with DIVA Project" in International Conference on Auditory Displays -98.*/ { int i, j, k, l, /*m, li, */ table_size; //int *i_ptr; //t_ls vb1, vb2, tmp_vec; int connections[MAX_LS_AMOUNT][MAX_LS_AMOUNT]; //float angles[MAX_LS_AMOUNT]; //int sorted_angles[MAX_LS_AMOUNT]; t_float distance_table[((MAX_LS_AMOUNT * (MAX_LS_AMOUNT - 1)) / 2)]; int distance_table_i[((MAX_LS_AMOUNT * (MAX_LS_AMOUNT - 1)) / 2)]; int distance_table_j[((MAX_LS_AMOUNT * (MAX_LS_AMOUNT - 1)) / 2)]; t_float distance; t_ls_set *trip_ptr, *prev, *tmp_ptr; int ls_amount = x->x_def_ls_amount; t_ls *lss = x->x_ls; if(ls_amount == 0) { post("define-loudspeakers: Number of loudspeakers is zero"); return; } for(i = 0; i < ls_amount; i++) for(j = i+1; j < ls_amount; j++) for(k = j+1; k < ls_amount; k++) { if(vol_p_side_lgth(i, j, k, x->x_ls) > MIN_VOL_P_SIDE_LGTH) { connections[i][j] = 1; connections[j][i] = 1; connections[i][k] = 1; connections[k][i] = 1; connections[j][k] = 1; connections[k][j] = 1; add_ldsp_triplet(i, j, k, x); } } /*calculate distancies between all lss and sorting them*/ table_size = (((ls_amount - 1) * (ls_amount)) / 2); for(i = 0; i < table_size; i++) distance_table[i] = 100000.0; for(i = 0; i < ls_amount; i++) { for(j = (i+1); j < ls_amount; j++) { if(connections[i][j] == 1) { distance = fabs(vec_angle(lss[i], lss[j])); k = 0; while(distance_table[k] < distance) k++; for(l=(table_size - 1); l > k ; l--) { distance_table[l] = distance_table[l-1]; distance_table_i[l] = distance_table_i[l-1]; distance_table_j[l] = distance_table_j[l-1]; } distance_table[k] = distance; distance_table_i[k] = i; distance_table_j[k] = j; } else { table_size--; } } } /* disconnecting connections which are crossing shorter ones, starting from shortest one and removing all that cross it, and proceeding to next shortest */ for(i = 0; i < (table_size); i++) { int fst_ls = distance_table_i[i]; int sec_ls = distance_table_j[i]; if(connections[fst_ls][sec_ls] == 1) { for(j = 0; j < ls_amount ; j++) { for(k = j+1; k < ls_amount; k++) { if((j != fst_ls) && (k != sec_ls) && (k != fst_ls) && (j != sec_ls)) { if(lines_intersect(fst_ls, sec_ls, j, k, x->x_ls) == 1) { connections[j][k] = 0; connections[k][j] = 0; } } } } } } /* remove triangles which had crossing sides with smaller triangles or include loudspeakers*/ trip_ptr = x->x_ls_set; prev = NULL; while (trip_ptr != NULL) { i = trip_ptr->ls_nos[0]; j = trip_ptr->ls_nos[1]; k = trip_ptr->ls_nos[2]; if(connections[i][j] == 0 || connections[i][k] == 0 || connections[j][k] == 0 || any_ls_inside_triplet(i, j, k, x->x_ls, ls_amount) == 1 ) { if(prev != NULL) { prev->next = trip_ptr->next; tmp_ptr = trip_ptr; trip_ptr = trip_ptr->next; freebytes(tmp_ptr, sizeof(struct t_ls_set)); } else { x->x_ls_set = trip_ptr->next; tmp_ptr = trip_ptr; trip_ptr = trip_ptr->next; freebytes(tmp_ptr, sizeof(struct t_ls_set)); } } else { prev = trip_ptr; trip_ptr = trip_ptr->next; } } x->x_triplets_specified = 1; } int any_ls_inside_triplet(int a, int b, int c, t_ls lss[MAX_LS_AMOUNT], int ls_amount) /* returns 1 if there is loudspeaker(s) inside given ls triplet */ { t_float invdet; t_ls *lp1, *lp2, *lp3; t_float invmx[9]; int i, j; t_float tmp; int any_ls_inside, this_inside; lp1 = &(lss[a]); lp2 = &(lss[b]); lp3 = &(lss[c]); /* matrix inversion */ invdet = 1.0 / ( lp1->x * ((lp2->y * lp3->z) - (lp2->z * lp3->y)) - lp1->y * ((lp2->x * lp3->z) - (lp2->z * lp3->x)) + lp1->z * ((lp2->x * lp3->y) - (lp2->y * lp3->x))); invmx[0] = ((lp2->y * lp3->z) - (lp2->z * lp3->y)) * invdet; invmx[3] = ((lp1->y * lp3->z) - (lp1->z * lp3->y)) * -invdet; invmx[6] = ((lp1->y * lp2->z) - (lp1->z * lp2->y)) * invdet; invmx[1] = ((lp2->x * lp3->z) - (lp2->z * lp3->x)) * -invdet; invmx[4] = ((lp1->x * lp3->z) - (lp1->z * lp3->x)) * invdet; invmx[7] = ((lp1->x * lp2->z) - (lp1->z * lp2->x)) * -invdet; invmx[2] = ((lp2->x * lp3->y) - (lp2->y * lp3->x)) * invdet; invmx[5] = ((lp1->x * lp3->y) - (lp1->y * lp3->x)) * -invdet; invmx[8] = ((lp1->x * lp2->y) - (lp1->y * lp2->x)) * invdet; any_ls_inside = 0; for(i = 0; i< ls_amount; i++) { if(i != a && i != b && i != c) { this_inside = 1; for(j = 0; j< 3; j++) { tmp = lss[i].x * invmx[0 + j*3]; tmp += lss[i].y * invmx[1 + j*3]; tmp += lss[i].z * invmx[2 + j*3]; if(tmp < -0.001) this_inside = 0; } if(this_inside == 1) any_ls_inside = 1; } } return any_ls_inside; } static void add_ldsp_triplet(int i, int j, int k, t_def_ls *x) /* adds i, j, k triplet to structure*/ { struct t_ls_set *trip_ptr, *prev; trip_ptr = x->x_ls_set; prev = NULL; while(trip_ptr != NULL) { prev = trip_ptr; trip_ptr = trip_ptr->next; } trip_ptr = (struct t_ls_set*)getbytes(sizeof(struct t_ls_set)); if(prev == NULL) x->x_ls_set = trip_ptr; else prev->next = trip_ptr; trip_ptr->next = NULL; trip_ptr->ls_nos[0] = i; trip_ptr->ls_nos[1] = j; trip_ptr->ls_nos[2] = k; } t_float vec_angle(t_ls v1, t_ls v2) // angle between two loudspeakers { t_float inner= ((v1.x*v2.x + v1.y*v2.y + v1.z*v2.z) / (vec_length(v1) * vec_length(v2))); if(inner > 1.0) inner = 1.0; if(inner < -1.0) inner = -1.0; return fabs(acos(inner)); } t_float vec_length(t_ls v1) // length of a vector { return (sqrt(v1.x*v1.x + v1.y*v1.y + v1.z*v1.z)); } t_float vec_prod(t_ls v1, t_ls v2) // vector dot product { return (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); } t_float vol_p_side_lgth(int i, int j, int k, t_ls lss[MAX_LS_AMOUNT]) { /* calculate volume of the parallelepiped defined by the loudspeaker direction vectors and divide it with total length of the triangle sides. This is used when removing too narrow triangles. */ t_float volper, lgth; t_ls xprod; ls_cross_prod(lss[i], lss[j], &xprod); volper = fabs(vec_prod(xprod, lss[k])); lgth = (fabs(vec_angle(lss[i], lss[j])) + fabs(vec_angle(lss[i], lss[k])) + fabs(vec_angle(lss[j], lss[k]))); if(lgth > 0.00001) return volper / lgth; else return 0.0; } static void ls_cross_prod(t_ls v1, t_ls v2, t_ls *res) // vector cross product { t_float length; res->x = (v1.y * v2.z ) - (v1.z * v2.y); res->y = (v1.z * v2.x ) - (v1.x * v2.z); res->z = (v1.x * v2.y ) - (v1.y * v2.x); length = vec_length(*res); res->x /= length; res->y /= length; res->z /= length; } static int lines_intersect(int i, int j, int k, int l, t_ls lss[MAX_LS_AMOUNT]) /* checks if two lines intersect on 3D sphere */ { t_ls v1; t_ls v2; t_ls v3, neg_v3; //t_float angle; t_float dist_ij, dist_kl, dist_iv3, dist_jv3, dist_inv3, dist_jnv3; t_float dist_kv3, dist_lv3, dist_knv3, dist_lnv3; ls_cross_prod(lss[i], lss[j], &v1); ls_cross_prod(lss[k], lss[l], &v2); ls_cross_prod(v1, v2, &v3); neg_v3.x = 0.0 - v3.x; neg_v3.y = 0.0 - v3.y; neg_v3.z = 0.0 - v3.z; dist_ij = (vec_angle(lss[i], lss[j])); dist_kl = (vec_angle(lss[k], lss[l])); dist_iv3 = (vec_angle(lss[i], v3)); dist_jv3 = (vec_angle(v3, lss[j])); dist_inv3 = (vec_angle(lss[i], neg_v3)); dist_jnv3 = (vec_angle(neg_v3, lss[j])); dist_kv3 = (vec_angle(lss[k], v3)); dist_lv3 = (vec_angle(v3, lss[l])); dist_knv3 = (vec_angle(lss[k], neg_v3)); dist_lnv3 = (vec_angle(neg_v3, lss[l])); /* if one of loudspeakers is close to crossing point, don't do anything*/ if(fabs(dist_iv3) <= TFLT_EPSILON || fabs(dist_jv3) <= TFLT_EPSILON || fabs(dist_kv3) <= TFLT_EPSILON || fabs(dist_lv3) <= TFLT_EPSILON || fabs(dist_inv3) <= TFLT_EPSILON || fabs(dist_jnv3) <= TFLT_EPSILON || fabs(dist_knv3) <= TFLT_EPSILON || fabs(dist_lnv3) <= TFLT_EPSILON ) return 0; // if crossing point is on line between both loudspeakers return 1 if(((fabs(dist_ij - (dist_iv3 + dist_jv3)) <= TFLT_EPSILON) && (fabs(dist_kl - (dist_kv3 + dist_lv3)) <= TFLT_EPSILON)) || ((fabs(dist_ij - (dist_inv3 + dist_jnv3)) <= TFLT_EPSILON) && (fabs(dist_kl - (dist_knv3 + dist_lnv3)) <= TFLT_EPSILON))) { return 1; } else { return 0; } } static void calculate_3x3_matrixes(t_def_ls *x) /* Calculates the inverse matrices for 3D */ { t_float invdet; t_ls *lp1, *lp2, *lp3; t_float *invmx; //t_float *ptr; struct t_ls_set *tr_ptr = x->x_ls_set; unsigned long triplet_amount = 0, /*ftable_size, */i, pointer, list_length = 0; Atom *at; t_ls *lss = x->x_ls; if(tr_ptr == NULL) { error("define-loudspeakers: Not valid 3-D configuration\n"); return; } /* counting triplet amount */ while(tr_ptr != NULL) { triplet_amount++; tr_ptr = tr_ptr->next; } tr_ptr = x->x_ls_set; list_length = triplet_amount * 21 + 3; at = (Atom *) getbytes(list_length * sizeof(Atom)); SETLONG(&at[0], x->x_def_ls_dimension); SETLONG(&at[1], x->x_def_ls_amount); pointer = 2; while(tr_ptr != NULL) { lp1 = &(lss[tr_ptr->ls_nos[0]]); lp2 = &(lss[tr_ptr->ls_nos[1]]); lp3 = &(lss[tr_ptr->ls_nos[2]]); /* matrix inversion */ invmx = tr_ptr->inv_mx; invdet = 1.0 / ( lp1->x * ((lp2->y * lp3->z) - (lp2->z * lp3->y)) - lp1->y * ((lp2->x * lp3->z) - (lp2->z * lp3->x)) + lp1->z * ((lp2->x * lp3->y) - (lp2->y * lp3->x))); invmx[0] = ((lp2->y * lp3->z) - (lp2->z * lp3->y)) * invdet; invmx[3] = ((lp1->y * lp3->z) - (lp1->z * lp3->y)) * -invdet; invmx[6] = ((lp1->y * lp2->z) - (lp1->z * lp2->y)) * invdet; invmx[1] = ((lp2->x * lp3->z) - (lp2->z * lp3->x)) * -invdet; invmx[4] = ((lp1->x * lp3->z) - (lp1->z * lp3->x)) * invdet; invmx[7] = ((lp1->x * lp2->z) - (lp1->z * lp2->x)) * -invdet; invmx[2] = ((lp2->x * lp3->y) - (lp2->y * lp3->x)) * invdet; invmx[5] = ((lp1->x * lp3->y) - (lp1->y * lp3->x)) * -invdet; invmx[8] = ((lp1->x * lp2->y) - (lp1->y * lp2->x)) * invdet; for(i = 0; i < 3; i++) { SETLONG(&at[pointer], tr_ptr->ls_nos[i]+1); pointer++; } for(i = 0; i < 9; i++) { SETFLOAT(&at[pointer], invmx[i]); pointer++; } SETFLOAT(&at[pointer], lp1->x); pointer++; SETFLOAT(&at[pointer], lp2->x); pointer++; SETFLOAT(&at[pointer], lp3->x); pointer++; SETFLOAT(&at[pointer], lp1->y); pointer++; SETFLOAT(&at[pointer], lp2->y); pointer++; SETFLOAT(&at[pointer], lp3->y); pointer++; SETFLOAT(&at[pointer], lp1->z); pointer++; SETFLOAT(&at[pointer], lp2->z); pointer++; SETFLOAT(&at[pointer], lp3->z); pointer++; tr_ptr = tr_ptr->next; } sendLoudspeakerMatrices(x, list_length, at); freebytes(at, list_length * sizeof(Atom)); } static void choose_ls_tuplets(t_def_ls *x) /* selects the loudspeaker pairs, calculates the inversion matrices and stores the data to a global array*/ { //t_float atorad = (2 * M_PI / 360) ; int i, j; //t_float w1, w2; //t_float p1, p2; int sorted_lss[MAX_LS_AMOUNT]; int exist[MAX_LS_AMOUNT]; int amount = 0; t_float inv_mat[MAX_LS_AMOUNT][4]; // In 2-D ls amount == max amount of LS pairs t_float mat[MAX_LS_AMOUNT][4]; //t_float *ptr; //t_float *ls_table; t_ls *lss = x->x_ls; long ls_amount=x->x_def_ls_amount; long list_length; Atom *at; long pointer; for(i = 0; i < MAX_LS_AMOUNT; i++){ exist[i] = 0; } /* sort loudspeakers according their aximuth angle */ sort_2D_lss(x->x_ls, sorted_lss, ls_amount); /* adjacent loudspeakers are the loudspeaker pairs to be used.*/ for(i = 0; i < (ls_amount-1); i++){ if((lss[sorted_lss[i+1]].azi - lss[sorted_lss[i]].azi) <= (180 - 10)){ if (calc_2D_inv_tmatrix( lss[sorted_lss[i]].azi, lss[sorted_lss[i+1]].azi, inv_mat[i], mat[i]) != 0){ exist[i] = 1; amount++; } } } if(((360 - lss[sorted_lss[ls_amount-1]].azi) +lss[sorted_lss[0]].azi) <= (180 - 10)) { if(calc_2D_inv_tmatrix(lss[sorted_lss[ls_amount-1]].azi, lss[sorted_lss[0]].azi, inv_mat[ls_amount-1], mat[ls_amount-1]) != 0) { exist[ls_amount-1] = 1; amount++; } } // Output list_length = amount * 10 + 2; at = (Atom *) getbytes(list_length * sizeof(Atom)); SETLONG(&at[0], x->x_def_ls_dimension); SETLONG(&at[1], x->x_def_ls_amount); pointer = 2; for(i = 0; i < ls_amount - 1; i++){ if(exist[i] == 1) { SETLONG(&at[pointer], sorted_lss[i]+1); pointer++; SETLONG(&at[pointer], sorted_lss[i+1]+1); pointer++; for(j = 0; j < 4; j++) { SETFLOAT(&at[pointer], inv_mat[i][j]); pointer++; } for(j = 0; j < 4; j++) { SETFLOAT(&at[pointer], mat[i][j]); pointer++; } } } if(exist[ls_amount-1] == 1) { SETLONG(&at[pointer], sorted_lss[ls_amount-1]+1); pointer++; SETLONG(&at[pointer], sorted_lss[0]+1); pointer++; for(j = 0; j < 4; j++) { SETFLOAT(&at[pointer], inv_mat[ls_amount-1][j]); pointer++; } for(j = 0; j < 4; j++) { SETFLOAT(&at[pointer], mat[ls_amount-1][j]); pointer++; } } sendLoudspeakerMatrices(x, list_length, at); freebytes(at, list_length * sizeof(Atom)); } void sort_2D_lss(t_ls lss[MAX_LS_AMOUNT], int sorted_lss[MAX_LS_AMOUNT], int ls_amount) // sort loudspeakers according to azimuth angle { t_float tmp, tmp_azi; //t_float rad2ang = 360.0f / ( 2.0f * M_PI ); //t_float x, y; /* Transforming angles between -180 and 180 */ int i; for(i = 0; i < ls_amount; i++) { ls_angles_to_cart(&lss[i]); lss[i].azi = acos( lss[i].x) * rad2ang; if(fabs(lss[i].y) <= 0.001) tmp = 1.0; else tmp = lss[i].y / fabs(lss[i].y); lss[i].azi *= tmp; } for(i = 0; i < ls_amount; i++) { tmp = 2000; int index = 0; int j; for(j = 0 ; j < ls_amount; j++) { if(lss[j].azi <= tmp) { tmp = lss[j].azi; index = j; } } sorted_lss[i] = index; tmp_azi = (lss[index].azi); lss[index].azi = (tmp_azi + (t_float)4000.0); } for(i = 0; i < ls_amount; i++) { tmp_azi = (lss[i].azi); lss[i].azi = (tmp_azi - (t_float)4000.0); } } static int calc_2D_inv_tmatrix(t_float azi1, t_float azi2, t_float inv_mat[4], t_float mat[4]) // calculate inverse 2x2 matrix { t_float x1, x2, x3, x4; /* x1 x3 */ //t_float y1, y2, y3, y4; /* x2 x4 */ t_float det; mat[0] = x1 = cos(azi1 / rad2ang); mat[1] = x2 = sin(azi1 / rad2ang); mat[2] = x3 = cos(azi2 / rad2ang); mat[3] = x4 = sin(azi2 / rad2ang); det = (x1 * x4) - ( x3 * x2 ); if(fabs(det) <= 0.001) { inv_mat[0] = 0.0; inv_mat[1] = 0.0; inv_mat[2] = 0.0; inv_mat[3] = 0.0; return 0; } else { inv_mat[0] = (x4 / det); inv_mat[1] = (-x3 / det); inv_mat[2] = (-x2 / det); inv_mat[3] = (x1 / det); return 1; } } vbap-1.2.0/examples/000077500000000000000000000000001417256401100142615ustar00rootroot00000000000000vbap-1.2.0/examples/graph-to-aziele.pd000066400000000000000000000022551417256401100176020ustar00rootroot00000000000000#N canvas 549 17 647 652 12; #X obj 42 505 expr acos($f1/$f3) * 180 / 3.1415 * $f2; #X obj 323 366 expr sqrt($f1); #X obj 323 338 + 0; #X obj 78 313 * 0; #X obj 343 276 * 0; #X msg 460 505 0; #X msg 233 265 -1; #X msg 268 265 1; #X obj 42 249 expr ($f1-0.5)/0.5; #X obj 233 145 expr ($f1-0.5)/0.5; #X obj 42 16 inlet; #X obj 42 570 outlet; #X obj 488 569 outlet; #X obj 407 431 expr 90 - ($f1*90.0); #X obj 460 479 moses 0; #X obj 233 238 moses 0; #X obj 42 279 t f f f; #X obj 233 179 t f f f; #X obj 42 99 unpack f f; #X msg 42 67 \$2 \$1; #X text 101 66 reverse order; #X text 101 16 float x \, float y; #X obj 323 393 t f f; #X connect 0 0 11 0; #X connect 1 0 22 0; #X connect 2 0 1 0; #X connect 3 0 2 0; #X connect 4 0 2 1; #X connect 5 0 12 0; #X connect 6 0 0 1; #X connect 7 0 0 1; #X connect 8 0 16 0; #X connect 9 0 17 0; #X connect 10 0 19 0; #X connect 13 0 14 0; #X connect 14 0 5 0; #X connect 14 1 12 0; #X connect 15 0 6 0; #X connect 15 1 7 0; #X connect 16 0 0 0; #X connect 16 1 3 0; #X connect 16 2 3 1; #X connect 17 0 15 0; #X connect 17 1 4 0; #X connect 17 2 4 1; #X connect 18 0 8 0; #X connect 18 1 9 0; #X connect 19 0 18 0; #X connect 22 0 0 2; #X connect 22 1 13 0; vbap-1.2.0/examples/high.pd000066400000000000000000000011121417256401100155200ustar00rootroot00000000000000#N canvas 0 0 576 425 10; #X obj 102 43 inlet; #X obj 124 188 f 0; #X obj 102 239 >; #X obj 102 281 sel 1; #X obj 201 158 f; #X obj 203 354 outlet; #X obj 203 267 f; #X obj 292 42 inlet; #X text 339 42 reset to float; #X text 148 44 test against high; #X obj 102 91 t b f b f; #X obj 292 89 t b f; #X connect 0 0 10 0; #X connect 1 0 2 1; #X connect 1 0 6 1; #X connect 2 0 3 0; #X connect 3 0 4 0; #X connect 4 0 1 0; #X connect 6 0 5 0; #X connect 7 0 11 0; #X connect 10 0 6 0; #X connect 10 1 2 0; #X connect 10 2 1 0; #X connect 10 3 4 1; #X connect 11 0 6 0; #X connect 11 1 1 0; vbap-1.2.0/examples/playsample~.pd000066400000000000000000000024441417256401100171570ustar00rootroot00000000000000#N canvas 27 300 849 403 10; #X obj 38 354 table \$0music; #X obj 148 160 soundfiler; #X floatatom 148 183 5 0 0; #X obj 243 207 phasor~ 1; #X floatatom 266 181 8 0 0; #X obj 243 237 *~ 0; #X msg 148 137 read -resize \$1 \$2; #X obj 199 83 symbol \$0music; #X obj 148 110 pack s s; #X obj 199 48 loadbang; #X obj 12 13 inlet; #X text 59 12 .aiff file name; #X obj 311 15 inlet; #X text 356 15 phasor speed; #X obj 243 334 outlet~; #X obj 243 267 tabread4~ \$0music; #X obj 302 83 samplerate~; #X floatatom 310 107 5 0 0; #X obj 460 14 inlet; #X text 505 14 gain; #X obj 243 306 *~ 1; #X obj 148 210 t b f; #X obj 148 232 f; #X obj 148 254 /; #X obj 419 334 outlet~; #X text 303 333 sample signal; #X text 483 334 phasor signal; #X msg 557 41 0; #X obj 557 16 inlet; #X text 601 16 restart; #X connect 1 0 2 0; #X connect 2 0 5 1; #X connect 2 0 21 0; #X connect 3 0 5 0; #X connect 3 0 24 0; #X connect 4 0 3 0; #X connect 5 0 15 0; #X connect 6 0 1 0; #X connect 7 0 8 1; #X connect 8 0 6 0; #X connect 9 0 7 0; #X connect 9 0 16 0; #X connect 10 0 8 0; #X connect 12 0 3 0; #X connect 15 0 20 0; #X connect 16 0 17 0; #X connect 16 0 22 1; #X connect 18 0 20 1; #X connect 20 0 14 0; #X connect 21 0 22 0; #X connect 21 1 23 1; #X connect 22 0 23 0; #X connect 23 0 4 0; #X connect 27 0 3 1; #X connect 28 0 27 0; vbap-1.2.0/examples/recent.pd000066400000000000000000000005061417256401100160670ustar00rootroot00000000000000#N canvas 399 148 452 302 12; #X obj 56 55 inlet; #X msg 91 123 clear; #X obj 59 252 outlet; #X obj 166 54 inlet; #X text 8 5 output only most "recent" messages; #X obj 56 89 t a b; #X obj 59 202 pipe \$1; #X connect 0 0 5 0; #X connect 1 0 6 0; #X connect 3 0 6 1; #X connect 5 0 6 0; #X connect 5 1 1 0; #X connect 6 0 2 0; vbap-1.2.0/examples/rvbap-demo.pd000066400000000000000000000401741417256401100166500ustar00rootroot00000000000000#N canvas 243 71 893 632 10; #X obj 345 85 define_loudspeakers 3 -45 0 45 0 0 45 180 45; #X msg 34 63 bang; #X obj 34 83 define_loudspeakers 2 -45 45 135 -135; #X msg 345 60 bang; #X floatatom 196 497 5 0 100 1 dB_after_1sec - -; #X obj 34 591 dac~ 1 2 3 4; #N canvas 0 0 699 527 sig 0; #X obj 58 72 line~; #X msg 58 49 0 \, 10000 5; #X obj 58 118 cos~; #X msg 146 70 1; #X obj 146 47 loadbang; #X obj 58 95 clip~ 0 0.25; #X obj 251 134 line~; #X obj 251 157 cos~; #X msg 324 54 -0.25 \, 0.25 100; #X obj 251 8 loadbang; #X msg 251 31 -0.25; #X obj 251 203 *~; #X obj 58 140 hip~ 5; #X msg 324 77 -0.25 \, 0.25 400; #X floatatom 151 126 0 0 0 0 freq - -; #X obj 324 191 osc~ 440; #X obj 324 168 mtof; #X msg 324 31 -0.25 \, 0.25 20; #X obj 251 180 *~ 0.1; #X msg 324 100 -0.25 \, 0.25 1000; #X msg 324 122 -0.25 \, 0.25 2000; #X obj 324 226 *~; #X obj 342 252 *~; #X msg 324 8 0; #X obj 308 257 *~; #X obj 58 26 metro 2000; #X floatatom 58 4 0 0 0 0 - - -; #X text 1 51 impulse; #X text 362 7 tone; #X obj 59 184 outlet~; #X obj 170 6 inlet; #X obj 442 18 metro 500; #X obj 91 8 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1 ; #X connect 0 0 5 0; #X connect 1 0 0 0; #X connect 2 0 12 0; #X connect 3 0 0 0; #X connect 4 0 3 0; #X connect 5 0 2 0; #X connect 6 0 7 0; #X connect 7 0 18 0; #X connect 8 0 6 0; #X connect 9 0 10 0; #X connect 10 0 6 0; #X connect 11 0 12 0; #X connect 12 0 29 0; #X connect 13 0 6 0; #X connect 14 0 16 0; #X connect 15 0 11 1; #X connect 15 0 21 0; #X connect 15 0 21 1; #X connect 15 0 22 0; #X connect 16 0 15 0; #X connect 17 0 6 0; #X connect 18 0 11 0; #X connect 19 0 6 0; #X connect 20 0 6 0; #X connect 21 0 22 1; #X connect 21 0 11 1; #X connect 21 0 24 0; #X connect 21 0 24 1; #X connect 22 0 11 1; #X connect 23 0 6 0; #X connect 24 0 11 1; #X connect 25 0 1 0; #X connect 26 0 25 0; #X connect 30 0 31 0; #X connect 31 0 8 0; #X connect 32 0 25 0; #X coords 0 -1 1 1 85 60 1 100 100; #X restore 63 415 pd sig; #X obj 63 387 tgl 24 0 empty empty test-sigs 26 7 1 10 -262144 -1 -1 0 1; #X msg 216 522 clear; #N canvas 0 0 766 594 four-reverbs 0; #X obj 67 179 rev1~; #X obj 114 179 rev1~; #X obj 160 179 rev1~; #X obj 210 179 rev1~; #X obj 282 55 inlet; #X obj 67 54 inlet~; #X obj 113 54 inlet~; #X obj 160 53 inlet~; #X obj 211 53 inlet~; #X obj 67 286 outlet~; #X obj 114 268 outlet~; #X obj 159 246 outlet~; #X obj 209 226 outlet~; #X obj 339 55 inlet; #X obj 339 76 b; #X connect 0 0 9 0; #X connect 1 0 10 0; #X connect 2 0 11 0; #X connect 3 0 12 0; #X connect 4 0 3 1; #X connect 4 0 2 1; #X connect 4 0 1 1; #X connect 4 0 0 1; #X connect 5 0 0 0; #X connect 6 0 1 0; #X connect 7 0 2 0; #X connect 8 0 3 0; #X connect 13 0 14 0; #X connect 14 0 3 2; #X connect 14 0 2 2; #X connect 14 0 1 2; #X connect 14 0 0 2; #X restore 116 544 pd four-reverbs; #N canvas 0 0 699 527 sig 0; #X obj 58 72 line~; #X msg 58 49 0 \, 10000 5; #X obj 58 118 cos~; #X msg 146 70 1; #X obj 146 47 loadbang; #X obj 58 95 clip~ 0 0.25; #X obj 251 134 line~; #X obj 251 157 cos~; #X msg 324 54 -0.25 \, 0.25 100; #X obj 251 8 loadbang; #X msg 251 31 -0.25; #X obj 251 203 *~; #X obj 58 140 hip~ 5; #X msg 324 77 -0.25 \, 0.25 400; #X floatatom 134 122 0 0 0 0 freq - -; #X obj 324 191 osc~ 440; #X obj 324 168 mtof; #X msg 324 31 -0.25 \, 0.25 20; #X obj 251 180 *~ 0.1; #X msg 324 100 -0.25 \, 0.25 1000; #X msg 324 122 -0.25 \, 0.25 2000; #X obj 324 226 *~; #X obj 342 252 *~; #X msg 324 8 0; #X obj 308 257 *~; #X obj 58 26 metro 2000; #X floatatom 58 4 0 0 0 0 - - -; #X text 1 51 impulse; #X text 362 7 tone; #X obj 59 184 outlet~; #X obj 170 6 inlet; #X obj 442 18 metro 500; #X obj 91 8 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1 ; #X connect 0 0 5 0; #X connect 1 0 0 0; #X connect 2 0 12 0; #X connect 3 0 0 0; #X connect 4 0 3 0; #X connect 5 0 2 0; #X connect 6 0 7 0; #X connect 7 0 18 0; #X connect 8 0 6 0; #X connect 9 0 10 0; #X connect 10 0 6 0; #X connect 11 0 12 0; #X connect 12 0 29 0; #X connect 13 0 6 0; #X connect 14 0 16 0; #X connect 15 0 11 1; #X connect 15 0 21 0; #X connect 15 0 21 1; #X connect 15 0 22 0; #X connect 16 0 15 0; #X connect 17 0 6 0; #X connect 18 0 11 0; #X connect 19 0 6 0; #X connect 20 0 6 0; #X connect 21 0 22 1; #X connect 21 0 11 1; #X connect 21 0 24 0; #X connect 21 0 24 1; #X connect 22 0 11 1; #X connect 23 0 6 0; #X connect 24 0 11 1; #X connect 25 0 1 0; #X connect 26 0 25 0; #X connect 30 0 31 0; #X connect 31 0 8 0; #X connect 32 0 25 0; #X coords 0 -1 1 1 85 60 1 100 100; #X restore 156 414 pd sig; #X obj 156 386 tgl 24 0 empty empty test-sigs 26 7 1 10 -262144 -1 -1 0 1; #X obj 34 364 mtx 8 3; #X obj 35 498 mtx_*~ 8 3 20; #N canvas 0 0 699 527 sig 0; #X obj 58 72 line~; #X msg 58 49 0 \, 10000 5; #X obj 58 118 cos~; #X msg 146 70 1; #X obj 146 47 loadbang; #X obj 58 95 clip~ 0 0.25; #X obj 251 134 line~; #X obj 251 157 cos~; #X msg 324 54 -0.25 \, 0.25 100; #X obj 251 8 loadbang; #X msg 251 31 -0.25; #X obj 251 203 *~; #X obj 58 140 hip~ 5; #X msg 324 77 -0.25 \, 0.25 400; #X floatatom 134 122 0 0 0 0 freq - -; #X obj 324 191 osc~ 440; #X obj 324 168 mtof; #X msg 324 31 -0.25 \, 0.25 20; #X obj 251 180 *~ 0.1; #X msg 324 100 -0.25 \, 0.25 1000; #X msg 324 122 -0.25 \, 0.25 2000; #X obj 324 226 *~; #X obj 342 252 *~; #X msg 324 8 0; #X obj 308 257 *~; #X obj 58 26 metro 2000; #X floatatom 58 4 0 0 0 0 - - -; #X text 1 51 impulse; #X text 362 7 tone; #X obj 59 184 outlet~; #X obj 170 6 inlet; #X obj 442 18 metro 500; #X obj 91 8 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1 ; #X connect 0 0 5 0; #X connect 1 0 0 0; #X connect 2 0 12 0; #X connect 3 0 0 0; #X connect 4 0 3 0; #X connect 5 0 2 0; #X connect 6 0 7 0; #X connect 7 0 18 0; #X connect 8 0 6 0; #X connect 9 0 10 0; #X connect 10 0 6 0; #X connect 11 0 12 0; #X connect 12 0 29 0; #X connect 13 0 6 0; #X connect 14 0 16 0; #X connect 15 0 11 1; #X connect 15 0 21 0; #X connect 15 0 21 1; #X connect 15 0 22 0; #X connect 16 0 15 0; #X connect 17 0 6 0; #X connect 18 0 11 0; #X connect 19 0 6 0; #X connect 20 0 6 0; #X connect 21 0 22 1; #X connect 21 0 11 1; #X connect 21 0 24 0; #X connect 21 0 24 1; #X connect 22 0 11 1; #X connect 23 0 6 0; #X connect 24 0 11 1; #X connect 25 0 1 0; #X connect 26 0 25 0; #X connect 30 0 31 0; #X connect 31 0 8 0; #X connect 32 0 25 0; #X coords 0 -1 1 1 85 60 1 100 100; #X restore 250 416 pd sig; #X obj 250 388 tgl 24 0 empty empty test-sigs 26 7 1 10 -262144 -1 -1 0 1; #X obj 196 353 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #N canvas 0 0 818 424 rvbap-module0 0; #X floatatom 104 128 5 0 0 2 azi - -; #X floatatom 148 128 5 0 0 2 ele - -; #X floatatom 192 128 5 0 100 2 spread - -; #X text 199 262 actual location; #X obj 83 202 rvbap 0 0; #X floatatom 245 129 5 1 20 2 dist - -; #X floatatom 127 233 5 0 0 3 azi - -; #X floatatom 171 233 5 0 0 3 ele - -; #X floatatom 215 233 5 0 0 3 spread - -; #X floatatom 268 234 5 0 0 3 dist - -; #N canvas 352 196 547 360 set-element 0; #X obj 70 81 unpack 0 0; #X obj 70 104 + 1; #X obj 70 137 pack 0 1 0; #X obj 70 179 list trim; #X obj 70 158 list prepend element; #X obj 70 59 inlet; #X obj 70 261 outlet; #X obj 70 221 t b a; #X text 48 33 transforms rvbap-output to be used with [mtx] from iemmatrix ; #X obj 168 80 inlet; #X connect 0 0 1 0; #X connect 0 1 2 2; #X connect 1 0 2 0; #X connect 2 0 4 0; #X connect 3 0 7 0; #X connect 4 0 3 0; #X connect 5 0 0 0; #X connect 7 0 6 0; #X connect 7 1 6 0; #X connect 9 0 2 1; #X restore 83 316 pd set-element; #N canvas 203 642 802 273 peek 0; #X floatatom 55 198 10 0 0 0 - - -; #X floatatom 134 198 10 0 0 0 - - -; #X floatatom 213 198 10 0 0 0 - - -; #X floatatom 292 198 10 0 0 0 - - -; #X obj 55 74 route 0 1 2 3 4 5 6 7; #X floatatom 366 197 10 0 0 0 - - -; #X floatatom 445 197 10 0 0 0 - - -; #X floatatom 524 197 10 0 0 0 - - -; #X floatatom 603 197 10 0 0 0 - - -; #X obj 55 49 inlet; #X connect 4 0 0 0; #X connect 4 1 1 0; #X connect 4 2 2 0; #X connect 4 3 3 0; #X connect 4 4 5 0; #X connect 4 5 6 0; #X connect 4 6 7 0; #X connect 4 7 8 0; #X connect 9 0 4 0; #X restore 97 290 pd peek; #N canvas 0 0 450 300 tba 0; #X obj 143 51 inlet; #X obj 96 49 inlet; #X obj 191 51 inlet; #X obj 238 51 inlet; #X obj 173 180 outlet; #X obj 61 178 outlet; #X obj 221 180 outlet; #X obj 268 180 outlet; #X obj 126 180 outlet; #X obj 96 73 t b a; #X obj 143 72 t b a; #X obj 191 72 t b a; #X obj 238 72 t b a; #X connect 0 0 10 0; #X connect 1 0 9 0; #X connect 2 0 11 0; #X connect 3 0 12 0; #X connect 9 0 5 0; #X connect 9 1 8 0; #X connect 10 0 5 0; #X connect 10 1 4 0; #X connect 11 0 5 0; #X connect 11 1 6 0; #X connect 12 0 5 0; #X connect 12 1 7 0; #X restore 104 168 pd tba; #X obj 281 114 hsl 64 15 1 20 0 0 empty empty empty -2 -8 0 10 -262144 -1 -1 0 1; #X obj 386 102 loadbang; #X obj 83 345 outlet; #X obj 83 39 inlet; #X obj 304 140 hradio 15 1 0 3 empty empty id: -20 8 1 10 -262144 -1 -1 0; #X obj 386 123 0; #X obj 539 151 f \$1; #X text 378 152 in an abstraction \, use; #X text 575 154 topass id as argument.; #X obj 338 272 + 1; #X connect 0 0 12 0; #X connect 1 0 12 1; #X connect 2 0 12 2; #X connect 4 0 10 0; #X connect 4 0 11 0; #X connect 4 1 6 0; #X connect 4 2 7 0; #X connect 4 3 8 0; #X connect 4 4 9 0; #X connect 5 0 12 3; #X connect 10 0 15 0; #X connect 12 0 4 0; #X connect 12 1 4 1; #X connect 12 2 4 2; #X connect 12 3 4 3; #X connect 12 4 4 4; #X connect 13 0 5 0; #X connect 14 0 18 0; #X connect 16 0 4 0; #X connect 17 0 22 0; #X connect 18 0 17 0; #X connect 22 0 10 1; #X coords 0 -1 1 1 260 60 1 100 100; #X restore 34 229 pd rvbap-module0; #N canvas 0 0 858 488 rvbap-module1 0; #X floatatom 104 128 5 0 0 2 azi - -; #X floatatom 148 128 5 0 0 2 ele - -; #X floatatom 192 128 5 0 100 2 spread - -; #X text 199 262 actual location; #X obj 83 202 rvbap 0 0; #X floatatom 245 129 5 1 20 2 dist - -; #X floatatom 127 233 5 0 0 3 azi - -; #X floatatom 171 233 5 0 0 3 ele - -; #X floatatom 215 233 5 0 0 3 spread - -; #X floatatom 268 234 5 0 0 3 dist - -; #N canvas 352 196 547 360 set-element 0; #X obj 70 81 unpack 0 0; #X obj 70 104 + 1; #X obj 70 137 pack 0 1 0; #X obj 70 179 list trim; #X obj 70 158 list prepend element; #X obj 70 59 inlet; #X obj 70 261 outlet; #X obj 70 221 t b a; #X text 48 33 transforms rvbap-output to be used with [mtx] from iemmatrix ; #X obj 168 80 inlet; #X connect 0 0 1 0; #X connect 0 1 2 2; #X connect 1 0 2 0; #X connect 2 0 4 0; #X connect 3 0 7 0; #X connect 4 0 3 0; #X connect 5 0 0 0; #X connect 7 0 6 0; #X connect 7 1 6 0; #X connect 9 0 2 1; #X restore 83 316 pd set-element; #N canvas 203 642 802 273 peek 0; #X floatatom 55 198 10 0 0 0 - - -; #X floatatom 134 198 10 0 0 0 - - -; #X floatatom 213 198 10 0 0 0 - - -; #X floatatom 292 198 10 0 0 0 - - -; #X obj 55 74 route 0 1 2 3 4 5 6 7; #X floatatom 366 197 10 0 0 0 - - -; #X floatatom 445 197 10 0 0 0 - - -; #X floatatom 524 197 10 0 0 0 - - -; #X floatatom 603 197 10 0 0 0 - - -; #X obj 55 49 inlet; #X connect 4 0 0 0; #X connect 4 1 1 0; #X connect 4 2 2 0; #X connect 4 3 3 0; #X connect 4 4 5 0; #X connect 4 5 6 0; #X connect 4 6 7 0; #X connect 4 7 8 0; #X connect 9 0 4 0; #X restore 97 290 pd peek; #N canvas 0 0 450 300 tba 0; #X obj 143 51 inlet; #X obj 96 49 inlet; #X obj 191 51 inlet; #X obj 238 51 inlet; #X obj 173 180 outlet; #X obj 61 178 outlet; #X obj 221 180 outlet; #X obj 268 180 outlet; #X obj 126 180 outlet; #X obj 96 73 t b a; #X obj 143 72 t b a; #X obj 191 72 t b a; #X obj 238 72 t b a; #X connect 0 0 10 0; #X connect 1 0 9 0; #X connect 2 0 11 0; #X connect 3 0 12 0; #X connect 9 0 5 0; #X connect 9 1 8 0; #X connect 10 0 5 0; #X connect 10 1 4 0; #X connect 11 0 5 0; #X connect 11 1 6 0; #X connect 12 0 5 0; #X connect 12 1 7 0; #X restore 104 168 pd tba; #X obj 281 114 hsl 64 15 1 20 0 0 empty empty empty -2 -8 0 10 -262144 -1 -1 0 1; #X obj 386 102 loadbang; #X obj 83 345 outlet; #X obj 83 39 inlet; #X obj 304 140 hradio 15 1 0 3 empty empty id: -20 8 1 10 -262144 -1 -1 1; #X obj 386 123 1; #X obj 544 152 f \$1; #X text 383 153 in an abstraction \, use; #X text 580 155 topass id as argument.; #X obj 338 272 + 1; #X connect 0 0 12 0; #X connect 1 0 12 1; #X connect 2 0 12 2; #X connect 4 0 10 0; #X connect 4 0 11 0; #X connect 4 1 6 0; #X connect 4 2 7 0; #X connect 4 3 8 0; #X connect 4 4 9 0; #X connect 5 0 12 3; #X connect 10 0 15 0; #X connect 12 0 4 0; #X connect 12 1 4 1; #X connect 12 2 4 2; #X connect 12 3 4 3; #X connect 12 4 4 4; #X connect 13 0 5 0; #X connect 14 0 18 0; #X connect 16 0 4 0; #X connect 17 0 22 0; #X connect 18 0 17 0; #X connect 22 0 10 1; #X coords 0 -1 1 1 260 60 1 100 100; #X restore 303 229 pd rvbap-module1; #N canvas 0 0 798 493 rvbap-module3 0; #X floatatom 104 128 5 0 0 2 azi - -; #X floatatom 148 128 5 0 0 2 ele - -; #X floatatom 192 128 5 0 100 2 spread - -; #X text 199 262 actual location; #X obj 83 202 rvbap 0 0; #X floatatom 245 129 5 1 20 2 dist - -; #X floatatom 127 233 5 0 0 3 azi - -; #X floatatom 171 233 5 0 0 3 ele - -; #X floatatom 215 233 5 0 0 3 spread - -; #X floatatom 268 234 5 0 0 3 dist - -; #N canvas 352 196 547 360 set-element 0; #X obj 70 81 unpack 0 0; #X obj 70 104 + 1; #X obj 70 137 pack 0 1 0; #X obj 70 179 list trim; #X obj 70 158 list prepend element; #X obj 70 59 inlet; #X obj 70 261 outlet; #X obj 70 221 t b a; #X text 48 33 transforms rvbap-output to be used with [mtx] from iemmatrix ; #X obj 168 80 inlet; #X connect 0 0 1 0; #X connect 0 1 2 2; #X connect 1 0 2 0; #X connect 2 0 4 0; #X connect 3 0 7 0; #X connect 4 0 3 0; #X connect 5 0 0 0; #X connect 7 0 6 0; #X connect 7 1 6 0; #X connect 9 0 2 1; #X restore 83 316 pd set-element; #N canvas 203 642 802 273 peek 0; #X floatatom 55 198 10 0 0 0 - - -; #X floatatom 134 198 10 0 0 0 - - -; #X floatatom 213 198 10 0 0 0 - - -; #X floatatom 292 198 10 0 0 0 - - -; #X obj 55 74 route 0 1 2 3 4 5 6 7; #X floatatom 366 197 10 0 0 0 - - -; #X floatatom 445 197 10 0 0 0 - - -; #X floatatom 524 197 10 0 0 0 - - -; #X floatatom 603 197 10 0 0 0 - - -; #X obj 55 49 inlet; #X connect 4 0 0 0; #X connect 4 1 1 0; #X connect 4 2 2 0; #X connect 4 3 3 0; #X connect 4 4 5 0; #X connect 4 5 6 0; #X connect 4 6 7 0; #X connect 4 7 8 0; #X connect 9 0 4 0; #X restore 97 290 pd peek; #N canvas 0 0 450 300 tba 0; #X obj 143 51 inlet; #X obj 96 49 inlet; #X obj 191 51 inlet; #X obj 238 51 inlet; #X obj 173 180 outlet; #X obj 61 178 outlet; #X obj 221 180 outlet; #X obj 268 180 outlet; #X obj 126 180 outlet; #X obj 96 73 t b a; #X obj 143 72 t b a; #X obj 191 72 t b a; #X obj 238 72 t b a; #X connect 0 0 10 0; #X connect 1 0 9 0; #X connect 2 0 11 0; #X connect 3 0 12 0; #X connect 9 0 5 0; #X connect 9 1 8 0; #X connect 10 0 5 0; #X connect 10 1 4 0; #X connect 11 0 5 0; #X connect 11 1 6 0; #X connect 12 0 5 0; #X connect 12 1 7 0; #X restore 104 168 pd tba; #X obj 281 114 hsl 64 15 1 20 0 0 empty empty empty -2 -8 0 10 -262144 -1 -1 0 1; #X obj 546 102 loadbang; #X obj 83 345 outlet; #X obj 83 39 inlet; #X obj 304 140 hradio 15 1 0 3 empty empty id: -20 8 1 10 -262144 -1 -1 2; #X obj 546 123 2; #X obj 547 186 f \$1; #X text 386 187 in an abstraction \, use; #X text 583 189 topass id as argument.; #X obj 338 272 + 1; #X connect 0 0 12 0; #X connect 1 0 12 1; #X connect 2 0 12 2; #X connect 4 0 10 0; #X connect 4 0 11 0; #X connect 4 1 6 0; #X connect 4 2 7 0; #X connect 4 3 8 0; #X connect 4 4 9 0; #X connect 5 0 12 3; #X connect 10 0 15 0; #X connect 12 0 4 0; #X connect 12 1 4 1; #X connect 12 2 4 2; #X connect 12 3 4 3; #X connect 12 4 4 4; #X connect 13 0 5 0; #X connect 14 0 18 0; #X connect 16 0 4 0; #X connect 17 0 22 0; #X connect 18 0 17 0; #X connect 22 0 10 1; #X coords 0 -1 1 1 260 60 1 100 100; #X restore 566 230 pd rvbap-module3; #X text 594 203 put this into an abstraction:; #X text 63 21 RVBAP - Demo how to use [mtx_*~]; #X connect 0 0 19 0; #X connect 0 0 18 0; #X connect 0 0 17 0; #X connect 1 0 2 0; #X connect 2 0 17 0; #X connect 2 0 18 0; #X connect 2 0 19 0; #X connect 3 0 0 0; #X connect 4 0 9 4; #X connect 6 0 13 1; #X connect 7 0 6 0; #X connect 8 0 9 5; #X connect 9 0 5 0; #X connect 9 1 5 1; #X connect 9 2 5 2; #X connect 9 3 5 3; #X connect 10 0 13 2; #X connect 11 0 10 0; #X connect 12 0 13 0; #X connect 13 0 5 0; #X connect 13 1 5 1; #X connect 13 2 5 2; #X connect 13 3 5 3; #X connect 13 4 9 0; #X connect 13 5 9 1; #X connect 13 6 9 2; #X connect 13 7 9 3; #X connect 14 0 13 3; #X connect 15 0 14 0; #X connect 16 0 11 0; #X connect 16 0 15 0; #X connect 16 0 7 0; #X connect 17 0 12 0; #X connect 18 0 12 0; #X connect 19 0 12 0; vbap-1.2.0/examples/vbap-demo.pd000066400000000000000000000022271417256401100164630ustar00rootroot00000000000000#N canvas 11 22 871 352 10; #X obj 50 80 playsample~; #X obj 50 55 openpanel; #X msg 50 31 bang; #X text 96 30 click to load and play a sample; #X floatatom 133 80 5 0 0 0 - - -; #X floatatom 133 101 5 0 0 0 - - -; #X text 182 80 pitch; #X text 182 102 volume; #X floatatom 76 142 5 0 0 0 - - -; #X floatatom 122 142 5 0 0 0 - - -; #X floatatom 168 142 5 0 0 0 - - -; #X text 218 143 set azimuth \, elevation \, spread; #N canvas 152 420 615 353 using 0; #X obj 59 258 graph-to-aziele; #X obj 59 233 pack f f; #X floatatom 59 281 5 0 0 0 - - -; #X obj 59 78 grid grid1 144 0 1 144 0 1 0 0.001 0.001 10 10 103 91 ; #X floatatom 159 281 5 0 0 0 - - -; #X text 21 17 To use Yves Degoyon's GRID (http://ydegoyon.free.fr) use graph-to-aziele.pd to count the azimuth and elevation.; #X text 25 49 (note: GRID must output values between 0 - 1); #X connect 0 0 2 0; #X connect 0 1 4 0; #X connect 1 0 0 0; #X connect 3 0 1 0; #X connect 3 1 1 1; #X restore 506 112 pd using GRID with vbap; #X obj 50 174 vbapmodule 1; #X connect 0 0 13 0; #X connect 1 0 0 0; #X connect 2 0 1 0; #X connect 4 0 0 1; #X connect 5 0 0 2; #X connect 8 0 13 1; #X connect 9 0 13 2; #X connect 10 0 13 3; vbap-1.2.0/examples/vbap-level-config.pd000066400000000000000000000017061417256401100201120ustar00rootroot00000000000000#N canvas 334 22 636 372 10; #X obj 19 100 noise~; #X obj 19 153 *~ 0.01; #X obj 61 128 hsl 128 15 0.01 1 1 0 empty empty empty -2 -8 0 10 -262144 -1 -1 0 1; #X floatatom 71 149 5 0 0 0 - - -; #X obj 19 183 cyclone/reson~ 1 1000 20; #X obj 18 269 dac~ 1 2 3 4 5 6 7 8; #X text 217 131 B. Measure loudspeaker distances. Delay all loudspeakers to mimic the distance between listener and farthest loudspeaker. The amount of delay can be calculated as t = ((d - x) / 330) * 1000 \, where d is the distance in metres between listener and one particular loudspeaker \, and t is the amount of delay in milliseconds. Apply the delay to function function ls-delays to corresponding channel. ; #X text 217 80 A. Adjust gain each speaker's channel same level sweet spot using this file. You use a sound pressure meter or your ears. ; #X text 265 287 based on an example by Ville Pulkki 1999-2006; #X connect 0 0 1 0; #X connect 1 0 4 0; #X connect 2 0 1 1; #X connect 2 0 3 0; vbap-1.2.0/examples/vbap.main.pd000066400000000000000000000047611417256401100164710ustar00rootroot00000000000000#N canvas 153 122 664 665 10; #X obj 22 149 bng 15 250 50 0 empty empty empty 20 8 32 8 -262144 -1 -1; #X floatatom 353 470 5 0 0; #X obj 166 579 dac~ 1 2 3 4 5 6 7 8; #X obj 109 535 *~ 1; #X obj 142 535 *~ 1; #X obj 175 535 *~ 1; #X obj 207 535 *~ 1; #X obj 239 535 *~ 1; #X obj 271 535 *~ 1; #X obj 304 535 *~ 1; #X obj 336 535 *~ 1; #X floatatom 382 504 5 0 0; #X text 401 470 crossfade; #X obj 22 208 send speaker_setup; #X obj 22 169 define_loudspeakers 3 -30 0 30 0 -90 0 90 0 180 0 180 45 -45 45 45 45; #X obj 326 443 matrix 4 8; #X obj 269 470 matrix~ 4 8; #X obj 39 146 loadbang; #X msg 471 169 \; pd dsp 1; #X text 540 175 turn on audio; #X text 430 504 gain; #X obj 467 496 vsl 15 128 0 1 0 0 empty empty empty 20 8 32 8 -262144 -1 -1 0 1; #X msg 422 452 50; #X obj 438 413 loadbang; #X obj 484 494 r master; #X obj 21 89 inlet; #X text 66 89 no meaning; #X obj 326 340 receive matrix; #X text 30 22 define loudspeakers \, receive signals and data from vbap \, output audio; #X obj 99 146 receive define_ls; #X obj 52 318 catch~ 1chan; #X obj 162 318 catch~ 2chan; #X obj 272 318 catch~ 3chan; #X obj 382 318 catch~ 4chan; #N canvas 229 245 540 236 parameters.readme 0; #X text 37 35 define_loudspeakers <..>; #X text 37 70 dimensions is 2 or 3 \, followed by list of azimuths (in 2d) or pairs (in 3d) \, defining the number and positions of loudspeakers.; #X text 38 122 azimuth is -180 to 180 \, where -90 is left \, 0 front \, 90 right and 180 back.; #X text 38 161 elevation is -90 to 90 \, where -90 is down \, 0 is not elevated and 90 is up.; #X restore 258 206 pd parameters.readme; #X connect 0 0 14 0; #X connect 1 0 16 5; #X connect 3 0 2 0; #X connect 4 0 2 1; #X connect 5 0 2 2; #X connect 6 0 2 3; #X connect 7 0 2 4; #X connect 8 0 2 5; #X connect 9 0 2 6; #X connect 10 0 2 7; #X connect 11 0 10 1; #X connect 11 0 9 1; #X connect 11 0 8 1; #X connect 11 0 7 1; #X connect 11 0 6 1; #X connect 11 0 5 1; #X connect 11 0 4 1; #X connect 11 0 3 1; #X connect 11 0 9 1; #X connect 11 0 10 1; #X connect 14 0 13 0; #X connect 15 0 16 4; #X connect 16 0 3 0; #X connect 16 1 4 0; #X connect 16 2 5 0; #X connect 16 3 6 0; #X connect 16 4 7 0; #X connect 16 5 8 0; #X connect 16 6 9 0; #X connect 16 7 10 0; #X connect 17 0 0 0; #X connect 21 0 11 0; #X connect 22 0 1 0; #X connect 23 0 22 0; #X connect 23 0 18 0; #X connect 24 0 21 0; #X connect 27 0 15 0; #X connect 29 0 0 0; #X connect 30 0 16 0; #X connect 31 0 16 1; #X connect 32 0 16 2; #X connect 33 0 16 3; vbap-1.2.0/examples/vbapmodule.pd000066400000000000000000000010431417256401100167420ustar00rootroot00000000000000#N canvas 94 380 450 300 10; #X obj 30 44 inlet~; #X obj 62 158 send matrix; #X obj 46 183 throw~ \$1chan; #X floatatom 180 186 5 0 0; #X floatatom 225 186 5 0 0; #X floatatom 270 186 5 0 0; #X text 137 217 actual azi / ele / spread; #X obj 82 44 inlet; #X obj 122 44 inlet; #X obj 162 44 inlet; #X text 210 44 azi / ele / spread; #X obj 46 112 vbapsnd \$1; #X connect 0 0 11 0; #X connect 7 0 11 1; #X connect 8 0 11 2; #X connect 9 0 11 3; #X connect 11 0 2 0; #X connect 11 1 1 0; #X connect 11 2 3 0; #X connect 11 3 4 0; #X connect 11 4 5 0; vbap-1.2.0/examples/vbapsnd.pd000066400000000000000000000030371417256401100162460ustar00rootroot00000000000000#N canvas 33 53 661 597 10; #X obj 133 20 inlet; #X obj 189 171 vbap 0 0; #X obj 240 20 inlet; #X obj 359 20 inlet; #X text 176 19 azimuth; #X text 285 19 elevation; #X text 405 19 spread; #X obj 233 460 pack f f f; #X obj 298 437 float \$1; #X obj 298 414 loadbang; #X msg 233 495 element \$3 \$1 \$2; #X text 287 536 to matrix object; #X obj 441 535 outlet; #X obj 488 535 outlet; #X obj 535 535 outlet; #X obj 359 78 t b f; #X obj 240 80 t b f; #X obj 133 80 t b f; #X obj 189 106 receive speaker_setup; #X obj 189 256 unpack f f; #X obj 189 279 + 1; #X obj 167 429 ==; #X obj 167 453 sel 1; #X obj 189 303 t f f f; #X obj 195 429 high; #X obj 233 536 outlet; #X obj 31 20 inlet~; #X obj 31 83 outlet~; #X text 28 113 for convenience; #X floatatom 489 62 5 0 0; #X obj 359 49 recent 10; #X obj 240 51 recent 10; #X obj 133 51 recent 10; #X connect 0 0 32 0; #X connect 1 0 19 0; #X connect 1 1 12 0; #X connect 1 2 13 0; #X connect 1 3 14 0; #X connect 2 0 31 0; #X connect 3 0 30 0; #X connect 7 0 10 0; #X connect 8 0 7 2; #X connect 9 0 8 0; #X connect 10 0 25 0; #X connect 15 0 1 0; #X connect 15 1 1 3; #X connect 16 0 1 0; #X connect 16 1 1 2; #X connect 17 0 1 0; #X connect 17 1 1 1; #X connect 18 0 1 0; #X connect 19 0 20 0; #X connect 19 1 7 1; #X connect 20 0 23 0; #X connect 21 0 22 0; #X connect 22 0 25 0; #X connect 23 0 21 0; #X connect 23 1 24 0; #X connect 23 2 7 0; #X connect 24 0 21 1; #X connect 26 0 27 0; #X connect 29 0 30 1; #X connect 29 0 31 1; #X connect 29 0 32 1; #X connect 30 0 15 0; #X connect 31 0 16 0; #X connect 32 0 17 0; vbap-1.2.0/max2pd.h000066400000000000000000000022371417256401100140130ustar00rootroot00000000000000/* * this header aims to make it easy to port Max objects to Pd */ /* name changes */ #define SETSYM SETSYMBOL /* Pd doesn't have longs */ #define SETLONG SETFLOAT /* different names for the same thing */ #define Atom t_atom #define Symbol t_symbol /* allocate memory */ #define sysmem_newptr(size) getbytes(128) #define sysmem_freeptr(ptr) freebytes(ptr, 128) /* standard object API functions */ #define atom_getlong(atom) atom_getfloatarg(0, 1, atom) #define atom_getsym(atom) atom_getsymbolarg(0, 1, atom) #define object_alloc(obj_class) pd_new(obj_class) #define object_free(obj) pd_free((t_pd*)obj) #define newobject(class) pd_new(class) #define outlet_int(outlet, number) outlet_float(outlet, number) /* debug things */ #define _enable_trace sys_verbose /* these are NOT included here because they would cause more problems than * they would solve. Usually, they are used in the setup() and new() * functions, where most of the differences are between the Max and PD APIs */ /* macros */ // A_DEFLONG /* types */ // method // Object /* functions */ // addint() // addmess() // newobject() // outlet_new() // setup() vbap-1.2.0/rvbap-help.pd000066400000000000000000000176751417256401100150500ustar00rootroot00000000000000#N canvas 143 28 813 728 10; #X obj 98 144 define_loudspeakers 3 -45 0 45 0 0 45 180 45; #X msg 32 62 bang; #X floatatom 136 276 5 0 0 2 azi - -, f 5; #X floatatom 180 276 5 0 0 2 ele - -, f 5; #X floatatom 224 276 5 0 100 2 spread - -, f 5; #X msg 115 173 bang; #X text 429 233 In two dimensions \, only specify the azimuth. (for example "define_loudspeakers 2 -45 45 0 180"; #X text 63 21 VBAP and define_loudspeakers; #X text 429 362 The spread-parameter can be used to prevent a situation where sound is coming from one speaker only \, which would make speaker positions "visible". The range is 0 to 100; #X text 231 410 actual location; #X obj 115 350 rvbap 0 0; #X floatatom 277 277 5 1 20 2 dist - -, f 5; #X text 131 241 azimuth \, elevation \, spread and distance; #X msg 98 119 bang; #X text 429 141 1) Use define_loudspeakers to list the speaker positions. The example here defines loudspeakers in three dimensions (the first parameter). For each speaker \, define its azimuth and elevation. Here we have speakers front left and right with no elevation (-45 0 45 0) and front and back with 45 degrees of elevation (0 45 180 45). Send the data to vbap.; #X floatatom 159 381 5 0 0 3 azi - -, f 5; #X floatatom 203 381 5 0 0 3 ele - -, f 5; #X floatatom 247 381 5 0 0 3 spread - -, f 5; #X floatatom 300 382 5 0 0 3 dist - -, f 5; #X floatatom 277 597 5 0 100 1 dB_after_1sec - -, f 5; #X obj 114 691 dac~ 1 2 3 4; #N canvas 0 22 699 527 sig 0; #X obj 58 72 line~; #X msg 58 49 0 \, 10000 5; #X obj 58 118 cos~; #X msg 146 70 1; #X obj 146 47 loadbang; #X obj 58 95 clip~ 0 0.25; #X obj 251 134 line~; #X obj 251 157 cos~; #X msg 324 54 -0.25 \, 0.25 100; #X obj 251 8 loadbang; #X msg 251 31 -0.25; #X obj 251 203 *~; #X obj 58 140 hip~ 5; #X msg 324 77 -0.25 \, 0.25 400; #X floatatom 324 145 0 0 0 0 - - -; #X obj 324 191 osc~ 440; #X obj 324 168 mtof; #X msg 324 31 -0.25 \, 0.25 20; #X obj 251 180 *~ 0.1; #X msg 324 100 -0.25 \, 0.25 1000; #X msg 324 122 -0.25 \, 0.25 2000; #X obj 324 226 *~; #X obj 342 252 *~; #X msg 324 8 0; #X obj 308 257 *~; #X obj 58 26 metro 2000; #X floatatom 58 4 0 0 0 0 - - -; #X text 1 51 impulse; #X text 362 7 tone; #X obj 59 184 outlet~; #X obj 170 6 inlet; #X obj 442 18 metro 500; #X obj 91 8 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1 ; #X connect 0 0 5 0; #X connect 1 0 0 0; #X connect 2 0 12 0; #X connect 3 0 0 0; #X connect 4 0 3 0; #X connect 5 0 2 0; #X connect 6 0 7 0; #X connect 7 0 18 0; #X connect 8 0 6 0; #X connect 9 0 10 0; #X connect 10 0 6 0; #X connect 11 0 12 0; #X connect 12 0 29 0; #X connect 13 0 6 0; #X connect 14 0 16 0; #X connect 15 0 11 1; #X connect 15 0 21 0; #X connect 15 0 21 1; #X connect 15 0 22 0; #X connect 16 0 15 0; #X connect 17 0 6 0; #X connect 18 0 11 0; #X connect 19 0 6 0; #X connect 20 0 6 0; #X connect 21 0 22 1; #X connect 21 0 11 1; #X connect 21 0 24 0; #X connect 21 0 24 1; #X connect 22 0 11 1; #X connect 23 0 6 0; #X connect 24 0 11 1; #X connect 25 0 1 0; #X connect 26 0 25 0; #X connect 30 0 31 0; #X connect 31 0 8 0; #X connect 32 0 25 0; #X restore 159 545 pd sig; #X obj 159 517 tgl 24 0 empty empty test-sigs 26 7 1 10 -262144 -1 -1 0 1; #X obj 115 488 mtx 8 1; #N canvas 352 196 547 360 set-element 0; #X obj 70 81 unpack 0 0; #X obj 70 104 + 1; #X obj 70 137 pack 0 1 0; #X obj 70 179 list trim; #X obj 70 158 list prepend element; #X obj 70 59 inlet; #X obj 70 261 outlet; #X obj 70 221 t b a; #X text 48 33 transforms rvbap-output to be used with [mtx] from iemmatrix ; #X connect 0 0 1 0; #X connect 0 1 2 2; #X connect 1 0 2 0; #X connect 2 0 4 0; #X connect 3 0 7 0; #X connect 4 0 3 0; #X connect 5 0 0 0; #X connect 7 0 6 0; #X connect 7 1 6 0; #X restore 115 464 pd set-element; #N canvas 203 642 802 273 peek 0; #X floatatom 55 198 10 0 0 0 - - -, f 10; #X floatatom 134 198 10 0 0 0 - - -, f 10; #X floatatom 213 198 10 0 0 0 - - -, f 10; #X floatatom 292 198 10 0 0 0 - - -, f 10; #X obj 55 74 route 0 1 2 3 4 5 6 7; #X floatatom 366 197 10 0 0 0 - - -, f 10; #X floatatom 445 197 10 0 0 0 - - -, f 10; #X floatatom 524 197 10 0 0 0 - - -, f 10; #X floatatom 603 197 10 0 0 0 - - -, f 10; #X obj 55 49 inlet; #X connect 4 0 0 0; #X connect 4 1 1 0; #X connect 4 2 2 0; #X connect 4 3 3 0; #X connect 4 4 5 0; #X connect 4 5 6 0; #X connect 4 6 7 0; #X connect 4 7 8 0; #X connect 9 0 4 0; #X restore 129 438 pd peek; #X floatatom 201 567 5 0 0 1 interp - -, f 5; #X msg 297 622 clear; #N canvas 0 22 450 300 tba 0; #X obj 143 51 inlet; #X obj 96 49 inlet; #X obj 191 51 inlet; #X obj 238 51 inlet; #X obj 173 180 outlet; #X obj 61 178 outlet; #X obj 221 180 outlet; #X obj 268 180 outlet; #X obj 126 180 outlet; #X obj 96 73 t b a; #X obj 143 72 t b a; #X obj 191 72 t b a; #X obj 238 72 t b a; #X connect 0 0 10 0; #X connect 1 0 9 0; #X connect 2 0 11 0; #X connect 3 0 12 0; #X connect 9 0 5 0; #X connect 9 1 8 0; #X connect 10 0 5 0; #X connect 10 1 4 0; #X connect 11 0 5 0; #X connect 11 1 6 0; #X connect 12 0 5 0; #X connect 12 1 7 0; #X restore 136 316 pd tba; #X obj 318 262 hsl 64 15 1 20 0 0 empty empty empty -2 -8 0 10 -262144 -1 -1 0 1; #N canvas 0 22 766 594 four-reverbs 0; #X obj 67 179 rev1~; #X obj 114 179 rev1~; #X obj 160 179 rev1~; #X obj 210 179 rev1~; #X obj 282 55 inlet; #X obj 67 54 inlet~; #X obj 113 54 inlet~; #X obj 160 53 inlet~; #X obj 211 53 inlet~; #X obj 67 286 outlet~; #X obj 114 268 outlet~; #X obj 159 246 outlet~; #X obj 209 226 outlet~; #X obj 339 55 inlet; #X obj 339 76 b; #X connect 0 0 9 0; #X connect 1 0 10 0; #X connect 2 0 11 0; #X connect 3 0 12 0; #X connect 4 0 3 1; #X connect 4 0 2 1; #X connect 4 0 1 1; #X connect 4 0 0 1; #X connect 5 0 0 0; #X connect 6 0 1 0; #X connect 7 0 2 0; #X connect 8 0 3 0; #X connect 13 0 14 0; #X connect 14 0 3 2; #X connect 14 0 2 2; #X connect 14 0 1 2; #X connect 14 0 0 2; #X restore 197 644 pd four-reverbs; #X text 429 45 rvbap is almost compatible to; #X obj 33 82 define_loudspeakers 2 -45 45 0 180; #X obj 115 599 mtx_*~ 8 1 20; #X text 429 82 Additionally it generates additional commands for controlling a reverberated signal and has control to set the radial distance of a sound.; #X text 429 421 3) rvbap also will generate messages to control the amount of reverberated signal to generate. This is meant to be used with [matrix~] or [mtx_*~] from the IEMmatrix collection of externals. ; #X text 429 281 2) For rvbap \, give azimuth and elevation and a distance (1-inf \, default 1) for the desired location. Bang the first inlet and vbap will output gain-factors for each speaker and the actual location produced. This can be different from the desired one depending where your speakers are.; #X text 428 618 See examples/rvbap-demo.pd for a more complex setup.; #X text 429 491 To use it \, create a [mtx_*~] object that has double the amount of outlets as you have speakers. Send the first half of the matrix-signals to the speakers and the second half through a reverbarator and add them to the respective speaker outs. The example shows this in action for four speakers. Pay attention to the "set-element" subpatch which translates the [rvbap] output to set matrix elements correctly. ; #X text 193 439 <= here's the output of [rvbap]; #X obj 633 7 import hexloader iemmatrix; #X obj 617 45 vbap; #X connect 0 0 10 0; #X connect 1 0 32 0; #X connect 2 0 28 0; #X connect 3 0 28 1; #X connect 4 0 28 2; #X connect 5 0 10 0; #X connect 10 0 24 0; #X connect 10 0 25 0; #X connect 10 1 15 0; #X connect 10 2 16 0; #X connect 10 3 17 0; #X connect 10 4 18 0; #X connect 11 0 28 3; #X connect 13 0 0 0; #X connect 19 0 30 4; #X connect 21 0 33 1; #X connect 22 0 21 0; #X connect 23 0 33 0; #X connect 24 0 23 0; #X connect 26 0 33 2; #X connect 27 0 30 5; #X connect 28 0 10 0; #X connect 28 1 10 1; #X connect 28 2 10 2; #X connect 28 3 10 3; #X connect 28 4 10 4; #X connect 29 0 11 0; #X connect 30 0 20 0; #X connect 30 1 20 1; #X connect 30 2 20 2; #X connect 30 3 20 3; #X connect 32 0 10 0; #X connect 33 0 20 0; #X connect 33 1 20 1; #X connect 33 2 20 2; #X connect 33 3 20 3; #X connect 33 4 30 0; #X connect 33 5 30 1; #X connect 33 6 30 2; #X connect 33 7 30 3; vbap-1.2.0/rvbap.c000066400000000000000000000762331417256401100137340ustar00rootroot00000000000000/* rvbap.c vers 1.1 written by Ville Pulkki 1999-2003 Helsinki University of Technology and Unversity of California at Berkeley and written by Olaf Matthes 2003, 2007 Pd port by Frank Barknecht See copyright in file with name LICENSE.txt */ #include #ifdef MAXMSP #include "ext.h" /* you must include this - it contains the external object's link to max */ #define t_float float #endif #ifdef PD #include "m_pd.h" /* you must include this - it contains the external object's link to pure data */ #endif #ifndef M_PI #define M_PI 3.14159265358979323846264338327950288 #endif #define RVBAP_VERSION "rvbap v1.2 - (c) Olaf Matthes 2003-2007, based on vbap by Ville Pulkki" #define MAX_LS_SETS 100 // maximum number of loudspeaker sets (triplets or pairs) allowed #define MAX_LS_AMOUNT 55 // maximum amount of loudspeakers, can be increased #ifdef _WINDOWS #define sqrtf sqrt #endif #ifdef MAXMSP typedef struct vbap /* This defines the object as an entity made up of other things */ { t_object x_ob; long x_azi; // panning direction azimuth long x_ele; // panning direction elevation t_float x_dist; // sound source distance (1.0-infinity) void *x_outlet0; void *x_outlet1; void *x_outlet2; void *x_outlet3; void *x_outlet4; t_float x_set_inv_matx[MAX_LS_SETS][9];// inverse matrix for each loudspeaker set t_float x_set_matx[MAX_LS_SETS][9]; // matrix for each loudspeaker set long x_lsset[MAX_LS_SETS][3]; // channel numbers of loudspeakers in each LS set long x_lsset_available; // have loudspeaker sets been defined with define_loudspeakers long x_lsset_amount; // amount of loudspeaker sets long x_ls_amount; // amount of loudspeakers long x_dimension; // 2 or 3 long x_spread; // spreading amount of virtual source (0-100) t_float x_spread_base[3]; // used to create uniform spreading t_float x_reverb_gs[MAX_LS_SETS]; // correction value for each loudspeaker set to get equal volume } t_rvbap; #endif #ifdef PD typedef struct vbap /* This defines the object as an entity made up of other things */ { t_object x_ob; t_float x_azi; // panning direction azimuth t_float x_ele; // panning direction elevation t_float x_dist; // sound source distance (1.0-infinity) void *x_outlet0; /* outlet creation - inlets are automatic */ void *x_outlet1; void *x_outlet2; void *x_outlet3; void *x_outlet4; t_float x_set_inv_matx[MAX_LS_SETS][9];// inverse matrix for each loudspeaker set t_float x_set_matx[MAX_LS_SETS][9]; // matrix for each loudspeaker set long x_lsset[MAX_LS_SETS][3]; // channel numbers of loudspeakers in each LS set long x_lsset_available; // have loudspeaker sets been defined with define_loudspeakers long x_lsset_amount; // amount of loudspeaker sets long x_ls_amount; // amount of loudspeakers long x_dimension; // 2 or 3 t_float x_spread; // spreading amount of virtual source (0-100) t_float x_spread_base[3]; // used to create uniform spreading t_float x_reverb_gs[MAX_LS_SETS]; // correction value for each loudspeaker set to get equal volume } t_rvbap; #endif // globals static void new_spread_dir(t_rvbap *x, t_float spreaddir[3], t_float vscartdir[3], t_float spread_base[3]); static void new_spread_base(t_rvbap *x, t_float spreaddir[3], t_float vscartdir[3]); #ifdef MAXMSP static void *rvbap_class; static void rvbap_assist(t_rvbap *x, void *b, long m, long a, char *s); static void rvbap_in1(t_rvbap *x, long n); static void rvbap_in2(t_rvbap *x, long n); static void rvbap_in3(t_rvbap *x, long n); static void rvbap_in4(t_rvbap *x, long n); static void rvbap_ft1(t_rvbap *x, double n); static void rvbap_ft2(t_rvbap *x, double n); static void rvbap_ft3(t_rvbap *x, double n); static void rvbap_ft4(t_rvbap *x, double n); #endif #ifdef PD static t_class *rvbap_class; #endif static void cross_prod(t_float v1[3], t_float v2[3], t_float v3[3]); static void additive_vbap(t_float *final_gs, t_float cartdir[3], t_rvbap *x); static void rvbap_bang(t_rvbap *x); static void rvbap_matrix(t_rvbap *x, t_symbol *s, int ac, t_atom *av); static void spread_it(t_rvbap *x, t_float *final_gs); static void *rvbap_new(t_symbol *s, int ac, t_atom *av); // using A_GIMME - typed message list static void vbap(t_float g[3], long ls[3], t_rvbap *x); static void angle_to_cart(long azi, long ele, t_float res[3]); static void cart_to_angle(t_float cvec[3], t_float avec[3]); static void equal_reverb(t_rvbap *x, t_float *final_gs); /* above are the prototypes for the methods/procedures/functions you will use */ #ifdef PD void rvbap_setup(void) { rvbap_class = class_new(gensym("rvbap"), (t_newmethod)rvbap_new, 0, (short)sizeof(t_rvbap), 0, A_GIMME, 0); /* rvbap_new = creation function, A_DEFLONG = its (optional) argument is a long (32-bit) int */ class_addbang(rvbap_class, rvbap_bang); class_addmethod(rvbap_class, (t_method)rvbap_matrix, gensym("loudspeaker-matrices"), A_GIMME, 0); } #endif #ifdef MAXMSP int main(void) { setup((t_messlist **)&rvbap_class, (method)rvbap_new, 0L, (short)sizeof(t_rvbap), 0L, A_GIMME, 0); /* rvbap_new = creation function, A_DEFLONG = its (optional) argument is a long (32-bit) int */ addmess((method)rvbap_assist, "assist", A_CANT, 0); addbang((method)rvbap_bang); /* the procedure it uses when it gets a bang in the left inlet */ addinx((method)rvbap_in1, 1); /* the procedure for an int in the right inlet (inlet 1) */ addinx((method)rvbap_in2, 2); /* the procedure for an int in the right inlet (inlet 2) */ addinx((method)rvbap_in3, 3); addinx((method)rvbap_in4, 4); addftx((method)rvbap_ft1, 1); /* the procedure for an int in the right inlet (inlet 1) */ addftx((method)rvbap_ft2, 2); /* the procedure for an int in the right inlet (inlet 2) */ addftx((method)rvbap_ft3, 3); addftx((method)rvbap_ft4, 4); addmess((method)rvbap_matrix, "loudspeaker-matrices", A_GIMME, 0); post(RVBAP_VERSION); return 0; } static void rvbap_assist(t_rvbap *x, void *b, long m, long a, char *s) { switch(m){ case 1: // inlet switch(a){ case 0: sprintf(s, "define_loudspeakers / Bang to output actual values."); break; case 1: sprintf(s, "(int) azimuth"); break; case 2: sprintf(s, "(int) elevation"); break; case 3: sprintf(s, "(int) spreading"); break; case 4: sprintf(s, "(t_float) distance"); break; } break; case 2: // outlet switch(a){ case 0: sprintf(s, "(list) matrix~ values"); break; case 1: sprintf(s, "(int) actual azimuth"); break; case 2: sprintf(s, "(int) actual elevation"); break; case 3: sprintf(s, "(int) actual spreading"); break; case 4: sprintf(s, "(t_float) actual distance"); break; } break; } } #endif /* end MAXMSP */ static void angle_to_cart(long azi, long ele, t_float res[3]) /* converts angular coordinates to cartesian */ { t_float atorad = (2.0 * M_PI / 360.0) ; res[0] = cos((t_float) azi * atorad) * cos((t_float) ele * atorad); res[1] = sin((t_float) azi * atorad) * cos((t_float) ele * atorad); res[2] = sin((t_float) ele * atorad); } static void cart_to_angle(t_float cvec[3], t_float avec[3]) // converts cartesian coordinates to angular { t_float atorad = (t_float)(2.0 * M_PI / 360.0) ; t_float pi = (t_float)M_PI; t_float dist, atan_y_per_x, atan_x_pl_y_per_z; t_float azi, ele; if(cvec[0] == 0.0) atan_y_per_x = pi / 2; else atan_y_per_x = atan(cvec[1] / cvec[0]); azi = atan_y_per_x / atorad; if(cvec[0] < 0.0) azi += 180; dist = sqrt(cvec[0]*cvec[0] + cvec[1]*cvec[1]); if(cvec[2] == 0.0) atan_x_pl_y_per_z = 0.0; else atan_x_pl_y_per_z = atan(cvec[2] / dist); if(dist == 0.0){ if(cvec[2] < 0.0) atan_x_pl_y_per_z = -pi/2.0; else atan_x_pl_y_per_z = pi/2.0; } ele = atan_x_pl_y_per_z / atorad; dist = sqrt(cvec[0]*cvec[0] + cvec[1]*cvec[1] + cvec[2]*cvec[2]); avec[0] = azi; avec[1] = ele; avec[2] = dist; } static void vbap(t_float g[3], long ls[3], t_rvbap *x) { /* calculates gain factors using loudspeaker setup and given direction */ t_float power; int i, j, k, gains_modified; t_float small_g; t_float big_sm_g, gtmp[3]; long winner_set = 0; t_float cartdir[3]; t_float new_cartdir[3]; t_float new_angle_dir[3]; long dim = x->x_dimension; long neg_g_am, best_neg_g_am; // transferring the azimuth angle to a decent value while(x->x_azi > 180) x->x_azi -= 360; while(x->x_azi < -179) x->x_azi += 360; // transferring the elevation to a decent value if(dim == 3){ while(x->x_ele > 180) x->x_ele -= 360; while(x->x_ele < -179) x->x_ele += 360; } else x->x_ele = 0; // go through all defined loudspeaker sets and find the set which // has all positive values. If such is not found, set with largest // minimum value is chosen. If at least one of gain factors of one LS set is negative // it means that the virtual source does not lie in that LS set. angle_to_cart(x->x_azi, x->x_ele, cartdir); big_sm_g = -100000.0; // initial value for largest minimum gain value best_neg_g_am = 3; // how many negative values in this set for(i = 0; i < x->x_lsset_amount; i++){ small_g = 10000000.0; neg_g_am = 3; for(j = 0; j < dim; j++){ gtmp[j] = 0.0; for(k = 0; k < dim; k++) gtmp[j] += cartdir[k]* x->x_set_inv_matx[i][k+j*dim]; if(gtmp[j] < small_g) small_g = gtmp[j]; if(gtmp[j] >= -0.01) neg_g_am--; } if(small_g > big_sm_g && neg_g_am <= best_neg_g_am){ big_sm_g = small_g; best_neg_g_am = neg_g_am; winner_set = i; g[0] = gtmp[0]; g[1] = gtmp[1]; ls[0] = x->x_lsset[i][0]; ls[1] = x->x_lsset[i][1]; if(dim == 3){ g[2] = gtmp[2]; ls[2] = x->x_lsset[i][2]; } else { g[2] = 0.0; ls[2] = 0; } } } // If chosen set produced a negative value, make it zero and // calculate direction that corresponds to these new // gain values. This happens when the virtual source is outside of // all loudspeaker sets. if(dim == 3){ gains_modified = 0; for(i = 0; i < dim; i++){ if(g[i] < -0.01){ g[i] = 0.0001; gains_modified = 1; } } if(gains_modified == 1){ new_cartdir[0] = x->x_set_matx[winner_set][0] * g[0] + x->x_set_matx[winner_set][1] * g[1] + x->x_set_matx[winner_set][2] * g[2]; new_cartdir[1] = x->x_set_matx[winner_set][3] * g[0] + x->x_set_matx[winner_set][4] * g[1] + x->x_set_matx[winner_set][5] * g[2]; new_cartdir[2] = x->x_set_matx[winner_set][6] * g[0] + x->x_set_matx[winner_set][7] * g[1] + x->x_set_matx[winner_set][8] * g[2]; cart_to_angle(new_cartdir, new_angle_dir); x->x_azi = (long) (new_angle_dir[0] + 0.5); x->x_ele = (long) (new_angle_dir[1] + 0.5); } } power = sqrt(g[0]*g[0] + g[1]*g[1] + g[2]*g[2]); g[0] /= power; g[1] /= power; g[2] /= power; } static void cross_prod(t_float v1[3], t_float v2[3], t_float v3[3]) // vector cross product { t_float length; v3[0] = (v1[1] * v2[2] ) - (v1[2] * v2[1]); v3[1] = (v1[2] * v2[0] ) - (v1[0] * v2[2]); v3[2] = (v1[0] * v2[1] ) - (v1[1] * v2[0]); length = sqrt(v3[0]*v3[0] + v3[1]*v3[1] + v3[2]*v3[2]); v3[0] /= length; v3[1] /= length; v3[2] /= length; } static void additive_vbap(t_float *final_gs, t_float cartdir[3], t_rvbap *x) // calculates gains to be added to previous gains, used in // multiple direction panning (source spreading) { t_float power; int i, j, k, gains_modified; t_float small_g; t_float big_sm_g, gtmp[3]; long dim = x->x_dimension; long neg_g_am, best_neg_g_am; t_float g[3]; long ls[3] = {0, 0, 0}; big_sm_g = -100000.0; best_neg_g_am = 3; for(i = 0; i < x->x_lsset_amount; i++){ small_g = 10000000.0; neg_g_am = 3; for(j = 0; j < dim; j++){ gtmp[j] = 0.0; for(k = 0; k < dim; k++) gtmp[j] += cartdir[k]* x->x_set_inv_matx[i][k+j*dim]; if(gtmp[j] < small_g) small_g = gtmp[j]; if(gtmp[j] >= -0.01) neg_g_am--; } if(small_g > big_sm_g && neg_g_am <= best_neg_g_am){ big_sm_g = small_g; best_neg_g_am = neg_g_am; g[0] = gtmp[0]; g[1] = gtmp[1]; ls[0] = x->x_lsset[i][0]; ls[1] = x->x_lsset[i][1]; if(dim == 3){ g[2] = gtmp[2]; ls[2] = x->x_lsset[i][2]; } else { g[2] = 0.0; ls[2] = 0; } } } gains_modified = 0; for(i = 0; i < dim; i++){ if(g[i] < -0.01){ gains_modified = 1; } } if(gains_modified != 1){ if(dim == 3) power = sqrt(g[0]*g[0] + g[1]*g[1] + g[2]*g[2]); else power = sqrt(g[0]*g[0] + g[1]*g[1]); g[0] /= power; g[1] /= power; if(dim == 3) g[2] /= power; final_gs[ls[0]-1] += g[0]; final_gs[ls[1]-1] += g[1]; /* BUG FIX: this was causing negative indices with 2 dimensions so I * made it only try when using 3 dimensions. * 2006-08-13 */ if(dim == 3) final_gs[ls[2]-1] += g[2]; } } static void new_spread_dir(t_rvbap *x, t_float spreaddir[3], t_float vscartdir[3], t_float spread_base[3]) // subroutine for spreading { t_float beta, m_gamma; t_float a, b; t_float pi = M_PI; t_float power; m_gamma = acos(vscartdir[0] * spread_base[0] + vscartdir[1] * spread_base[1] + vscartdir[2] * spread_base[2]) / pi * 180; if(fabs(m_gamma) < 1){ angle_to_cart(x->x_azi + 90, 0, spread_base); m_gamma = acos(vscartdir[0] * spread_base[0] + vscartdir[1] * spread_base[1] + vscartdir[2] * spread_base[2]) / pi * 180; } beta = 180 - m_gamma; b = sin(x->x_spread * pi / 180) / sin(beta * pi / 180); a = sin((180- x->x_spread - beta) * pi / 180) / sin(beta * pi / 180); spreaddir[0] = a * vscartdir[0] + b * spread_base[0]; spreaddir[1] = a * vscartdir[1] + b * spread_base[1]; spreaddir[2] = a * vscartdir[2] + b * spread_base[2]; power=sqrt(spreaddir[0]*spreaddir[0] + spreaddir[1]*spreaddir[1] + spreaddir[2]*spreaddir[2]); spreaddir[0] /= power; spreaddir[1] /= power; spreaddir[2] /= power; } static void new_spread_base(t_rvbap *x, t_float spreaddir[3], t_float vscartdir[3]) // subroutine for spreading { t_float d; t_float pi = M_PI; t_float power; d = cos(x->x_spread / 180 * pi); x->x_spread_base[0] = spreaddir[0] - d * vscartdir[0]; x->x_spread_base[1] = spreaddir[1] - d * vscartdir[1]; x->x_spread_base[2] = spreaddir[2] - d * vscartdir[2]; power = sqrt(x->x_spread_base[0]*x->x_spread_base[0] + x->x_spread_base[1]*x->x_spread_base[1] + x->x_spread_base[2]*x->x_spread_base[2]); x->x_spread_base[0] /= power; x->x_spread_base[1] /= power; x->x_spread_base[2] /= power; } static void spread_it(t_rvbap *x, t_float *final_gs) // apply the sound signal to multiple panning directions // that causes some spreading. // See theory in paper V. Pulkki "Uniform spreading of amplitude panned // virtual sources" in WASPAA 99 { t_float vscartdir[3]; t_float spreaddir[16][3]; t_float spreadbase[16][3]; long i, spreaddirnum; t_float power; if(x->x_dimension == 3){ spreaddirnum = 16; angle_to_cart(x->x_azi, x->x_ele, vscartdir); new_spread_dir(x, spreaddir[0], vscartdir, x->x_spread_base); new_spread_base(x, spreaddir[0], vscartdir); cross_prod(x->x_spread_base, vscartdir, spreadbase[1]); // four orthogonal dirs cross_prod(spreadbase[1], vscartdir, spreadbase[2]); cross_prod(spreadbase[2], vscartdir, spreadbase[3]); // four between them for(i=0;i<3;i++) spreadbase[4][i] = (x->x_spread_base[i] + spreadbase[1][i]) / 2.0; for(i=0;i<3;i++) spreadbase[5][i] = (spreadbase[1][i] + spreadbase[2][i]) / 2.0; for(i=0;i<3;i++) spreadbase[6][i] = (spreadbase[2][i] + spreadbase[3][i]) / 2.0; for(i=0;i<3;i++) spreadbase[7][i] = (spreadbase[3][i] + x->x_spread_base[i]) / 2.0; // four at half spread angle for(i=0;i<3;i++) spreadbase[8][i] = (vscartdir[i] + x->x_spread_base[i]) / 2.0; for(i=0;i<3;i++) spreadbase[9][i] = (vscartdir[i] + spreadbase[1][i]) / 2.0; for(i=0;i<3;i++) spreadbase[10][i] = (vscartdir[i] + spreadbase[2][i]) / 2.0; for(i=0;i<3;i++) spreadbase[11][i] = (vscartdir[i] + spreadbase[3][i]) / 2.0; // four at quarter spread angle for(i=0;i<3;i++) spreadbase[12][i] = (vscartdir[i] + spreadbase[8][i]) / 2.0; for(i=0;i<3;i++) spreadbase[13][i] = (vscartdir[i] + spreadbase[9][i]) / 2.0; for(i=0;i<3;i++) spreadbase[14][i] = (vscartdir[i] + spreadbase[10][i]) / 2.0; for(i=0;i<3;i++) spreadbase[15][i] = (vscartdir[i] + spreadbase[11][i]) / 2.0; additive_vbap(final_gs, spreaddir[0], x); for(i = 1; i < spreaddirnum; i++){ new_spread_dir(x, spreaddir[i], vscartdir, spreadbase[i]); additive_vbap(final_gs, spreaddir[i], x); } } else if(x->x_dimension == 2){ spreaddirnum = 6; angle_to_cart(x->x_azi - x->x_spread, 0, spreaddir[0]); angle_to_cart(x->x_azi - x->x_spread / 2, 0, spreaddir[1]); angle_to_cart(x->x_azi - x->x_spread / 4, 0, spreaddir[2]); angle_to_cart(x->x_azi + x->x_spread / 4, 0, spreaddir[3]); angle_to_cart(x->x_azi + x->x_spread / 2, 0, spreaddir[4]); angle_to_cart(x->x_azi + x->x_spread, 0, spreaddir[5]); for(i = 0; i < spreaddirnum; i++) additive_vbap(final_gs, spreaddir[i], x); } else return; if(x->x_spread > 70) for(i = 0; i < x->x_ls_amount; i++){ final_gs[i] += (x->x_spread - 70) / 30.0 * (x->x_spread - 70) / 30.0 * 10.0; } for(i = 0, power = 0.0; i < x->x_ls_amount; i++){ power += final_gs[i] * final_gs[i]; } power = sqrt(power); for(i = 0; i < x->x_ls_amount; i++){ final_gs[i] /= power; } } static void equal_reverb(t_rvbap *x, t_float *final_gs) // calculate constant reverb gains for equally distributed // reverb levels // this is achieved by calculating gains for a sound source // that is everywhere, i.e. present in all directions { t_float spreaddir[16][3]; long i, spreaddirnum; t_float power; if(x->x_dimension == 3){ spreaddirnum = 5; // horizontal plane angle_to_cart( 90, 0, spreaddir[0]); angle_to_cart(180, 0, spreaddir[1]); angle_to_cart(270, 0, spreaddir[2]); // above, below angle_to_cart(0, 90, spreaddir[3]); angle_to_cart(0, -90, spreaddir[4]); for(i = 1; i < spreaddirnum; i++){ additive_vbap(x->x_reverb_gs, spreaddir[i], x); } } else if(x->x_dimension == 2){ // for 2-D we calculate virtual sources // every 45 degrees in a horizontal plane spreaddirnum = 7; angle_to_cart( 90, 0, spreaddir[0]); angle_to_cart(180, 0, spreaddir[1]); angle_to_cart(270, 0, spreaddir[2]); angle_to_cart( 45, 0, spreaddir[3]); angle_to_cart(135, 0, spreaddir[4]); angle_to_cart(225, 0, spreaddir[5]); angle_to_cart(315, 0, spreaddir[6]); for(i = 0; i < spreaddirnum; i++) additive_vbap(x->x_reverb_gs, spreaddir[i], x); } else return; for(i = 0, power = 0.0; i < x->x_ls_amount; i++){ power += x->x_reverb_gs[i] * x->x_reverb_gs[i]; } power = sqrt(power); for(i = 0; i < x->x_ls_amount; i++){ final_gs[i] /= power; } } static void rvbap_bang(t_rvbap *x) // top level, vbap gains are calculated and outputted { t_atom at[MAX_LS_AMOUNT]; t_float g[3]; long ls[3]; long i; t_float *final_gs, overdist, oversqrtdist; final_gs = (t_float *) getbytes(x->x_ls_amount * sizeof(t_float)); #ifdef PD // avoid NaN explosions, MAX does this in rvbap_in4() && rvbap_ft4() if(x->x_dist < 1.0) {x->x_dist = 1.0;} #endif if(x->x_lsset_available == 1){ vbap(g, ls, x); for(i = 0; i < x->x_ls_amount; i++) final_gs[i] = 0.0; for(i = 0; i < x->x_dimension; i++){ final_gs[ls[i]-1] = g[i]; } if(x->x_spread != 0){ spread_it(x, final_gs); } overdist = 1 / x->x_dist; oversqrtdist = 1 / sqrt(x->x_dist); // build output for every loudspeaker for(i = 0; i < x->x_ls_amount; i++){ // first, we output the gains for the direct (unreverberated) signals // these just decrease as the distance increases #ifdef MAXMSP SETLONG(&at[0], i); SETFLOAT(&at[1], (final_gs[i] / x->x_dist)); outlet_list(x->x_outlet0, NULL, 2, at); #endif #ifdef PD SETFLOAT(&at[0], i); SETFLOAT(&at[1], (final_gs[i] / x->x_dist)); outlet_list(x->x_outlet0, gensym("list"), 2, at); #endif // second, we output the gains for the reverberated signals // these are made up of a global (all speakers) and a local part #ifdef MAXMSP SETLONG(&at[0], i+x->x_ls_amount); // direct signals come first in matrix~ SETFLOAT(&at[1], (((oversqrtdist / x->x_dist) * x->x_reverb_gs[i]) + (oversqrtdist * (1 - overdist) * final_gs[i]))); outlet_list(x->x_outlet0, NULL, 2, at); #endif #ifdef PD SETFLOAT(&at[0], (i+x->x_ls_amount)); // direct signals come first in matrix~ SETFLOAT(&at[1], (((oversqrtdist / x->x_dist) * x->x_reverb_gs[i]) + (oversqrtdist * (1 - overdist) * final_gs[i]))); outlet_list(x->x_outlet0, gensym("list"), 2, at); #endif } #ifdef MAXMSP outlet_int(x->x_outlet1, x->x_azi); outlet_int(x->x_outlet2, x->x_ele); outlet_int(x->x_outlet3, x->x_spread); outlet_float(x->x_outlet4, (double)x->x_dist); #endif #ifdef PD outlet_float(x->x_outlet1, x->x_azi); outlet_float(x->x_outlet2, x->x_ele); outlet_float(x->x_outlet3, x->x_spread); outlet_float(x->x_outlet4, x->x_dist); #endif } else post("rvbap: Configure loudspeakers first!"); freebytes(final_gs, x->x_ls_amount * sizeof(t_float)); // bug fix added 9/00 } /*--------------------------------------------------------------------------*/ static void rvbap_matrix(t_rvbap *x, t_symbol *s, int ac, t_atom *av) // read in loudspeaker matrices // and calculate the gains for the equally distributed // reverb signal part (i.e. global reverb) { long counter = 0; long datapointer = 0; long setpointer = 0; long i; long azi = x->x_azi, ele = x->x_ele; // store original values t_float g[3]; long ls[3]; (void)s; if(ac > 0) #ifdef MAXMSP if(av[datapointer].a_type == A_LONG){ x->x_dimension = av[datapointer++].a_w.w_long; x->x_lsset_available = 1; } else #endif { if(av[datapointer].a_type == A_FLOAT){ x->x_dimension = (long)av[datapointer++].a_w.w_float; x->x_lsset_available = 1; } else { post("Error in loudspeaker data!"); x->x_lsset_available = 0; return; } //post("%d",deb++); } if(ac > 1) #ifdef MAXMSP if(av[datapointer].a_type == A_LONG) x->x_ls_amount = av[datapointer++].a_w.w_long; else #endif if(av[datapointer].a_type == A_FLOAT) x->x_ls_amount = (long) av[datapointer++].a_w.w_float; else { post("rvbap: Error in loudspeaker data!"); x->x_lsset_available = 0; return; } else x->x_lsset_available = 0; if(x->x_dimension == 3) counter = (ac - 2) / ((x->x_dimension * x->x_dimension*2) + x->x_dimension); if(x->x_dimension == 2) counter = (ac - 2) / ((x->x_dimension * x->x_dimension) + x->x_dimension); x->x_lsset_amount = counter; if(counter <= 0){ post("rvbap: Error in loudspeaker data!"); x->x_lsset_available = 0; return; } while(counter-- > 0){ for(i = 0; i < x->x_dimension; i++){ #ifdef MAXMSP if(av[datapointer].a_type == A_LONG) #endif #ifdef PD if(av[datapointer].a_type == A_FLOAT) #endif { x->x_lsset[setpointer][i] = (long)av[datapointer++].a_w.w_float; } else { post("rvbap: Error in loudspeaker data!"); x->x_lsset_available = 0; return; } } for(i = 0; i < x->x_dimension * x->x_dimension; i++){ if(av[datapointer].a_type == A_FLOAT){ x->x_set_inv_matx[setpointer][i] = av[datapointer++].a_w.w_float; } else { post("rvbap: Error in loudspeaker data!"); x->x_lsset_available = 0; return; } } if(x->x_dimension == 3){ for(i = 0; i < x->x_dimension * x->x_dimension; i++){ if(av[datapointer].a_type == A_FLOAT){ x->x_set_matx[setpointer][i] = av[datapointer++].a_w.w_float; } else { post("rvbap: Error in loudspeaker data!"); x->x_lsset_available = 0; return; } } } setpointer++; } // now configure static reverb correction values... x->x_azi = x->x_ele = 0; vbap(g, ls, x); for(i = 0; i < x->x_ls_amount; i++){ x->x_reverb_gs[i] = 0.0; } for(i = 0; i < x->x_dimension; i++){ x->x_reverb_gs[ls[i]-1] = g[i]; // post("reverb gs #%d = %f", i, x->x_reverb_gs[i]); } equal_reverb(x,x->x_reverb_gs); /* for(i=0; ix_ls_amount; i++){ // do this for every speaker post("reverb gs #%d = %f", i, x->x_reverb_gs[i]); } */ post("rvbap: Loudspeaker setup configured!"); x->x_azi = azi; // restore original panning directions x->x_ele = ele; } #ifdef MAXMSP static void rvbap_in1(t_rvbap *x, long n) /* x = the instance of the object, n = the int received in the right inlet */ // panning angle azimuth { x->x_azi = n; /* store n in a global variable */ } static void rvbap_in2(t_rvbap *x, long n) /* x = the instance of the object, n = the int received in the right inlet */ // panning angle elevation { x->x_ele = n; /* store n in a global variable */ } /*--------------------------------------------------------------------------*/ static void rvbap_in3(t_rvbap *x, long n) /* x = the instance of the object, n = the int received in the right inlet */ // spread amount { if(n < 0) n = 0; if(n > 100) n = 100; x->x_spread = n; /* store n in a global variable */ } /*--------------------------------------------------------------------------*/ static void rvbap_in4(t_rvbap *x, long n) /* x = the instance of the object, n = the int received in the right inlet */ // distance { if(n < 1) n = 1; x->x_dist = (t_float)n; /* store n in a global variable */ } static void rvbap_ft1(t_rvbap *x, double n) /* x = the instance of the object, n = the int received in the right inlet */ // panning angle azimuth { x->x_azi = (long)n; /* store n in a global variable */ } static void rvbap_ft2(t_rvbap *x, double n) /* x = the instance of the object, n = the int received in the right inlet */ // panning angle elevation { x->x_ele = (long)n; /* store n in a global variable */ } /*--------------------------------------------------------------------------*/ static void rvbap_ft3(t_rvbap *x, double n) /* x = the instance of the object, n = the int received in the right inlet */ // spreading { if(n < 0.0) n = 0.0; if(n > 100.0) n = 100.0; x->x_spread = (long) n; /* store n in a global variable */ } /*--------------------------------------------------------------------------*/ static void rvbap_ft4(t_rvbap *x, double n) /* x = the instance of the object, n = the int received in the right inlet */ // distance { if(n < 1.0) n = 1.0; x->x_dist = (t_float)n; /* store n in a global variable */ } #endif // MAXMSP static void *rvbap_new(t_symbol *s, int ac, t_atom *av) /* create new instance of object... MUST send it an int even if you do nothing with this int!! */ { t_rvbap *x; (void)s; #ifdef MAXMSP x = (t_rvbap *)newobject(rvbap_class); t_floatin(x, 4); /* takes the distance */ intin(x, 3); intin(x, 2); /* create a second (int) inlet... remember right-to-left ordering in Max */ intin(x, 1); /* create a second (int) inlet... remember right-to-left ordering in Max */ x->x_outlet4 = floatout(x); /* distance */ x->x_outlet3 = intout(x); x->x_outlet2 = intout(x); /* create an (int) outlet - rightmost outlet first... */ x->x_outlet1 = intout(x); /* create an (int) outlet */ x->x_outlet0 = listout(x); /* create a (list) outlet */ #endif #ifdef PD x = (t_rvbap *)pd_new(rvbap_class); floatinlet_new(&x->x_ob, &x->x_azi); floatinlet_new(&x->x_ob, &x->x_ele); floatinlet_new(&x->x_ob, &x->x_spread); floatinlet_new(&x->x_ob, &x->x_dist); x->x_outlet0 = outlet_new(&x->x_ob, gensym("list")); x->x_outlet1 = outlet_new(&x->x_ob, gensym("float")); x->x_outlet2 = outlet_new(&x->x_ob, gensym("float")); x->x_outlet3 = outlet_new(&x->x_ob, gensym("float")); x->x_outlet4 = outlet_new(&x->x_ob, gensym("float")); #endif x->x_azi = 0; x->x_ele = 0; x->x_dist = 1.0; x->x_spread_base[0] = 0.0; x->x_spread_base[1] = 1.0; x->x_spread_base[2] = 0.0; x->x_spread = 0; x->x_lsset_available = 0; if(ac > 0){ #ifdef MAXMSP if(av[0].a_type == A_LONG) x->x_azi = av[0].a_w.w_long; else #endif if(av[0].a_type == A_FLOAT) x->x_azi = av[0].a_w.w_float; } if(ac > 1){ #ifdef MAXMSP if(av[1].a_type == A_LONG) x->x_ele = av[1].a_w.w_long; else #endif if(av[1].a_type == A_FLOAT) x->x_ele = av[1].a_w.w_float; } if(ac > 2){ #ifdef MAXMSP if(av[2].a_type == A_LONG) x->x_dist = (float)av[2].a_w.w_long; else #endif if(av[2].a_type == A_FLOAT) x->x_dist = av[2].a_w.w_float; } return(x); /* return a reference to the object instance */ } vbap-1.2.0/unittests/000077500000000000000000000000001417256401100145055ustar00rootroot00000000000000vbap-1.2.0/unittests/define-loudspeaker-unittest.wav000066400000000000000000000040541417256401100226520ustar00rootroot00000000000000RIFF$WAVEfmt D¬± data@€@€?@@󵿀?€?ô5?ó5¿€?@@@€?€¿€óµ?€?ô5?ó5?@€@Ie4ñµ?ýÿ¿ÿÿ?ô5?ó5?€¿i!"4€@€?€¿€¿†Ie´õµ¿€¿i!"4ô5?ó5¿@@€@€?@@@ó5?ó5¿ó5¿ó5?ó5?ó5¿€€óµ?ó5?ó5?ó5?ó5¿ó5?ó5?€?@@€@cÀ„3òµ¿cÀ„³ò5?ò5?ó5?ò5¿ò5¿ò5?ó5?ó5?ó5¿ó5¿cÀ„³ó5?ó5?@@@€@dÀ„³ôµ?dÀ„3ô5?ô5¿ô5?ô5¿ô5?ô5?ó5?ó5?ó5¿ó5?cÀ„³ó5?ó5?vbap-1.2.0/unittests/define_loudspeakers-unittest.pd000066400000000000000000000032561417256401100227300ustar00rootroot00000000000000#N canvas 177 32 793 647 10; #X text 19 210 In two dimensions \, only specify the azimuth. (for example "define_loudspeakers 2 -45 45 0 180"; #X text 20 80 [define_loudspeakers] works with; #X obj 252 80 vbap; #X text 15 111 Use [define_loudspeakers] to list the speaker positions. The example here defines loudspeakers in three dimensions (the first parameter). For each speaker \, define its azimuth and elevation. Here we have speakers front left and right with no elevation (-45 0 45 0) and front and back with 45 degrees of elevation (0 45 180 45). Send the data to:; #X msg 428 221 ls-directions 3 -30 0 30 0 -90 0 90 0 0 0 -150 0 150 0 180 45 -45 45 45 45; #X msg 411 158 ls-triplets 1 2 7 2 7 8 1 3 7; #X text 431 113 If you are not happy with triangle selections of define_loudspeakers \, you can define your own. (for experts only); #X text 427 268 2-D setup -> only loudspeaker azimuths specified:; #X text 426 283 (2 azi1 azi2 azi3 azi4...); #X text 425 184 3-D setup -> (azimuth elevation) pairs are specified: ; #X text 423 202 (3 azi1 ele1 azi2 ele2 ...); #X text 637 47 a 2D setup; #X text 679 77 a 3D setup; #X obj 56 355 testtools/unit-test-frame; #X obj 377 14 t b b b b; #X msg 56 322 define-loudspeaker-unittest; #X obj 56 296 loadbang; #X obj 377 79 vbap/define_loudspeakers 3 -45 0 45 0 0 45 180 45; #X obj 394 49 vbap/define_loudspeakers 2 -45 45 0 180; #X obj 411 303 vbap/define_loudspeakers 2 -40 0 40 80 130 180 -130 -80; #X connect 4 0 19 0; #X connect 5 0 19 0; #X connect 13 3 14 0; #X connect 14 0 17 0; #X connect 14 1 18 0; #X connect 14 2 5 0; #X connect 14 3 4 0; #X connect 15 0 13 0; #X connect 16 0 15 0; #X connect 17 0 13 1; #X connect 18 0 13 1; #X connect 19 0 13 1; vbap-1.2.0/unittests/rvbap-unittest.pd000066400000000000000000000035501417256401100200240ustar00rootroot00000000000000#N canvas 90 51 726 609 10; #X obj 38 320 testtools/unit-test-frame; #X text 383 141 azimuth \, elevation \, spread and distance; #N canvas 0 22 450 300 tba 0; #X obj 143 51 inlet; #X obj 96 49 inlet; #X obj 191 51 inlet; #X obj 238 51 inlet; #X obj 173 180 outlet; #X obj 61 178 outlet; #X obj 221 180 outlet; #X obj 268 180 outlet; #X obj 126 180 outlet; #X obj 96 73 t b a; #X obj 143 72 t b a; #X obj 191 72 t b a; #X obj 238 72 t b a; #X connect 0 0 10 0; #X connect 1 0 9 0; #X connect 2 0 11 0; #X connect 3 0 12 0; #X connect 9 0 5 0; #X connect 9 1 8 0; #X connect 10 0 5 0; #X connect 10 1 4 0; #X connect 11 0 5 0; #X connect 11 1 6 0; #X connect 12 0 5 0; #X connect 12 1 7 0; #X restore 407 169 pd tba; #X obj 342 16 t b b b b; #X obj 446 95 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 -1; #X msg 407 125 60; #X msg 446 125 15; #X msg 524 124 20; #X obj 38 262 loadbang; #X obj 393 70 vbap/define_loudspeakers 3 -45 0 45 0 0 45 180 45; #X obj 359 203 vbap/rvbap 0 0; #X msg 485 125 0; #X floatatom 379 298 5 0 0 0 - - -; #X floatatom 399 275 5 0 0 0 - - -; #X floatatom 419 254 5 0 0 0 - - -; #X floatatom 440 230 5 0 0 0 - - -; #X obj 359 46 vbap/define_loudspeakers 3 -45 45 45 45 0 0 180 0; #X text 15 36 In Pd-extended 0.42 \, [rvbap] crashes when using 2 dimensional setup.; #X msg 38 291 rvbap-unittest; #X connect 0 3 3 0; #X connect 2 0 10 0; #X connect 2 1 10 1; #X connect 2 2 10 2; #X connect 2 3 10 3; #X connect 2 4 10 4; #X connect 3 0 4 0; #X connect 3 1 16 0; #X connect 3 2 4 0; #X connect 3 3 9 0; #X connect 4 0 5 0; #X connect 4 0 6 0; #X connect 4 0 11 0; #X connect 4 0 7 0; #X connect 5 0 2 0; #X connect 6 0 2 1; #X connect 7 0 2 3; #X connect 8 0 18 0; #X connect 9 0 10 0; #X connect 10 0 0 1; #X connect 10 1 12 0; #X connect 10 2 13 0; #X connect 10 3 14 0; #X connect 10 4 15 0; #X connect 11 0 2 2; #X connect 16 0 10 0; #X connect 18 0 0 0; vbap-1.2.0/unittests/rvbap-unittest.wav000066400000000000000000000040541417256401100202160ustar00rootroot00000000000000RIFF$WAVEfmt D¬± data÷Ù8€@ÿÿÿ>€?€? @?@À@ýÿÿ>@@à@?€@ÿÿÿ>€?ëFw? @?@«g;>À@ýÿÿ>@@³g;>à@?€@ÿÿÿ>€?ëFw? @?@«g;>À@ýÿÿ>@@³g;>à@?€@½-·;€?VÒE= @JÖW>@‰ì<À@ì"6=@@ì<à@ó"6=7Þ6€@èÙP;€?3yD= @qìS>@®6g<À@ñ†=@@à@ÙÀ <²6€@ÛQ;€?= @°#>@QJ =À@Ô>@@à@ÙÀ <Šœr9€@6û‡;€?3>= @4Â!>@¼> =À@W“>@@à@ÙÀ <Šœr9€@6û‡;€?3>= @4Â!>@¼> =À@W“>@@à@ÙÀ <vbap-1.2.0/unittests/vbap-unittest.pd000066400000000000000000000111261417256401100176400ustar00rootroot00000000000000#N canvas 225 22 809 680 10; #X text 21 122 In two dimensions \, only specify the azimuth. (for example "define_loudspeakers 2 -45 45 0 180"; #X text 417 17 VBAP and define_loudspeakers; #X text 22 166 2 For vbap \, give azimuth and elevation for the desired location. Bang the first inlet and vbap will output gain-factors for each speaker and the actual location produced. This can be different from the desired one depending where your speakers are.; #X text 418 46 loudspeaker position definition can also be sent directly to a vbap object. This changes the setup information only in a single vbap object.; #X text 419 94 For an example of how to use vbap with [matrix~] from zexy-library \, see vbap-demo.pd.; #X text 21 22 1 Use the define_loudspeakers object or message to list the speaker positions. The example here defines loudspeakers in three dimensions (the first parameter). For each speaker \, define its azimuth and elevation. Here we have speakers front left and right with no elevation (-45 0 45 0) and front and back with 45 degrees of elevation (0 45 180 45). Send the data to vbap.; #N canvas 283 22 628 620 simple 0; #X floatatom 159 198 5 0 0 2 - - -; #X floatatom 112 425 10 0 0 0 - - -; #X floatatom 181 425 10 0 0 0 - - -; #X floatatom 209 349 0 0 0 3 actual_azimuth - -; #X floatatom 221 315 0 0 0 3 actual_elevation - -; #X floatatom 231 294 7 0 0 1 actual_spreading - -; #X obj 162 182 hsl 100 13 -180 180 0 1 empty empty azimuth 10 7 0 10 -262144 -1 -1 1500 1; #X floatatom 237 237 5 0 0 2 - - -; #X obj 240 220 hsl 100 13 0 100 0 1 empty empty spread 10 7 0 10 -262144 -1 -1 700 1; #X text 130 134 to output updated values; #X obj 112 155 metro 100; #X obj 112 133 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X text 197 200 (ignore elevation in 2D \, that's for 3D); #X obj 63 524 *~; #X obj 92 525 *~; #X msg 454 145 dsp 1; #X obj 454 169 send pd; #X floatatom 250 425 10 0 0 0 - - -; #X floatatom 319 425 10 0 0 0 - - -; #X obj 123 524 *~; #X obj 152 525 *~; #X msg 17 48 define_loudspeakers 2 -135 -45 0 45 135; #X text 263 37 Define 2D speaker arrangement with speakers at -135 \, -45 \, 0 \, 45 and 135 degree positions (0 degrees is the center front). This is a classic 5.1 setup.; #X floatatom 389 425 10 0 0 0 - - -; #X obj 72 558 dac~ 1 2 3 4 5; #X obj 182 525 *~; #X obj 112 267 vbap 0 0 30; #X obj 112 376 route 0 1 2 3 4; #X obj 13 396 *~ 0.2; #X obj 13 374 noise~; #X connect 1 0 13 1; #X connect 2 0 14 1; #X connect 6 0 0 0; #X connect 8 0 7 0; #X connect 10 0 26 0; #X connect 11 0 10 0; #X connect 13 0 24 0; #X connect 14 0 24 1; #X connect 15 0 16 0; #X connect 17 0 19 1; #X connect 18 0 20 1; #X connect 19 0 24 2; #X connect 20 0 24 3; #X connect 21 0 26 0; #X connect 23 0 25 1; #X connect 25 0 24 4; #X connect 26 0 27 0; #X connect 27 0 1 0; #X connect 27 1 2 0; #X connect 27 2 17 0; #X connect 27 3 18 0; #X connect 27 4 23 0; #X connect 28 0 13 0; #X connect 28 0 14 0; #X connect 28 0 19 0; #X connect 28 0 20 0; #X connect 28 0 25 0; #X connect 29 0 28 0; #X restore 421 139 pd simple 5.1 example; #X obj 421 277 vbap/vbap 90 0 30; #X obj 100 371 testtools/unit-test-frame; #X msg 100 342 vbap; #X floatatom 454 342 5 0 0 0 - - -; #X floatatom 487 322 5 0 0 0 - - -; #X floatatom 520 300 5 0 0 0 - - -; #X text 22 244 The spread-parameter can be used to prevent a situation where sound is coming from one speaker only \, which would make speaker positions "visible". The range is 0 to 100; #X text 496 344 azimuth; #X text 524 324 elevation; #X text 556 300 spread; #X obj 100 315 loadbang; #N canvas 0 22 504 332 define-loudspeakers 0; #X msg 51 87 define_loudspeakers 2 -135 -45 0 45 135; #X msg 106 121 define_loudspeakers 3 -45 0 45 0 0 45 180 45; #X obj 51 21 inlet; #X obj 51 284 outlet; #X obj 188 283 outlet; #X obj 106 148 t b a; #X obj 51 112 t b a; #X obj 51 53 t b b b b; #X obj 161 199 t b a; #X obj 208 245 t b a; #X msg 161 172 define_loudspeakers 3 -30 0 30 0 0 45 180 45; #X msg 207 217 define_loudspeakers 3 -45 0 45 0 0 30 180 30; #X connect 0 0 6 0; #X connect 1 0 5 0; #X connect 2 0 7 0; #X connect 5 0 3 0; #X connect 5 1 4 0; #X connect 6 0 3 0; #X connect 6 1 4 0; #X connect 7 0 0 0; #X connect 7 1 1 0; #X connect 7 2 10 0; #X connect 7 3 11 0; #X connect 8 0 3 0; #X connect 8 1 4 0; #X connect 9 0 3 0; #X connect 9 1 4 0; #X connect 10 0 8 0; #X connect 11 0 9 0; #X restore 421 237 pd define-loudspeakers; #X obj 421 208 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 -1; #X connect 7 0 8 1; #X connect 7 1 10 0; #X connect 7 2 11 0; #X connect 7 3 12 0; #X connect 8 3 19 0; #X connect 9 0 8 0; #X connect 17 0 9 0; #X connect 18 0 7 0; #X connect 18 1 7 0; #X connect 19 0 18 0; vbap-1.2.0/unittests/vbap-unittest.wav000066400000000000000000000040541417256401100200340ustar00rootroot00000000000000RIFF$WAVEfmt D¬± data.OÈ=€?l¸n?@@@ײ>pB4BY—Ö>€?Â'K?@¨õ©>@@ !!>€@”z>ñxB3±A4B.OÈ=€?l¸n?@@@ײ>pB4Bvbap-1.2.0/unittests/vbap.wav000066400000000000000000000040541417256401100161570ustar00rootroot00000000000000RIFF$WAVEfmt D¬± data€?ö¸]?@åv>@@oà>€?ö¸]?@åv>@@oà>€?«3R?@iı>@@¼ôç>€?«3R?@iı>@@¼ôç>‡ï<€?Á£V?@fIã>@@Ä.¡>‡ï<€?Á£V?@fIã>@@Ä.¡>€?@…m>@@71v?€@ïU>€?@…m>@@71v?€@ïU>vbap-1.2.0/vbap-help.pd000066400000000000000000000131151417256401100146470ustar00rootroot00000000000000#N canvas 477 23 871 531 10; #X floatatom 149 218 5 0 0 2 - - -, f 5; #X floatatom 187 237 5 0 0 2 - - -, f 5; #X msg 115 163 bang; #X obj 102 432 route 0 1 2 3; #X floatatom 102 465 10 0 0 0 - - -, f 10; #X floatatom 181 465 10 0 0 0 - - -, f 10; #X floatatom 260 465 10 0 0 0 - - -, f 10; #X floatatom 339 465 10 0 0 0 - - -, f 10; #X text 26 26 VBAP and define_loudspeakers; #X floatatom 119 396 0 0 0 3 actual_azimuth - -; #X floatatom 136 361 0 0 0 3 actual_elevation - -; #X floatatom 153 338 7 0 0 1 actual_spread - -, f 7; #X msg 25 115 define_loudspeakers 2 -45 45 -135 135; #X msg 51 135 define_loudspeakers 3 -45 45 -135 135 32 5 94 34 34 11 ; #X obj 152 202 hsl 100 13 0 360 0 0 empty empty azimuth 10 7 0 10 -262144 -1 -1 0 1; #X obj 190 220 hsl 100 13 0 360 0 0 empty empty elevation 10 7 0 10 -262144 -1 -1 0 1; #X floatatom 227 257 5 0 0 2 - - -, f 5; #X obj 230 240 hsl 100 13 0 100 0 0 empty empty spread 10 7 0 10 -262144 -1 -1 0 1; #X text 149 163 to output updated values; #X obj 102 287 vbap 90 0 30; #X obj 83 185 metro 100; #X obj 83 163 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #N canvas 283 23 519 588 simple 0; #X floatatom 133 183 5 0 0 2 - - -, f 5; #X floatatom 112 445 10 0 0 0 - - -, f 10; #X floatatom 181 445 10 0 0 0 - - -, f 10; #X floatatom 127 371 0 0 0 3 actual_azimuth - -; #X floatatom 143 335 0 0 0 3 actual_elevation - -; #X floatatom 159 306 7 0 0 1 actual_spread - -, f 7; #X obj 136 167 hsl 100 13 -180 180 0 1 empty empty azimuth 10 7 0 10 -262144 -1 -1 9900 1; #X floatatom 175 231 5 0 0 2 - - -, f 5; #X obj 178 214 hsl 100 13 0 100 0 1 empty empty spread 10 7 0 10 -262144 -1 -1 700 1; #X text 130 113 to output updated values; #X obj 112 134 metro 100; #X obj 112 112 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X text 171 185 (ignore elevation in 2D \, that's for 3D); #X obj 97 524 *~; #X obj 126 525 *~; #X msg 454 145 dsp 1; #X obj 454 169 send pd; #X floatatom 250 445 10 0 0 0 - - -, f 10; #X floatatom 319 445 10 0 0 0 - - -, f 10; #X obj 157 524 *~; #X obj 186 525 *~; #X msg 38 68 define_loudspeakers 2 -135 -45 0 45 135; #X text 32 17 Define 2D speaker arrangement with speakers at -135 \, -45 \, 0 \, 45 and 135 degree positions (0 degrees is the center front). This is a classic 5.1 setup.; #X floatatom 389 445 10 0 0 0 - - -, f 10; #X obj 97 556 dac~ 1 2 3 4 5; #X obj 216 525 *~; #X obj 112 267 vbap 0 0 30; #X obj 112 411 route 0 1 2 3 4; #X obj 22 442 *~ 0.2; #X obj 22 420 noise~; #X obj 308 305 route current; #X floatatom 308 332 5 0 0 1 current_set - -, f 5; #X connect 0 0 26 1; #X connect 1 0 13 1; #X connect 2 0 14 1; #X connect 6 0 0 0; #X connect 7 0 26 3; #X connect 8 0 7 0; #X connect 10 0 26 0; #X connect 11 0 10 0; #X connect 13 0 24 0; #X connect 14 0 24 1; #X connect 15 0 16 0; #X connect 17 0 19 1; #X connect 18 0 20 1; #X connect 19 0 24 2; #X connect 20 0 24 3; #X connect 21 0 26 0; #X connect 23 0 25 1; #X connect 25 0 24 4; #X connect 26 0 27 0; #X connect 26 1 3 0; #X connect 26 2 4 0; #X connect 26 3 5 0; #X connect 26 4 30 0; #X connect 27 0 1 0; #X connect 27 1 2 0; #X connect 27 2 17 0; #X connect 27 3 18 0; #X connect 27 4 23 0; #X connect 28 0 13 0; #X connect 28 0 14 0; #X connect 28 0 19 0; #X connect 28 0 20 0; #X connect 28 0 25 0; #X connect 29 0 28 0; #X connect 30 0 31 0; #X restore 572 457 pd simple 5.1 example; #X text 337 238 (0-100); #X text 297 217 (0-360); #X text 257 199 (0-360); #X text 21 490 Vector Base Amplitude Panning; #X text 241 489 (C) 1998-2003 \, 2006 Ville Pulkki; #X obj 299 340 route current; #X obj 374 363 print; #X floatatom 299 382 5 0 0 1 current_set - -, f 5; #X text 477 488 You can also use [vbap] to control: cyclone/matrix~ ; #X text 476 152 In two dimensions \, only specify the azimuth. For example: "define_loudspeakers 2 -45 45 0 180"; #X text 24 62 THe loudspeaker position definition can also be sent directly to a vbap object. This changes the setup information only in a single vbap object.; #X text 476 406 For an example of how to use vbap with iemmatrix/matrix~ see: examples/vbap-demo.pd; #X text 476 320 The spread-parameter can be used to prevent a situation where sound is coming from one speaker only \, which would make speaker positions "visible". The range is 0 to 100; #X text 476 366 As location changes \, the current speaker set id is output from the right-most outlet: "current setid"; #X text 476 191 When loaded \, the defined speaker set ids are output from the right-most outlet as lists: "set setid speakerid speakerid speakerid...".; #X text 298 314 loudspeaker set info; #X text 476 59 1) Use the define_loudspeakers object or message to list the speaker positions. The example here defines loudspeakers in three dimensions (the first parameter). For each speaker \, define its azimuth and elevation. Here we have speakers front left and right with no elevation (-45 0 45 0) and front and back with 45 degrees of elevation (0 45 180 45). Send the data to vbap.; #X text 476 249 2) For vbap \, give azimuth and elevation for the desired location. Bang the first inlet and vbap will output gain-factors for each speaker and the actual location produced. This can be different from the desired one depending where your speakers are.; #X connect 0 0 19 1; #X connect 1 0 19 2; #X connect 2 0 19 0; #X connect 3 0 4 0; #X connect 3 1 5 0; #X connect 3 2 6 0; #X connect 3 3 7 0; #X connect 12 0 19 0; #X connect 13 0 19 0; #X connect 14 0 0 0; #X connect 15 0 1 0; #X connect 16 0 19 3; #X connect 17 0 16 0; #X connect 19 0 3 0; #X connect 19 1 9 0; #X connect 19 2 10 0; #X connect 19 3 11 0; #X connect 19 4 28 0; #X connect 20 0 19 0; #X connect 21 0 20 0; #X connect 28 0 30 0; #X connect 28 1 29 0; vbap-1.2.0/vbap-meta.pd000066400000000000000000000003761417256401100146520ustar00rootroot00000000000000#N canvas 140 23 200 200 10; #N canvas 20 23 420 300 META 0; #X text 10 30 NAME vbap; #X text 10 50 AUTHOR Ville Pulkki; #X text 10 70 DESCRIPTION spatialization using Vector Based Amplitude Panning; #X text 10 107 VERSION 1.2; #X restore 10 10 pd META; vbap-1.2.0/vbap.c000066400000000000000000000652341417256401100135510ustar00rootroot00000000000000/* vbap.c written by Ville Pulkki 1999-2003 Helsinki University of Technology and University of California at Berkeley See copyright in file with name LICENSE.txt */ // Indicate that we are within VBAP object (specific to include define_loudspeakers content within vbap) #define VBAP_OBJECT #include "vbap.h" #include "s_stuff.h" // Function prototypes static void new_spread_dir(t_vbap *x, t_float spreaddir[3], t_float vscartdir[3], t_float spread_base[3]); static void new_spread_base(t_vbap *x, t_float spreaddir[3], t_float vscartdir[3]); static void *vbap_class; static void vect_cross_prod(t_float v1[3], t_float v2[3],t_float v3[3]); static void additive_vbap(t_float *final_gs, t_float cartdir[3], t_vbap *x); static void vbap_bang(t_vbap *x); static int vbap_getmem(t_vbap *x, int lsSetCount ); static void vbap_free(t_vbap *x); static void vbap_matrix(t_vbap *x, Symbol *s, int ac, Atom *av); #ifndef PD /* Max */ /* these are for getting data from a cold inlet on Max/MSP, in Pd you use t_floatinlet_new() in new() */ void vbap_ft1(t_vbap *x, double n); void vbap_ft2(t_vbap *x, double n); void vbap_in3(t_vbap *x, long n); void vbap_ft4(t_vbap *x, double g); #endif static void spread_it(t_vbap *x, t_float *final_gs); static void *vbap_new(t_float azi, t_float ele, t_float spread); static void vbap(t_float g[3], long ls[3], t_vbap *x); static void angle_to_cart(t_float azi, t_float ele, t_float res[3]); static void cart_to_angle(t_float cvec[3], t_float avec[3]); /***************************************************** INCLUDE ALL define_loudspeakers functions directly into VBAP ******************************************************/ #include "define_loudspeakers.c" /***************************************************** Max Object Assist ******************************************************/ #ifndef PD /* Max */ void vbap_assist(t_vbap *x, void *b, long m, long a, char *s) { char *mess = "unknown"; if(m == ASSIST_INLET) { switch(a) { case 0 : mess = "bang to calc and output vbap gains. loudspeakers definition"; break; case 1 : mess = "panning angle azimuth"; break; case 2 : mess = "panning angle elevation"; break; case 3 : mess = "spread amount"; break; case 4 : mess = "gain control"; break; } } else { switch(a) { case 0 : mess = "vbap gains"; break; case 1 : mess = "panning angle azimuth"; break; case 2 : mess = "panning angle elevation"; break; case 3 : mess = "spread amount"; break; case 4 : mess = "gain control"; break; } } sprintf(s,mess); } #endif /* Max */ /* above are the prototypes for the methods/procedures/functions you will use */ /*--------------------------------------------------------------------------*/ #ifdef PD void vbap_setup(void) { vbap_class = class_new(gensym("vbap"), (t_newmethod)vbap_new, (t_method) vbap_free, (short)sizeof(t_vbap), 0, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0); class_addbang(vbap_class, (t_method)vbap_bang); /* these are for getting data from a cold inlet on Max/MSP, in Pd you use floatinlet_new() in new() addftx((t_method)vbap_ft1, 1); addftx((t_method)vbap_ft2, 2); addftx((t_method)vbap_in3, 3); addftx((t_method)vbap_ft4, 4); */ class_addmethod(vbap_class, (t_method)vbap_matrix, gensym("loudspeaker-matrices"), A_GIMME, 0); // define_loudspeaker messages class_addmethod(vbap_class, (t_method)vbap_def_ls, gensym("define-loudspeakers"), A_GIMME, 0); class_addmethod(vbap_class, (t_method)vbap_def_ls, gensym("define_loudspeakers"), A_GIMME, 0); class_addmethod(vbap_class, (t_method)def_ls_read_directions, gensym("ls-directions"), A_GIMME, 0); class_addmethod(vbap_class, (t_method)def_ls_read_triplets, gensym("ls-triplets"), A_GIMME, 0); post(VBAP_VERSION); } #else /* MAX */ void main(void) { setup((t_messlist **)&vbap_class, (method)vbap_new, 0L, (short)sizeof(t_vbap), 0L, A_DEFLONG,A_DEFLONG,A_DEFLONG, 0); addbang((method)vbap_bang); addftx((method)vbap_ft1, 1); addftx((method)vbap_ft2, 2); addftx((method)vbap_in3, 3); addftx((method)vbap_ft4, 4); addmess((method)vbap_matrix, "loudspeaker-matrices", A_GIMME, 0); addmess((method)traces, "enabletrace", A_LONG, 0); // define_loudspeaker messages addmess((method)vbap_def_ls, "define-loudspeakers", A_GIMME, 0); addmess((method)vbap_def_ls, "define_loudspeakers", A_GIMME, 0); addmess((method)def_ls_read_directions, "ls-directions", A_GIMME, 0); addmess((method)def_ls_read_triplets, "ls-triplets", A_GIMME, 0); addmess((method)vbap_assist,"assist", A_CANT,0); post(VBAP_VERSION); } /* these are for getting data from a cold inlet on Max/MSP, in Pd you use floatinlet_new() in new() */ /*--------------------------------------------------------------------------*/ // panning angle azimuth void vbap_ft1(t_vbap *x, double n) { x->x_azi = (float) n; } /*--------------------------------------------------------------------------*/ // panning angle elevation void vbap_ft2(t_vbap *x, double n) { x->x_ele = (float) n; } /*--------------------------------------------------------------------------*/ // spread amount void vbap_in3(t_vbap *x, long n) { x->x_spread = (n < 0) ? 0 : (n > 100) ? 100 : n; } /*--------------------------------------------------------------------------*/ // gain control void vbap_ft4(t_vbap *x, double g) { x->x_gain = g; } #endif /* MAX */ /*--------------------------------------------------------------------------*/ // create new instance of object... static void *vbap_new(t_float azi, t_float ele, t_float spread) { #ifdef PD t_vbap *x = (t_vbap *)newobject(vbap_class); floatinlet_new(&x->x_obj, &x->x_azi); floatinlet_new(&x->x_obj, &x->x_ele); floatinlet_new(&x->x_obj, &x->x_spread); x->x_outlet0 = outlet_new(&x->x_obj, &s_float); x->x_outlet1 = outlet_new(&x->x_obj, &s_float); x->x_outlet2 = outlet_new(&x->x_obj, &s_float); x->x_outlet3 = outlet_new(&x->x_obj, &s_float); x->x_outlet4 = outlet_new(&x->x_obj, 0); // allocate space for the runtime matrices //if(!vbap_getmem(x, MAX_LS_SETS)) // return NULL; #else /* Max */ t_vbap *x = (t_vbap *)newobject(vbap_class); floatin(x, 4); floatin(x, 3); floatin(x, 2); floatin(x, 1); x->x_outlet4 = floatout(x); x->x_outlet3 = floatout(x); x->x_outlet2 = floatout(x); x->x_outlet1 = floatout(x); x->x_outlet0 = listout(x); #endif /* PD */ x->x_ls_setCount = 0; // refers to memory dynamically allocated when a define_loudspeakers config is received x->x_ls_set_current = 0; x->x_spread_base[0] = 0.0; x->x_spread_base[1] = 1.0; x->x_spread_base[2] = 0.0; x->x_lsset_available = 0; x->x_azi = azi; x->x_ele = ele; x->x_spread = spread; return x; /* return a reference to the object instance */ } // currently can allocate up to 256K to support up to 44 channels in 3D // note: to save memory, the required memory for a given configuration could instead, be dynamically allocated by calling this method from the vbap_matrix() method static int vbap_getmem(t_vbap *x, int lsSetCount) { #ifdef PD int i; if(x->x_ls_setCount) vbap_free(x); //was t_float x_set_inv_matx[MAX_LS_SETS][9]; x->x_set_inv_matx = getbytes(sizeof(t_float *) * lsSetCount); if(!x->x_set_inv_matx) {error("vbap_getmem: can't allocate additional %ld bytes", sizeof(t_float *) * lsSetCount); return 0;} for(i = 0; i < lsSetCount; i++) { x->x_set_inv_matx[i] = getbytes(sizeof(t_float) * MATRIX_DIM); if(!x->x_set_inv_matx[i]) {error("vbap_getmem: can't allocate additional %ld bytes", sizeof(t_float) * MATRIX_DIM); return 0;} } //was t_float x_set_matx[MAX_LS_SETS][9]; x->x_set_matx = getbytes(sizeof(t_float *) * lsSetCount); if(!x->x_set_matx) {error("vbap_getmem: can't allocate additional %ld bytes", sizeof(t_float *) * lsSetCount); return 0;} for(i = 0; i < lsSetCount; i++) { x->x_set_matx[i] = getbytes(sizeof(t_float) * MATRIX_DIM); if(!x->x_set_matx[i]) {error("vbap_getmem: can't allocate additional %ld bytes", sizeof(t_float) * MATRIX_DIM); return 0;} } //was long x_lsset[MAX_LS_SETS][3]; x->x_lsset = getbytes(sizeof(long *) * lsSetCount); if(!x->x_lsset) {error("vbap_getmem: can't allocate additional %ld bytes", sizeof(long *) * lsSetCount); return 0;} for(i = 0; i < lsSetCount; i++) { x->x_lsset[i] = getbytes(sizeof(long) * SPEAKER_SET_DIM); if(!x->x_lsset[i]) {error("vbap_getmem: can't allocate additional %ld bytes", sizeof(long) * SPEAKER_SET_DIM); return 0;} } unsigned long memallocd = 2 * (sizeof(t_float *) * lsSetCount * sizeof(t_float) * MATRIX_DIM) + (sizeof(long *) * lsSetCount * sizeof(long) * SPEAKER_SET_DIM); logpost(NULL, 3, "vbap_new: %ldK bytes allocated for instance", memallocd /1000); x->x_ls_setCount = lsSetCount; #endif return 1; } // free any allocated memory for instance static void vbap_free(t_vbap *x) { int i; if(!x->x_ls_setCount) return; for(i = 0; i < x->x_ls_setCount; i++) { freebytes(x->x_set_inv_matx[i], (sizeof(t_float) * MATRIX_DIM)); // = getbytes(sizeof(t_float) * MATRIX_DIM); freebytes(x->x_set_matx[i], sizeof(t_float) * MATRIX_DIM); } freebytes(x->x_set_inv_matx, (sizeof(t_float *) * x->x_ls_setCount)); freebytes(x->x_set_matx, sizeof(t_float *) * x->x_ls_setCount); for(i = 0; i < x->x_ls_setCount; i++) { freebytes(x->x_lsset[i], sizeof(long) * SPEAKER_SET_DIM); } freebytes(x->x_lsset, sizeof(long *) * x->x_ls_setCount); } static void angle_to_cart(t_float azi, t_float ele, t_float res[3]) // converts angular coordinates to cartesian { res[0] = cos(azi * atorad) * cos(ele * atorad); res[1] = sin(azi * atorad) * cos(ele * atorad); res[2] = sin(ele * atorad); } static void cart_to_angle(t_float cvec[3], t_float avec[3]) // converts cartesian coordinates to angular { //float tmp, tmp2, tmp3, tmp4; //float power; t_float dist, atan_y_per_x, atan_x_pl_y_per_z; t_float azi, ele; if(cvec[0] == 0.0) atan_y_per_x = M_PI / 2; else atan_y_per_x = atan(cvec[1] / cvec[0]); azi = atan_y_per_x / atorad; if(cvec[0] < 0.0) azi += 180.0; dist = sqrt(cvec[0]*cvec[0] + cvec[1]*cvec[1]); if(cvec[2] == 0.0) atan_x_pl_y_per_z = 0.0; else atan_x_pl_y_per_z = atan(cvec[2] / dist); if(dist == 0.0) { if(cvec[2] < 0.0) atan_x_pl_y_per_z = -M_PI/2.0; else atan_x_pl_y_per_z = M_PI/2.0; } ele = atan_x_pl_y_per_z / atorad; dist = sqrtf(cvec[0]*cvec[0] + cvec[1]*cvec[1] + cvec[2]*cvec[2]); avec[0] = azi; avec[1] = ele; avec[2] = dist; } static void vbap(t_float g[3], long ls[3], t_vbap *x) { /* calculates gain factors using loudspeaker setup and given direction */ t_float power; int i, j, k, gains_modified; t_float small_g; t_float big_sm_g, gtmp[3]; long winner_set = 0; t_float cartdir[3]; t_float new_cartdir[3]; t_float new_angle_dir[3]; long dim = x->x_dimension; long neg_g_am, best_neg_g_am; // transferring the azimuth angle to a decent value if(x->x_azi > 360.0 || x->x_azi < -360.0) x->x_azi = fmod(x->x_azi, 360.0); if(x->x_azi > 180.0) x->x_azi -= 360.0; if(x->x_azi < -179.0) x->x_azi += 360.0; // transferring the elevation to a decent value if(dim == 3) { if(x->x_ele > 360.0 || x->x_ele < -360.0) x->x_ele = fmod(x->x_ele, 360.0); if(x->x_ele > 180.0) x->x_ele -= 360.0; if(x->x_ele < -179.0) x->x_ele += 360.0; } else x->x_ele = 0.0; // go through all defined loudspeaker sets and find the set which // has all positive values. If such is not found, set with largest // minimum value is chosen. If at least one of gain factors of one LS set is negative // it means that the virtual source does not lie in that LS set. angle_to_cart(x->x_azi, x->x_ele, cartdir); big_sm_g = -100000.0; // initial value for largest minimum gain value best_neg_g_am = 3; // how many negative values in this set for(i = 0; i < x->x_lsset_amount; i++) { small_g = 10000000.0; neg_g_am = 3; for(j = 0; j < dim; j++) { gtmp[j] = 0.0; for(k = 0; k < dim; k++) gtmp[j] += cartdir[k]* x->x_set_inv_matx[i][k+j*dim]; if(gtmp[j] < small_g) small_g = gtmp[j]; if(gtmp[j] >= -0.01) neg_g_am--; } if(small_g > big_sm_g && neg_g_am <= best_neg_g_am) { big_sm_g = small_g; best_neg_g_am = neg_g_am; winner_set = i; g[0] = gtmp[0]; g[1] = gtmp[1]; ls[0] = x->x_lsset[i][0]; ls[1] = x->x_lsset[i][1]; if(dim == 3) { g[2] = gtmp[2]; ls[2] = x->x_lsset[i][2]; } else { g[2] = 0.0; ls[2] = 0; } } } // output new active loudspeaker set on a change if(winner_set != x->x_ls_set_current) { t_atom floatValue; SETFLOAT(&floatValue, winner_set); outlet_anything(x->x_outlet4, gensym("current"), 1, &floatValue); x->x_ls_set_current = winner_set; } // If chosen set produced a negative value, make it zero and // calculate direction that corresponds to these new // gain values. This happens when the virtual source is outside of // all loudspeaker sets. gains_modified = 0; for(i = 0; i < dim; i++) if(g[i] < -0.01){ g[i] = 0.0001; gains_modified = 1; } if(gains_modified == 1) { new_cartdir[0] = x->x_set_matx[winner_set][0] * g[0] + x->x_set_matx[winner_set][1] * g[1] + x->x_set_matx[winner_set][2] * g[2]; new_cartdir[1] = x->x_set_matx[winner_set][3] * g[0] + x->x_set_matx[winner_set][4] * g[1] + x->x_set_matx[winner_set][5] * g[2]; if(dim == 3) { new_cartdir[2] = x->x_set_matx[winner_set][6] * g[0] + x->x_set_matx[winner_set][7] * g[1] + x->x_set_matx[winner_set][8] * g[2]; } else new_cartdir[2] = 0; cart_to_angle(new_cartdir,new_angle_dir); x->x_azi = (new_angle_dir[0] ); x->x_ele = (new_angle_dir[1]); } power = sqrt(g[0]*g[0] + g[1]*g[1] + g[2]*g[2]); g[0] /= power; g[1] /= power; g[2] /= power; } static void vect_cross_prod(t_float v1[3], t_float v2[3], t_float v3[3]) // vector cross product { t_float length; v3[0] = (v1[1] * v2[2] ) - (v1[2] * v2[1]); v3[1] = (v1[2] * v2[0] ) - (v1[0] * v2[2]); v3[2] = (v1[0] * v2[1] ) - (v1[1] * v2[0]); length = sqrt(v3[0]*v3[0] + v3[1]*v3[1] + v3[2]*v3[2]); v3[0] /= length; v3[1] /= length; v3[2] /= length; } static void additive_vbap(t_float *final_gs, t_float cartdir[3], t_vbap *x) // calculates gains to be added to previous gains, used in // multiple direction panning (source spreading) { t_float power; int i, j, k, gains_modified; t_float small_g; t_float big_sm_g, gtmp[3]; long dim = x->x_dimension; long neg_g_am, best_neg_g_am; t_float g[3] = {0,0,0}; long ls[3] = {0,0,0}; big_sm_g = -100000.0; best_neg_g_am = 3; for(i = 0; i < x->x_lsset_amount; i++) { small_g = 10000000.0; neg_g_am = 3; for(j = 0; j < dim; j++) { gtmp[j] = 0.0; for(k = 0; k < dim; k++) gtmp[j] += cartdir[k] * x->x_set_inv_matx[i][k+j*dim]; if(gtmp[j] < small_g) small_g = gtmp[j]; if(gtmp[j] >= -0.01) neg_g_am--; } if(small_g > big_sm_g && neg_g_am <= best_neg_g_am) { big_sm_g = small_g; best_neg_g_am = neg_g_am; g[0] = gtmp[0]; g[1] = gtmp[1]; ls[0] = x->x_lsset[i][0]; ls[1] = x->x_lsset[i][1]; if(dim == 3) { g[2] = gtmp[2]; ls[2] = x->x_lsset[i][2]; } else { g[2] = 0.0; ls[2] = 0; } } } gains_modified = 0; for(i = 0; i < dim; i++) { if(g[i] < -0.01) { gains_modified = 1; } } if(gains_modified != 1){ power=sqrt(g[0]*g[0] + g[1]*g[1] + g[2]*g[2]); g[0] /= power; g[1] /= power; g[2] /= power; final_gs[ls[0]-1] += g[0]; final_gs[ls[1]-1] += g[1]; if(dim == 3) final_gs[ls[2]-1] += g[2]; } } static void new_spread_dir(t_vbap *x, t_float spreaddir[3], t_float vscartdir[3], t_float spread_base[3]) // subroutine for spreading { t_float beta, gamma; t_float a, b; t_float power; gamma = acos(vscartdir[0] * spread_base[0] + vscartdir[1] * spread_base[1] + vscartdir[2] * spread_base[2]) / M_PI * 180; if(fabs(gamma) < 1){ angle_to_cart(x->x_azi+90.0, 0, spread_base); gamma = acos(vscartdir[0] * spread_base[0] + vscartdir[1] * spread_base[1] + vscartdir[2] * spread_base[2]) / M_PI * 180; } beta = 180 - gamma; b = sin(x->x_spread * M_PI / 180) / sin(beta * M_PI / 180); a = sin((180- x->x_spread - beta) * M_PI / 180) / sin(beta * M_PI / 180); spreaddir[0] = a * vscartdir[0] + b * spread_base[0]; spreaddir[1] = a * vscartdir[1] + b * spread_base[1]; spreaddir[2] = a * vscartdir[2] + b * spread_base[2]; power = sqrt(spreaddir[0]*spreaddir[0] + spreaddir[1]*spreaddir[1] + spreaddir[2]*spreaddir[2]); spreaddir[0] /= power; spreaddir[1] /= power; spreaddir[2] /= power; } static void new_spread_base(t_vbap *x, t_float spreaddir[3], t_float vscartdir[3]) // subroutine for spreading { t_float d; t_float power; d = cos(x->x_spread / 180 * M_PI); x->x_spread_base[0] = spreaddir[0] - d * vscartdir[0]; x->x_spread_base[1] = spreaddir[1] - d * vscartdir[1]; x->x_spread_base[2] = spreaddir[2] - d * vscartdir[2]; power=sqrt(x->x_spread_base[0]*x->x_spread_base[0] + x->x_spread_base[1]*x->x_spread_base[1] + x->x_spread_base[2]*x->x_spread_base[2]); x->x_spread_base[0] /= power; x->x_spread_base[1] /= power; x->x_spread_base[2] /= power; } static void spread_it(t_vbap *x, t_float *final_gs) // apply the sound signal to multiple panning directions // that causes some spreading. // See theory in paper V. Pulkki "Uniform spreading of amplitude panned // virtual sources" in WASPAA 99 { t_float vscartdir[3]; t_float spreaddir[16][3]; t_float spreadbase[16][3]; long i, spreaddirnum; t_float power; if(x->x_dimension == 3) { spreaddirnum = 16; angle_to_cart(x->x_azi, x->x_ele, vscartdir); new_spread_dir(x, spreaddir[0], vscartdir, x->x_spread_base); new_spread_base(x, spreaddir[0], vscartdir); vect_cross_prod(x->x_spread_base, vscartdir, spreadbase[1]); // four orthogonal dirs vect_cross_prod(spreadbase[1], vscartdir, spreadbase[2]); vect_cross_prod(spreadbase[2], vscartdir, spreadbase[3]); // four between them for(i=0;i<3;i++) spreadbase[4][i] = (x->x_spread_base[i] + spreadbase[1][i]) / 2.0; for(i=0;i<3;i++) spreadbase[5][i] = (spreadbase[1][i] + spreadbase[2][i]) / 2.0; for(i=0;i<3;i++) spreadbase[6][i] = (spreadbase[2][i] + spreadbase[3][i]) / 2.0; for(i=0;i<3;i++) spreadbase[7][i] = (spreadbase[3][i] + x->x_spread_base[i]) / 2.0; // four at half spreadangle for(i=0;i<3;i++) spreadbase[8][i] = (vscartdir[i] + x->x_spread_base[i]) / 2.0; for(i=0;i<3;i++) spreadbase[9][i] = (vscartdir[i] + spreadbase[1][i]) / 2.0; for(i=0;i<3;i++) spreadbase[10][i] = (vscartdir[i] + spreadbase[2][i]) / 2.0; for(i=0;i<3;i++) spreadbase[11][i] = (vscartdir[i] + spreadbase[3][i]) / 2.0; // four at quarter spreadangle for(i=0;i<3;i++) spreadbase[12][i] = (vscartdir[i] + spreadbase[8][i]) / 2.0; for(i=0;i<3;i++) spreadbase[13][i] = (vscartdir[i] + spreadbase[9][i]) / 2.0; for(i=0;i<3;i++) spreadbase[14][i] = (vscartdir[i] + spreadbase[10][i]) / 2.0; for(i=0;i<3;i++) spreadbase[15][i] = (vscartdir[i] + spreadbase[11][i]) / 2.0; additive_vbap(final_gs, spreaddir[0], x); for(i = 1; i < spreaddirnum; i++){ new_spread_dir(x, spreaddir[i], vscartdir, spreadbase[i]); additive_vbap(final_gs, spreaddir[i], x); } } else if(x->x_dimension == 2) { spreaddirnum = 6; angle_to_cart(x->x_azi - x->x_spread, 0, spreaddir[0]); angle_to_cart(x->x_azi - x->x_spread / 2, 0, spreaddir[1]); angle_to_cart(x->x_azi - x->x_spread / 4, 0, spreaddir[2]); angle_to_cart(x->x_azi + x->x_spread / 4, 0, spreaddir[3]); angle_to_cart(x->x_azi + x->x_spread / 2, 0, spreaddir[4]); angle_to_cart(x->x_azi + x->x_spread, 0, spreaddir[5]); for(i = 0; i < spreaddirnum; i++) additive_vbap(final_gs, spreaddir[i], x); } else return; if(x->x_spread > 70) { for(i = 0; i < x->x_ls_amount; i++) { final_gs[i] += (x->x_spread - 70) / 30.0 * (x->x_spread - 70) / 30.0 * 10.0; } } for(i = 0, power = 0.0; i < x->x_ls_amount; i++) { power += final_gs[i] * final_gs[i]; } power = sqrt(power); for(i = 0; i < x->x_ls_amount; i++) { final_gs[i] /= power; } } static void vbap_bang(t_vbap *x) // top level, vbap gains are calculated and outputted { Atom at[MAX_LS_AMOUNT]; t_float g[3]; long ls[3]; long i; t_float *final_gs = (t_float *)getbytes(x->x_ls_amount * sizeof(t_float)); // avoid NaN explosions if(x->x_spread < 0.00001f) { x->x_spread = 0.0f; } if(x->x_lsset_available == 1) { vbap(g, ls, x); for(i = 0; i < x->x_ls_amount; i++) final_gs[i] = 0.0; for(i = 0; i < x->x_dimension; i++) { final_gs[ls[i]-1] = g[i]; } if(x->x_spread != 0) { spread_it(x,final_gs); } for(i = 0; i < x->x_ls_amount; i++) { #ifdef PD SETFLOAT(&at[0], (t_float)i); SETFLOAT(&at[1], (t_float)final_gs[i]); outlet_list(x->x_obj.ob_outlet, &s_list, 2, at); #else /* Max */ SETLONG(&at[0], i); SETFLOAT(&at[1], final_gs[i]*x->x_gain); // freeverb gain is applied here outlet_list(x->x_outlet0, 0L, 2, at); #endif /* PD */ } outlet_float(x->x_outlet1, x->x_azi); outlet_float(x->x_outlet2, x->x_ele); outlet_int(x->x_outlet3, x->x_spread); } else error("vbap: Configure loudspeakers first!"); freebytes(final_gs, x->x_ls_amount * sizeof(t_float)); // bug fix added 9/00 } /*--------------------------------------------------------------------------*/ static void vbap_matrix(t_vbap *x, Symbol *s, int ac, Atom *av) // read in loudspeaker matrices { int datapointer = 0; if(ac > 0) { int d = 0; /*if(av[datapointer].a_type == A_LONG) d = av[datapointer++].a_w.w_long; else*/ if(av[datapointer].a_type == A_FLOAT) d = (long)av[datapointer++].a_w.w_float; else { error("vbap: Dimension NaN"); x->x_lsset_available = 0; return; } if(d!=2 && d!=3) { error("vbap %s: Dimension can be only 2 or 3", s->s_name); x->x_lsset_available = 0; return; } x->x_dimension = d; x->x_lsset_available=1; } else { error("vbap %s: bad empty parameter list", s->s_name); x->x_lsset_available = 0; return; } if(ac > 1) { long a = 0; /*if(av[datapointer].a_type == A_LONG) a = av[datapointer++].a_w.w_long; else*/ if(av[datapointer].a_type == A_FLOAT) a = (long)av[datapointer++].a_w.w_float; else { error("vbap: ls_amount NaN"); x->x_lsset_available = 0; return; } x->x_ls_amount = a; } long counter = (ac - 2) / ((x->x_dimension * x->x_dimension * 2) + x->x_dimension); if(counter-1 > MAX_LS_SETS) { error("vbap %s: loudspeaker definitions exceed maximum number of speakers", s->s_name); x->x_lsset_available = 0; return; } vbap_getmem(x, counter); // PD only: allocate memory (frees any previously allocated memory automatically) x->x_lsset_amount = counter; if(counter == 0) { error("vbap %s: not enough parameters", s->s_name); x->x_lsset_available = 0; return; } long setpointer=0; long i; while(counter-- > 0) { for(i = 0; i < x->x_dimension; i++) { # ifdef PD if(av[datapointer].a_type == A_FLOAT) { x->x_lsset[setpointer][i] = (long)av[datapointer++].a_w.w_float; } else { error("vbap %s: param %d is not a float",s->s_name,datapointer); x->x_lsset_available = 0; return; } # else /* Max */ if(av[datapointer].a_type == A_LONG) { x->x_lsset[setpointer][i] = av[datapointer++].a_w.w_long; } else { error("vbap %s: param %d is not an in", s->s_name,datapointer); x->x_lsset_available = 0; return; } # endif /* PD */ } for(i = 0; i < x->x_dimension*x->x_dimension; i++) { if(av[datapointer].a_type == A_FLOAT) { x->x_set_inv_matx[setpointer][i] = av[datapointer++].a_w.w_float; } else { error("vbap BB %s: param %d is not a float", s->s_name,datapointer); x->x_lsset_available = 0; return; } } for(i = 0; i < x->x_dimension*x->x_dimension; i++) { if(av[datapointer].a_type == A_FLOAT) { x->x_set_matx[setpointer][i] = av[datapointer++].a_w.w_float; } else { error("vbap %s: param %d is not a float", s->s_name,datapointer); x->x_lsset_available = 0; return; } } // output defined loudspeaker sets for this setup t_atom atoms[4]; SETFLOAT(&atoms[0], setpointer); SETFLOAT(&atoms[1], x->x_lsset[setpointer][0]); SETFLOAT(&atoms[2], x->x_lsset[setpointer][1]); if(x->x_dimension == 3) { SETFLOAT(&atoms[3], x->x_lsset[setpointer][2]); } outlet_anything(x->x_outlet4, gensym("set"), x->x_dimension, atoms); setpointer++; } if(_enable_trace) post("vbap: Loudspeaker setup configured!"); } vbap-1.2.0/vbap.h000066400000000000000000000125351417256401100135520ustar00rootroot00000000000000#ifndef VBAP_H #define VBAP_H #include #ifndef M_PI // don't know where it is in win32, so add it here # define M_PI 3.14159265358979323846264338327950288 /* pi */ #endif #ifdef PD #include "m_pd.h" #include "max2pd.h" // Revised by Z. Settel to dynamically allocate memory # define MAX_LS_SETS 745 // maximum number of loudspeaker sets (triplets or pairs) allowed -- allows for up to 44 speakers in 3D config //#define MAX_LS_SETS 100 // former maximum value crashed for 3D speaker configurations with more than 13 speakers //#define MAX_LS_SETS 571 // example: for up to 32 speakers in 3D config #else // Max # define MAX_LS_SETS 100 // maximum number of loudspeaker sets (triplets or pairs) allowed - This can crash when too many speakers are defined #endif #define MATRIX_DIM 9 // hard-coded matrx dimension for the algorithm #define SPEAKER_SET_DIM 3 // hard-coded speaker set dimension for the algorithm #define MAX_LS_AMOUNT 55 // maximum amount of loudspeakers, can be increased, but see comments next to MAX_LS_SETS above #define MIN_VOL_P_SIDE_LGTH 0.01 #define VBAP_VERSION "vbap v1.2 - 17 Jul 2018 - (c) Ville Pulkki 1999-2006 (Pd port by HCS)" #define DFLS_VERSION "define_loudspeakers v1.2 - 17 July 2018 - (c) Ville Pulkki 1999-2006" static t_float rad2ang = 360.0 / (2.0f * M_PI); static t_float atorad = (2.0f * M_PI) / 360.0f; #ifdef VBAP_OBJECT // inside vbap object, so sending matrix from define_loudspeakers is a simple call to the vbap receiver... #define sendLoudspeakerMatrices(x, list_length, at) \ vbap_matrix(x, gensym("loudspeaker-matrices"),list_length, at); \ vbap_bang(x) #else // inside define_loudspeaker object, send matrix to outlet #define sendLoudspeakerMatrices(x, list_length, at) \ outlet_anything(x->x_outlet0, gensym("loudspeaker-matrices"), list_length, at); #endif /* A struct for a loudspeaker instance */ typedef struct { // distance value is 1.0 == unit vectors t_float x; // cartesian coordinates t_float y; t_float z; t_float azi; // polar coordinates t_float ele; int channel_nbr; // which speaker channel number } t_ls; /* A struct for all loudspeaker sets */ typedef struct t_ls_set { int ls_nos[3]; // channel numbers t_float inv_mx[9]; // inverse 3x3 or 2x2 matrix struct t_ls_set *next; // next set (triplet or pair) } t_ls_set; #ifdef VBAP_OBJECT typedef struct vbap /* This defines the object as an entity made up of other things */ { t_object x_obj; t_float x_azi; // panning direction azimuth t_float x_ele; // panning direction elevation void *x_outlet0; /* outlet creation - inlets are automatic */ void *x_outlet1; void *x_outlet2; void *x_outlet3; void *x_outlet4; long x_lsset_available; // have loudspeaker sets been defined with define_loudspeakers long x_lsset_amount; // amount of loudspeaker sets long x_ls_amount; // amount of loudspeakers long x_dimension; // 2 or 3 # ifdef PD // memory for data sets is now allocated dynamically in each instance // WAS t_float x_set_inv_matx[MAX_LS_SETS][9]; t_float **x_set_inv_matx; // inverse matrice for each loudspeaker set // WAS t_float x_set_matx[MAX_LS_SETS][9]; t_float **x_set_matx; // matrice for each loudspeaker set // WAS long x_lsset[MAX_LS_SETS][3]; long **x_lsset; // channel numbers of loudspeakers in each LS set t_float x_spread; // speading amount of virtual source (0-100) # else /* Max */ // memory allocation not tested for max, so it is allocated in the struct, as it was before t_float x_set_inv_matx[MAX_LS_SETS][9]; // inverse matrice for each loudspeaker set t_float x_set_matx[MAX_LS_SETS][9]; // matrice for each loudspeaker set long x_lsset[MAX_LS_SETS][3]; // channel numbers of loudspeakers in each LS set long x_spread; // speading amount of virtual source (0-100) double x_gain; // general gain control (0-2) # endif /* PD */ t_float x_spread_base[3]; // used to create uniform spreading // define_loudspeaker data long x_ls_read; // 1 if loudspeaker directions have been read long x_triplets_specified; // 1 if loudspeaker triplets have been chosen t_ls x_ls[MAX_LS_AMOUNT]; // loudspeakers t_ls_set *x_ls_set; // loudspeaker sets long x_def_ls_amount; // number of loudspeakers long x_def_ls_dimension; // 2 (horizontal arrays) or 3 (3d setups) long x_ls_setCount; // the number of loudspeaker sets used for an instance's current loudspeaker configuration long x_ls_set_current; // current active loudspeaker set } t_vbap; // define loudspeaker data type... typedef t_vbap t_def_ls; #else /* define_loudspeakers maxmsp object */ typedef struct { t_object x_obj; /* gotta say this... it creates a reference to your object */ long x_ls_read; // 1 if loudspeaker directions have been read long x_triplets_specified; // 1 if loudspeaker triplets have been chosen t_ls x_ls[MAX_LS_AMOUNT]; // loudspeakers t_ls_set *x_ls_set; // loudspeaker sets void *x_outlet0; /* outlet creation - inlets are automatic */ long x_def_ls_amount; // number of loudspeakers long x_def_ls_dimension; // 2 (horizontal arrays) or 3 (3d setups) } t_def_ls; #endif /* VBAP_OBJECT */ #ifndef PD /** Enable/Disable traces */ static bool _enable_trace = false; void traces(t_def_ls *x, long n) { _enable_trace = n ? true : false;} #endif /* ! PD */ #endif