initial commit

This commit is contained in:
Andrew Gundersen 2021-07-26 14:08:25 -05:00
commit 1d75db5bde
33 changed files with 2327 additions and 0 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

16
binding.gyp Normal file
View file

@ -0,0 +1,16 @@
{
"targets": [
{
"target_name": "nodeaudio",
"sources": [ "src/nodeaudio.c", "src/audio_handler.c", "src/frame_buffer.c" ],
"include_dirs": [
"/usr/local/include"
],
"link_settings": {
"libraries": [
"-lportaudio"
]
}
}
]
}

28
binding.js Normal file
View file

@ -0,0 +1,28 @@
const nodeAudio = require('bindings')('nodeaudio.node');
console.log("Imported nodeAudio");
const wait = async (callback, seconds) => {
await setTimeout(() => {
callback();
}, seconds);
}
const recordThenPlay = async (recSeconds) => {
console.log("Initializing nodeAudio");
nodeAudio.initialize();
nodeAudio.fill_input_buffer();
console.log(`Recording for ${recSeconds} seconds`);
const audio = await wait(nodeAudio.return_input_buffer, recSeconds);
console.log("Playing audio");
nodeAudio.feed_output_buffer(audio);
console.log("Shutting down audio");
nodeAudio.terminate();
}
recordThenPlay(5);

342
build/Makefile Normal file
View file

@ -0,0 +1,342 @@
# We borrow heavily from the kernel build setup, though we are simpler since
# we don't have Kconfig tweaking settings on us.
# The implicit make rules have it looking for RCS files, among other things.
# We instead explicitly write all the rules we care about.
# It's even quicker (saves ~200ms) to pass -r on the command line.
MAKEFLAGS=-r
# The source directory tree.
srcdir := ..
abs_srcdir := $(abspath $(srcdir))
# The name of the builddir.
builddir_name ?= .
# The V=1 flag on command line makes us verbosely print command lines.
ifdef V
quiet=
else
quiet=quiet_
endif
# Specify BUILDTYPE=Release on the command line for a release build.
BUILDTYPE ?= Release
# Directory all our build output goes into.
# Note that this must be two directories beneath src/ for unit tests to pass,
# as they reach into the src/ directory for data with relative paths.
builddir ?= $(builddir_name)/$(BUILDTYPE)
abs_builddir := $(abspath $(builddir))
depsdir := $(builddir)/.deps
# Object output directory.
obj := $(builddir)/obj
abs_obj := $(abspath $(obj))
# We build up a list of every single one of the targets so we can slurp in the
# generated dependency rule Makefiles in one pass.
all_deps :=
CC.target ?= $(CC)
CFLAGS.target ?= $(CPPFLAGS) $(CFLAGS)
CXX.target ?= $(CXX)
CXXFLAGS.target ?= $(CPPFLAGS) $(CXXFLAGS)
LINK.target ?= $(LINK)
LDFLAGS.target ?= $(LDFLAGS)
AR.target ?= $(AR)
# C++ apps need to be linked with g++.
LINK ?= $(CXX.target)
# TODO(evan): move all cross-compilation logic to gyp-time so we don't need
# to replicate this environment fallback in make as well.
CC.host ?= gcc
CFLAGS.host ?= $(CPPFLAGS_host) $(CFLAGS_host)
CXX.host ?= g++
CXXFLAGS.host ?= $(CPPFLAGS_host) $(CXXFLAGS_host)
LINK.host ?= $(CXX.host)
LDFLAGS.host ?=
AR.host ?= ar
# Define a dir function that can handle spaces.
# http://www.gnu.org/software/make/manual/make.html#Syntax-of-Functions
# "leading spaces cannot appear in the text of the first argument as written.
# These characters can be put into the argument value by variable substitution."
empty :=
space := $(empty) $(empty)
# http://stackoverflow.com/questions/1189781/using-make-dir-or-notdir-on-a-path-with-spaces
replace_spaces = $(subst $(space),?,$1)
unreplace_spaces = $(subst ?,$(space),$1)
dirx = $(call unreplace_spaces,$(dir $(call replace_spaces,$1)))
# Flags to make gcc output dependency info. Note that you need to be
# careful here to use the flags that ccache and distcc can understand.
# We write to a dep file on the side first and then rename at the end
# so we can't end up with a broken dep file.
depfile = $(depsdir)/$(call replace_spaces,$@).d
DEPFLAGS = -MMD -MF $(depfile).raw
# We have to fixup the deps output in a few ways.
# (1) the file output should mention the proper .o file.
# ccache or distcc lose the path to the target, so we convert a rule of
# the form:
# foobar.o: DEP1 DEP2
# into
# path/to/foobar.o: DEP1 DEP2
# (2) we want missing files not to cause us to fail to build.
# We want to rewrite
# foobar.o: DEP1 DEP2 \
# DEP3
# to
# DEP1:
# DEP2:
# DEP3:
# so if the files are missing, they're just considered phony rules.
# We have to do some pretty insane escaping to get those backslashes
# and dollar signs past make, the shell, and sed at the same time.
# Doesn't work with spaces, but that's fine: .d files have spaces in
# their names replaced with other characters.
define fixup_dep
# The depfile may not exist if the input file didn't have any #includes.
touch $(depfile).raw
# Fixup path as in (1).
sed -e "s|^$(notdir $@)|$@|" $(depfile).raw >> $(depfile)
# Add extra rules as in (2).
# We remove slashes and replace spaces with new lines;
# remove blank lines;
# delete the first line and append a colon to the remaining lines.
sed -e 's|\\||' -e 'y| |\n|' $(depfile).raw |\
grep -v '^$$' |\
sed -e 1d -e 's|$$|:|' \
>> $(depfile)
rm $(depfile).raw
endef
# Command definitions:
# - cmd_foo is the actual command to run;
# - quiet_cmd_foo is the brief-output summary of the command.
quiet_cmd_cc = CC($(TOOLSET)) $@
cmd_cc = $(CC.$(TOOLSET)) $(GYP_CFLAGS) $(DEPFLAGS) $(CFLAGS.$(TOOLSET)) -c -o $@ $<
quiet_cmd_cxx = CXX($(TOOLSET)) $@
cmd_cxx = $(CXX.$(TOOLSET)) $(GYP_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $<
quiet_cmd_objc = CXX($(TOOLSET)) $@
cmd_objc = $(CC.$(TOOLSET)) $(GYP_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $<
quiet_cmd_objcxx = CXX($(TOOLSET)) $@
cmd_objcxx = $(CXX.$(TOOLSET)) $(GYP_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $<
# Commands for precompiled header files.
quiet_cmd_pch_c = CXX($(TOOLSET)) $@
cmd_pch_c = $(CC.$(TOOLSET)) $(GYP_PCH_CFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $<
quiet_cmd_pch_cc = CXX($(TOOLSET)) $@
cmd_pch_cc = $(CC.$(TOOLSET)) $(GYP_PCH_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $<
quiet_cmd_pch_m = CXX($(TOOLSET)) $@
cmd_pch_m = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $<
quiet_cmd_pch_mm = CXX($(TOOLSET)) $@
cmd_pch_mm = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $<
# gyp-mac-tool is written next to the root Makefile by gyp.
# Use $(4) for the command, since $(2) and $(3) are used as flag by do_cmd
# already.
quiet_cmd_mac_tool = MACTOOL $(4) $<
cmd_mac_tool = ./gyp-mac-tool $(4) $< "$@"
quiet_cmd_mac_package_framework = PACKAGE FRAMEWORK $@
cmd_mac_package_framework = ./gyp-mac-tool package-framework "$@" $(4)
quiet_cmd_infoplist = INFOPLIST $@
cmd_infoplist = $(CC.$(TOOLSET)) -E -P -Wno-trigraphs -x c $(INFOPLIST_DEFINES) "$<" -o "$@"
quiet_cmd_touch = TOUCH $@
cmd_touch = touch $@
quiet_cmd_copy = COPY $@
# send stderr to /dev/null to ignore messages when linking directories.
cmd_copy = rm -rf "$@" && cp -af "$<" "$@"
quiet_cmd_alink = LIBTOOL-STATIC $@
cmd_alink = rm -f $@ && ./gyp-mac-tool filter-libtool libtool $(GYP_LIBTOOLFLAGS) -static -o $@ $(filter %.o,$^)
quiet_cmd_link = LINK($(TOOLSET)) $@
cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS)
quiet_cmd_solink = SOLINK($(TOOLSET)) $@
cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS)
quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@
cmd_solink_module = $(LINK.$(TOOLSET)) -bundle $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS)
# Define an escape_quotes function to escape single quotes.
# This allows us to handle quotes properly as long as we always use
# use single quotes and escape_quotes.
escape_quotes = $(subst ','\'',$(1))
# This comment is here just to include a ' to unconfuse syntax highlighting.
# Define an escape_vars function to escape '$' variable syntax.
# This allows us to read/write command lines with shell variables (e.g.
# $LD_LIBRARY_PATH), without triggering make substitution.
escape_vars = $(subst $$,$$$$,$(1))
# Helper that expands to a shell command to echo a string exactly as it is in
# make. This uses printf instead of echo because printf's behaviour with respect
# to escape sequences is more portable than echo's across different shells
# (e.g., dash, bash).
exact_echo = printf '%s\n' '$(call escape_quotes,$(1))'
# Helper to compare the command we're about to run against the command
# we logged the last time we ran the command. Produces an empty
# string (false) when the commands match.
# Tricky point: Make has no string-equality test function.
# The kernel uses the following, but it seems like it would have false
# positives, where one string reordered its arguments.
# arg_check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \
# $(filter-out $(cmd_$@), $(cmd_$(1))))
# We instead substitute each for the empty string into the other, and
# say they're equal if both substitutions produce the empty string.
# .d files contain ? instead of spaces, take that into account.
command_changed = $(or $(subst $(cmd_$(1)),,$(cmd_$(call replace_spaces,$@))),\
$(subst $(cmd_$(call replace_spaces,$@)),,$(cmd_$(1))))
# Helper that is non-empty when a prerequisite changes.
# Normally make does this implicitly, but we force rules to always run
# so we can check their command lines.
# $? -- new prerequisites
# $| -- order-only dependencies
prereq_changed = $(filter-out FORCE_DO_CMD,$(filter-out $|,$?))
# Helper that executes all postbuilds until one fails.
define do_postbuilds
@E=0;\
for p in $(POSTBUILDS); do\
eval $$p;\
E=$$?;\
if [ $$E -ne 0 ]; then\
break;\
fi;\
done;\
if [ $$E -ne 0 ]; then\
rm -rf "$@";\
exit $$E;\
fi
endef
# do_cmd: run a command via the above cmd_foo names, if necessary.
# Should always run for a given target to handle command-line changes.
# Second argument, if non-zero, makes it do asm/C/C++ dependency munging.
# Third argument, if non-zero, makes it do POSTBUILDS processing.
# Note: We intentionally do NOT call dirx for depfile, since it contains ? for
# spaces already and dirx strips the ? characters.
define do_cmd
$(if $(or $(command_changed),$(prereq_changed)),
@$(call exact_echo, $($(quiet)cmd_$(1)))
@mkdir -p "$(call dirx,$@)" "$(dir $(depfile))"
$(if $(findstring flock,$(word 2,$(cmd_$1))),
@$(cmd_$(1))
@echo " $(quiet_cmd_$(1)): Finished",
@$(cmd_$(1))
)
@$(call exact_echo,$(call escape_vars,cmd_$(call replace_spaces,$@) := $(cmd_$(1)))) > $(depfile)
@$(if $(2),$(fixup_dep))
$(if $(and $(3), $(POSTBUILDS)),
$(call do_postbuilds)
)
)
endef
# Declare the "all" target first so it is the default,
# even though we don't have the deps yet.
.PHONY: all
all:
# make looks for ways to re-generate included makefiles, but in our case, we
# don't have a direct way. Explicitly telling make that it has nothing to do
# for them makes it go faster.
%.d: ;
# Use FORCE_DO_CMD to force a target to run. Should be coupled with
# do_cmd.
.PHONY: FORCE_DO_CMD
FORCE_DO_CMD:
TOOLSET := target
# Suffix rules, putting all outputs into $(obj).
$(obj).$(TOOLSET)/%.o: $(srcdir)/%.c FORCE_DO_CMD
@$(call do_cmd,cc,1)
$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD
@$(call do_cmd,cxx,1)
$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cpp FORCE_DO_CMD
@$(call do_cmd,cxx,1)
$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cxx FORCE_DO_CMD
@$(call do_cmd,cxx,1)
$(obj).$(TOOLSET)/%.o: $(srcdir)/%.m FORCE_DO_CMD
@$(call do_cmd,objc,1)
$(obj).$(TOOLSET)/%.o: $(srcdir)/%.mm FORCE_DO_CMD
@$(call do_cmd,objcxx,1)
$(obj).$(TOOLSET)/%.o: $(srcdir)/%.S FORCE_DO_CMD
@$(call do_cmd,cc,1)
$(obj).$(TOOLSET)/%.o: $(srcdir)/%.s FORCE_DO_CMD
@$(call do_cmd,cc,1)
# Try building from generated source, too.
$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD
@$(call do_cmd,cc,1)
$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD
@$(call do_cmd,cxx,1)
$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cpp FORCE_DO_CMD
@$(call do_cmd,cxx,1)
$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cxx FORCE_DO_CMD
@$(call do_cmd,cxx,1)
$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.m FORCE_DO_CMD
@$(call do_cmd,objc,1)
$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.mm FORCE_DO_CMD
@$(call do_cmd,objcxx,1)
$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.S FORCE_DO_CMD
@$(call do_cmd,cc,1)
$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.s FORCE_DO_CMD
@$(call do_cmd,cc,1)
$(obj).$(TOOLSET)/%.o: $(obj)/%.c FORCE_DO_CMD
@$(call do_cmd,cc,1)
$(obj).$(TOOLSET)/%.o: $(obj)/%.cc FORCE_DO_CMD
@$(call do_cmd,cxx,1)
$(obj).$(TOOLSET)/%.o: $(obj)/%.cpp FORCE_DO_CMD
@$(call do_cmd,cxx,1)
$(obj).$(TOOLSET)/%.o: $(obj)/%.cxx FORCE_DO_CMD
@$(call do_cmd,cxx,1)
$(obj).$(TOOLSET)/%.o: $(obj)/%.m FORCE_DO_CMD
@$(call do_cmd,objc,1)
$(obj).$(TOOLSET)/%.o: $(obj)/%.mm FORCE_DO_CMD
@$(call do_cmd,objcxx,1)
$(obj).$(TOOLSET)/%.o: $(obj)/%.S FORCE_DO_CMD
@$(call do_cmd,cc,1)
$(obj).$(TOOLSET)/%.o: $(obj)/%.s FORCE_DO_CMD
@$(call do_cmd,cc,1)
ifeq ($(strip $(foreach prefix,$(NO_LOAD),\
$(findstring $(join ^,$(prefix)),\
$(join ^,nodeaudio.target.mk)))),)
include nodeaudio.target.mk
endif
quiet_cmd_regen_makefile = ACTION Regenerating $@
cmd_regen_makefile = cd $(srcdir); /usr/local/Cellar/node@14/14.17.0/lib/node_modules/npm/node_modules/node-gyp/gyp/gyp_main.py -fmake --ignore-environment "--toplevel-dir=." -I/Users/adgundersen/Documents/Crimata/nodeaudio/build/config.gypi -I/usr/local/Cellar/node@14/14.17.0/lib/node_modules/npm/node_modules/node-gyp/addon.gypi -I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/include/node/common.gypi "--depth=." "-Goutput_dir=." "--generator-output=build" "-Dlibrary=shared_library" "-Dvisibility=default" "-Dnode_root_dir=/Users/adgundersen/Library/Caches/node-gyp/14.17.0" "-Dnode_gyp_dir=/usr/local/Cellar/node@14/14.17.0/lib/node_modules/npm/node_modules/node-gyp" "-Dnode_lib_file=/Users/adgundersen/Library/Caches/node-gyp/14.17.0/<(target_arch)/node.lib" "-Dmodule_root_dir=/Users/adgundersen/Documents/Crimata/nodeaudio" "-Dnode_engine=v8" binding.gyp
Makefile: $(srcdir)/../../../Library/Caches/node-gyp/14.17.0/include/node/common.gypi $(srcdir)/../../../../../usr/local/Cellar/node@14/14.17.0/lib/node_modules/npm/node_modules/node-gyp/addon.gypi $(srcdir)/build/config.gypi $(srcdir)/binding.gyp
$(call do_cmd,regen_makefile)
# "all" is a concatenation of the "all" targets from all the included
# sub-makefiles. This is just here to clarify.
all:
# Add in dependency-tracking rules. $(all_deps) is the list of every single
# target in our tree. Only consider the ones with .d (dependency) info:
d_files := $(wildcard $(foreach f,$(all_deps),$(depsdir)/$(f).d))
ifneq ($(d_files),)
include $(d_files)
endif

View file

@ -0,0 +1 @@
cmd_Release/nodeaudio.node := c++ -bundle -undefined dynamic_lookup -Wl,-no_pie -Wl,-search_paths_first -mmacosx-version-min=10.13 -arch x86_64 -L./Release -stdlib=libc++ -o Release/nodeaudio.node Release/obj.target/nodeaudio/src/nodeaudio.o Release/obj.target/nodeaudio/src/audio_handler.o Release/obj.target/nodeaudio/src/frame_buffer.o -lportaudio

View file

@ -0,0 +1,8 @@
cmd_Release/obj.target/nodeaudio/src/audio_handler.o := cc '-DNODE_GYP_MODULE_NAME=nodeaudio' '-DUSING_UV_SHARED=1' '-DUSING_V8_SHARED=1' '-DV8_DEPRECATION_WARNINGS=1' '-DV8_DEPRECATION_WARNINGS' '-DV8_IMMINENT_DEPRECATION_WARNINGS' '-D_DARWIN_USE_64_BIT_INODE=1' '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' '-DOPENSSL_NO_PINSHARED' '-DOPENSSL_THREADS' '-DBUILDING_NODE_EXTENSION' -I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/include/node -I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/src -I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/deps/openssl/config -I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/deps/openssl/openssl/include -I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/deps/uv/include -I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/deps/zlib -I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/deps/v8/include -I/usr/local/include -O3 -gdwarf-2 -mmacosx-version-min=10.13 -arch x86_64 -Wall -Wendif-labels -W -Wno-unused-parameter -fno-strict-aliasing -MMD -MF ./Release/.deps/Release/obj.target/nodeaudio/src/audio_handler.o.d.raw -c -o Release/obj.target/nodeaudio/src/audio_handler.o ../src/audio_handler.c
Release/obj.target/nodeaudio/src/audio_handler.o: ../src/audio_handler.c \
/usr/local/include/portaudio.h ../src/audio_handler.h \
../src/frame_buffer.h
../src/audio_handler.c:
/usr/local/include/portaudio.h:
../src/audio_handler.h:
../src/frame_buffer.h:

View file

@ -0,0 +1,5 @@
cmd_Release/obj.target/nodeaudio/src/frame_buffer.o := cc '-DNODE_GYP_MODULE_NAME=nodeaudio' '-DUSING_UV_SHARED=1' '-DUSING_V8_SHARED=1' '-DV8_DEPRECATION_WARNINGS=1' '-DV8_DEPRECATION_WARNINGS' '-DV8_IMMINENT_DEPRECATION_WARNINGS' '-D_DARWIN_USE_64_BIT_INODE=1' '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' '-DOPENSSL_NO_PINSHARED' '-DOPENSSL_THREADS' '-DBUILDING_NODE_EXTENSION' -I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/include/node -I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/src -I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/deps/openssl/config -I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/deps/openssl/openssl/include -I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/deps/uv/include -I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/deps/zlib -I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/deps/v8/include -I/usr/local/include -O3 -gdwarf-2 -mmacosx-version-min=10.13 -arch x86_64 -Wall -Wendif-labels -W -Wno-unused-parameter -fno-strict-aliasing -MMD -MF ./Release/.deps/Release/obj.target/nodeaudio/src/frame_buffer.o.d.raw -c -o Release/obj.target/nodeaudio/src/frame_buffer.o ../src/frame_buffer.c
Release/obj.target/nodeaudio/src/frame_buffer.o: ../src/frame_buffer.c \
../src/frame_buffer.h
../src/frame_buffer.c:
../src/frame_buffer.h:

View file

@ -0,0 +1,13 @@
cmd_Release/obj.target/nodeaudio/src/nodeaudio.o := cc '-DNODE_GYP_MODULE_NAME=nodeaudio' '-DUSING_UV_SHARED=1' '-DUSING_V8_SHARED=1' '-DV8_DEPRECATION_WARNINGS=1' '-DV8_DEPRECATION_WARNINGS' '-DV8_IMMINENT_DEPRECATION_WARNINGS' '-D_DARWIN_USE_64_BIT_INODE=1' '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' '-DOPENSSL_NO_PINSHARED' '-DOPENSSL_THREADS' '-DBUILDING_NODE_EXTENSION' -I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/include/node -I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/src -I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/deps/openssl/config -I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/deps/openssl/openssl/include -I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/deps/uv/include -I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/deps/zlib -I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/deps/v8/include -I/usr/local/include -O3 -gdwarf-2 -mmacosx-version-min=10.13 -arch x86_64 -Wall -Wendif-labels -W -Wno-unused-parameter -fno-strict-aliasing -MMD -MF ./Release/.deps/Release/obj.target/nodeaudio/src/nodeaudio.o.d.raw -c -o Release/obj.target/nodeaudio/src/nodeaudio.o ../src/nodeaudio.c
Release/obj.target/nodeaudio/src/nodeaudio.o: ../src/nodeaudio.c \
/Users/adgundersen/Library/Caches/node-gyp/14.17.0/include/node/node_api.h \
/Users/adgundersen/Library/Caches/node-gyp/14.17.0/include/node/js_native_api.h \
/Users/adgundersen/Library/Caches/node-gyp/14.17.0/include/node/js_native_api_types.h \
/Users/adgundersen/Library/Caches/node-gyp/14.17.0/include/node/node_api_types.h \
../src/audio_handler.h
../src/nodeaudio.c:
/Users/adgundersen/Library/Caches/node-gyp/14.17.0/include/node/node_api.h:
/Users/adgundersen/Library/Caches/node-gyp/14.17.0/include/node/js_native_api.h:
/Users/adgundersen/Library/Caches/node-gyp/14.17.0/include/node/js_native_api_types.h:
/Users/adgundersen/Library/Caches/node-gyp/14.17.0/include/node/node_api_types.h:
../src/audio_handler.h:

BIN
build/Release/nodeaudio.node Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

6
build/binding.Makefile Normal file
View file

@ -0,0 +1,6 @@
# This file is generated by gyp; do not edit.
export builddir_name ?= ./build/.
.PHONY: all
all:
$(MAKE) nodeaudio

200
build/config.gypi Normal file
View file

@ -0,0 +1,200 @@
# Do not edit. File was generated by node-gyp's "configure" step
{
"target_defaults": {
"cflags": [],
"default_configuration": "Release",
"defines": [],
"include_dirs": [],
"libraries": []
},
"variables": {
"asan": 0,
"build_v8_with_gn": "false",
"coverage": "false",
"dcheck_always_on": 0,
"debug_nghttp2": "false",
"debug_node": "false",
"enable_lto": "false",
"enable_pgo_generate": "false",
"enable_pgo_use": "false",
"error_on_warn": "false",
"force_dynamic_crt": 0,
"host_arch": "x64",
"icu_gyp_path": "tools/icu/icu-system.gyp",
"icu_small": "false",
"icu_ver_major": "69",
"is_debug": 0,
"llvm_version": "12.0",
"napi_build_version": "8",
"node_byteorder": "little",
"node_debug_lib": "false",
"node_enable_d8": "false",
"node_install_npm": "true",
"node_module_version": 83,
"node_no_browser_globals": "false",
"node_prefix": "/usr/local/Cellar/node@14/14.17.0",
"node_release_urlbase": "",
"node_shared": "false",
"node_shared_brotli": "false",
"node_shared_cares": "false",
"node_shared_http_parser": "false",
"node_shared_libuv": "false",
"node_shared_nghttp2": "false",
"node_shared_openssl": "false",
"node_shared_zlib": "false",
"node_tag": "",
"node_target_type": "executable",
"node_use_bundled_v8": "true",
"node_use_dtrace": "true",
"node_use_etw": "false",
"node_use_node_code_cache": "true",
"node_use_node_snapshot": "true",
"node_use_openssl": "true",
"node_use_v8_platform": "true",
"node_with_ltcg": "false",
"node_without_node_options": "false",
"openssl_fips": "",
"openssl_is_fips": "false",
"ossfuzz": "false",
"shlib_suffix": "83.dylib",
"target_arch": "x64",
"v8_enable_31bit_smis_on_64bit_arch": 0,
"v8_enable_gdbjit": 0,
"v8_enable_i18n_support": 1,
"v8_enable_inspector": 1,
"v8_enable_lite_mode": 0,
"v8_enable_object_print": 1,
"v8_enable_pointer_compression": 0,
"v8_no_strict_aliasing": 1,
"v8_optimized_debug": 1,
"v8_promise_internal_field_count": 1,
"v8_random_seed": 0,
"v8_trace_maps": 0,
"v8_use_siphash": 1,
"want_separate_host_toolset": 0,
"xcode_version": "12.0",
"nodedir": "/Users/adgundersen/Library/Caches/node-gyp/14.17.0",
"standalone_static_library": 1,
"dry_run": "",
"legacy_bundling": "",
"save_dev": "",
"browser": "",
"commit_hooks": "true",
"only": "",
"viewer": "man",
"also": "",
"rollback": "true",
"sign_git_commit": "",
"audit": "true",
"usage": "",
"globalignorefile": "/usr/local/etc/npmignore",
"init_author_url": "",
"maxsockets": "50",
"shell": "/bin/zsh",
"metrics_registry": "https://registry.npmjs.org/",
"parseable": "",
"shrinkwrap": "true",
"init_license": "ISC",
"timing": "",
"if_present": "",
"cache_max": "Infinity",
"init_author_email": "",
"sign_git_tag": "",
"cert": "",
"git_tag_version": "true",
"local_address": "",
"long": "",
"preid": "",
"fetch_retries": "2",
"registry": "https://registry.npmjs.org/",
"key": "",
"message": "%s",
"versions": "",
"globalconfig": "/usr/local/etc/npmrc",
"always_auth": "",
"logs_max": "10",
"prefer_online": "",
"cache_lock_retries": "10",
"global_style": "",
"update_notifier": "true",
"audit_level": "low",
"heading": "npm",
"fetch_retry_mintimeout": "10000",
"offline": "",
"read_only": "",
"searchlimit": "20",
"access": "",
"json": "",
"allow_same_version": "",
"description": "true",
"engine_strict": "",
"https_proxy": "",
"init_module": "/Users/adgundersen/.npm-init.js",
"userconfig": "/Users/adgundersen/.npmrc",
"cidr": "",
"node_version": "14.17.0",
"user": "",
"auth_type": "legacy",
"editor": "vi",
"ignore_prepublish": "",
"save": "true",
"script_shell": "",
"tag": "latest",
"before": "",
"global": "",
"progress": "true",
"ham_it_up": "",
"optional": "true",
"searchstaleness": "900",
"bin_links": "true",
"force": "",
"save_prod": "",
"searchopts": "",
"depth": "Infinity",
"node_gyp": "/usr/local/Cellar/node@14/14.17.0/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js",
"rebuild_bundle": "true",
"sso_poll_frequency": "500",
"unicode": "true",
"fetch_retry_maxtimeout": "60000",
"ca": "",
"save_prefix": "^",
"scripts_prepend_node_path": "warn-only",
"sso_type": "oauth",
"strict_ssl": "true",
"tag_version_prefix": "v",
"dev": "",
"fetch_retry_factor": "10",
"group": "20",
"save_exact": "",
"cache_lock_stale": "60000",
"prefer_offline": "",
"version": "",
"cache_min": "10",
"otp": "",
"cache": "/Users/adgundersen/.npm",
"searchexclude": "",
"color": "true",
"package_lock": "true",
"fund": "true",
"package_lock_only": "",
"save_optional": "",
"user_agent": "npm/6.14.13 node/v14.17.0 darwin x64",
"ignore_scripts": "",
"cache_lock_wait": "10000",
"production": "",
"save_bundle": "",
"send_metrics": "",
"init_version": "1.0.0",
"node_options": "",
"umask": "0022",
"scope": "",
"git": "git",
"init_author_name": "",
"onload_script": "",
"tmp": "/var/folders/8h/5gtb4v414sj09_yzw9rs5z7m0000gq/T",
"unsafe_perm": "true",
"prefix": "/usr/local",
"format_package_lock": "true",
"link": ""
}
}

615
build/gyp-mac-tool Executable file
View file

@ -0,0 +1,615 @@
#!/usr/bin/env python
# Generated by gyp. Do not edit.
# Copyright (c) 2012 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Utility functions to perform Xcode-style build steps.
These functions are executed via gyp-mac-tool when using the Makefile generator.
"""
from __future__ import print_function
import fcntl
import fnmatch
import glob
import json
import os
import plistlib
import re
import shutil
import string
import subprocess
import sys
import tempfile
PY3 = bytes != str
def main(args):
executor = MacTool()
exit_code = executor.Dispatch(args)
if exit_code is not None:
sys.exit(exit_code)
class MacTool(object):
"""This class performs all the Mac tooling steps. The methods can either be
executed directly, or dispatched from an argument list."""
def Dispatch(self, args):
"""Dispatches a string command to a method."""
if len(args) < 1:
raise Exception("Not enough arguments")
method = "Exec%s" % self._CommandifyName(args[0])
return getattr(self, method)(*args[1:])
def _CommandifyName(self, name_string):
"""Transforms a tool name like copy-info-plist to CopyInfoPlist"""
return name_string.title().replace('-', '')
def ExecCopyBundleResource(self, source, dest, convert_to_binary):
"""Copies a resource file to the bundle/Resources directory, performing any
necessary compilation on each resource."""
extension = os.path.splitext(source)[1].lower()
if os.path.isdir(source):
# Copy tree.
# TODO(thakis): This copies file attributes like mtime, while the
# single-file branch below doesn't. This should probably be changed to
# be consistent with the single-file branch.
if os.path.exists(dest):
shutil.rmtree(dest)
shutil.copytree(source, dest)
elif extension == '.xib':
return self._CopyXIBFile(source, dest)
elif extension == '.storyboard':
return self._CopyXIBFile(source, dest)
elif extension == '.strings':
self._CopyStringsFile(source, dest, convert_to_binary)
else:
shutil.copy(source, dest)
def _CopyXIBFile(self, source, dest):
"""Compiles a XIB file with ibtool into a binary plist in the bundle."""
# ibtool sometimes crashes with relative paths. See crbug.com/314728.
base = os.path.dirname(os.path.realpath(__file__))
if os.path.relpath(source):
source = os.path.join(base, source)
if os.path.relpath(dest):
dest = os.path.join(base, dest)
args = ['xcrun', 'ibtool', '--errors', '--warnings', '--notices',
'--output-format', 'human-readable-text', '--compile', dest, source]
ibtool_section_re = re.compile(r'/\*.*\*/')
ibtool_re = re.compile(r'.*note:.*is clipping its content')
ibtoolout = subprocess.Popen(args, stdout=subprocess.PIPE)
current_section_header = None
for line in ibtoolout.stdout:
if ibtool_section_re.match(line):
current_section_header = line
elif not ibtool_re.match(line):
if current_section_header:
sys.stdout.write(current_section_header)
current_section_header = None
sys.stdout.write(line)
return ibtoolout.returncode
def _ConvertToBinary(self, dest):
subprocess.check_call([
'xcrun', 'plutil', '-convert', 'binary1', '-o', dest, dest])
def _CopyStringsFile(self, source, dest, convert_to_binary):
"""Copies a .strings file using iconv to reconvert the input into UTF-16."""
input_code = self._DetectInputEncoding(source) or "UTF-8"
# Xcode's CpyCopyStringsFile / builtin-copyStrings seems to call
# CFPropertyListCreateFromXMLData() behind the scenes; at least it prints
# CFPropertyListCreateFromXMLData(): Old-style plist parser: missing
# semicolon in dictionary.
# on invalid files. Do the same kind of validation.
import CoreFoundation
s = open(source, 'rb').read()
d = CoreFoundation.CFDataCreate(None, s, len(s))
_, error = CoreFoundation.CFPropertyListCreateFromXMLData(None, d, 0, None)
if error:
return
fp = open(dest, 'wb')
fp.write(s.decode(input_code).encode('UTF-16'))
fp.close()
if convert_to_binary == 'True':
self._ConvertToBinary(dest)
def _DetectInputEncoding(self, file_name):
"""Reads the first few bytes from file_name and tries to guess the text
encoding. Returns None as a guess if it can't detect it."""
fp = open(file_name, 'rb')
try:
header = fp.read(3)
except Exception:
fp.close()
return None
fp.close()
if header.startswith("\xFE\xFF"):
return "UTF-16"
elif header.startswith("\xFF\xFE"):
return "UTF-16"
elif header.startswith("\xEF\xBB\xBF"):
return "UTF-8"
else:
return None
def ExecCopyInfoPlist(self, source, dest, convert_to_binary, *keys):
"""Copies the |source| Info.plist to the destination directory |dest|."""
# Read the source Info.plist into memory.
fd = open(source, 'r')
lines = fd.read()
fd.close()
# Insert synthesized key/value pairs (e.g. BuildMachineOSBuild).
plist = plistlib.readPlistFromString(lines)
if keys:
plist = dict(plist.items() + json.loads(keys[0]).items())
lines = plistlib.writePlistToString(plist)
# Go through all the environment variables and replace them as variables in
# the file.
IDENT_RE = re.compile(r'[/\s]')
for key in os.environ:
if key.startswith('_'):
continue
evar = '${%s}' % key
evalue = os.environ[key]
lines = string.replace(lines, evar, evalue)
# Xcode supports various suffices on environment variables, which are
# all undocumented. :rfc1034identifier is used in the standard project
# template these days, and :identifier was used earlier. They are used to
# convert non-url characters into things that look like valid urls --
# except that the replacement character for :identifier, '_' isn't valid
# in a URL either -- oops, hence :rfc1034identifier was born.
evar = '${%s:identifier}' % key
evalue = IDENT_RE.sub('_', os.environ[key])
lines = string.replace(lines, evar, evalue)
evar = '${%s:rfc1034identifier}' % key
evalue = IDENT_RE.sub('-', os.environ[key])
lines = string.replace(lines, evar, evalue)
# Remove any keys with values that haven't been replaced.
lines = lines.split('\n')
for i in range(len(lines)):
if lines[i].strip().startswith("<string>${"):
lines[i] = None
lines[i - 1] = None
lines = '\n'.join(filter(lambda x: x is not None, lines))
# Write out the file with variables replaced.
fd = open(dest, 'w')
fd.write(lines)
fd.close()
# Now write out PkgInfo file now that the Info.plist file has been
# "compiled".
self._WritePkgInfo(dest)
if convert_to_binary == 'True':
self._ConvertToBinary(dest)
def _WritePkgInfo(self, info_plist):
"""This writes the PkgInfo file from the data stored in Info.plist."""
plist = plistlib.readPlist(info_plist)
if not plist:
return
# Only create PkgInfo for executable types.
package_type = plist['CFBundlePackageType']
if package_type != 'APPL':
return
# The format of PkgInfo is eight characters, representing the bundle type
# and bundle signature, each four characters. If that is missing, four
# '?' characters are used instead.
signature_code = plist.get('CFBundleSignature', '????')
if len(signature_code) != 4: # Wrong length resets everything, too.
signature_code = '?' * 4
dest = os.path.join(os.path.dirname(info_plist), 'PkgInfo')
fp = open(dest, 'w')
fp.write('%s%s' % (package_type, signature_code))
fp.close()
def ExecFlock(self, lockfile, *cmd_list):
"""Emulates the most basic behavior of Linux's flock(1)."""
# Rely on exception handling to report errors.
fd = os.open(lockfile, os.O_RDONLY|os.O_NOCTTY|os.O_CREAT, 0o666)
fcntl.flock(fd, fcntl.LOCK_EX)
return subprocess.call(cmd_list)
def ExecFilterLibtool(self, *cmd_list):
"""Calls libtool and filters out '/path/to/libtool: file: foo.o has no
symbols'."""
libtool_re = re.compile(r'^.*libtool: file: .* has no symbols$')
libtool_re5 = re.compile(
r'^.*libtool: warning for library: ' +
r'.* the table of contents is empty ' +
r'\(no object file members in the library define global symbols\)$')
env = os.environ.copy()
# Ref:
# http://www.opensource.apple.com/source/cctools/cctools-809/misc/libtool.c
# The problem with this flag is that it resets the file mtime on the file to
# epoch=0, e.g. 1970-1-1 or 1969-12-31 depending on timezone.
env['ZERO_AR_DATE'] = '1'
libtoolout = subprocess.Popen(cmd_list, stderr=subprocess.PIPE, env=env)
_, err = libtoolout.communicate()
if PY3:
err = err.decode('utf-8')
for line in err.splitlines():
if not libtool_re.match(line) and not libtool_re5.match(line):
print(line, file=sys.stderr)
# Unconditionally touch the output .a file on the command line if present
# and the command succeeded. A bit hacky.
if not libtoolout.returncode:
for i in range(len(cmd_list) - 1):
if cmd_list[i] == "-o" and cmd_list[i+1].endswith('.a'):
os.utime(cmd_list[i+1], None)
break
return libtoolout.returncode
def ExecPackageFramework(self, framework, version):
"""Takes a path to Something.framework and the Current version of that and
sets up all the symlinks."""
# Find the name of the binary based on the part before the ".framework".
binary = os.path.basename(framework).split('.')[0]
CURRENT = 'Current'
RESOURCES = 'Resources'
VERSIONS = 'Versions'
if not os.path.exists(os.path.join(framework, VERSIONS, version, binary)):
# Binary-less frameworks don't seem to contain symlinks (see e.g.
# chromium's out/Debug/org.chromium.Chromium.manifest/ bundle).
return
# Move into the framework directory to set the symlinks correctly.
pwd = os.getcwd()
os.chdir(framework)
# Set up the Current version.
self._Relink(version, os.path.join(VERSIONS, CURRENT))
# Set up the root symlinks.
self._Relink(os.path.join(VERSIONS, CURRENT, binary), binary)
self._Relink(os.path.join(VERSIONS, CURRENT, RESOURCES), RESOURCES)
# Back to where we were before!
os.chdir(pwd)
def _Relink(self, dest, link):
"""Creates a symlink to |dest| named |link|. If |link| already exists,
it is overwritten."""
if os.path.lexists(link):
os.remove(link)
os.symlink(dest, link)
def ExecCompileXcassets(self, keys, *inputs):
"""Compiles multiple .xcassets files into a single .car file.
This invokes 'actool' to compile all the inputs .xcassets files. The
|keys| arguments is a json-encoded dictionary of extra arguments to
pass to 'actool' when the asset catalogs contains an application icon
or a launch image.
Note that 'actool' does not create the Assets.car file if the asset
catalogs does not contains imageset.
"""
command_line = [
'xcrun', 'actool', '--output-format', 'human-readable-text',
'--compress-pngs', '--notices', '--warnings', '--errors',
]
is_iphone_target = 'IPHONEOS_DEPLOYMENT_TARGET' in os.environ
if is_iphone_target:
platform = os.environ['CONFIGURATION'].split('-')[-1]
if platform not in ('iphoneos', 'iphonesimulator'):
platform = 'iphonesimulator'
command_line.extend([
'--platform', platform, '--target-device', 'iphone',
'--target-device', 'ipad', '--minimum-deployment-target',
os.environ['IPHONEOS_DEPLOYMENT_TARGET'], '--compile',
os.path.abspath(os.environ['CONTENTS_FOLDER_PATH']),
])
else:
command_line.extend([
'--platform', 'macosx', '--target-device', 'mac',
'--minimum-deployment-target', os.environ['MACOSX_DEPLOYMENT_TARGET'],
'--compile',
os.path.abspath(os.environ['UNLOCALIZED_RESOURCES_FOLDER_PATH']),
])
if keys:
keys = json.loads(keys)
for key, value in keys.items():
arg_name = '--' + key
if isinstance(value, bool):
if value:
command_line.append(arg_name)
elif isinstance(value, list):
for v in value:
command_line.append(arg_name)
command_line.append(str(v))
else:
command_line.append(arg_name)
command_line.append(str(value))
# Note: actool crashes if inputs path are relative, so use os.path.abspath
# to get absolute path name for inputs.
command_line.extend(map(os.path.abspath, inputs))
subprocess.check_call(command_line)
def ExecMergeInfoPlist(self, output, *inputs):
"""Merge multiple .plist files into a single .plist file."""
merged_plist = {}
for path in inputs:
plist = self._LoadPlistMaybeBinary(path)
self._MergePlist(merged_plist, plist)
plistlib.writePlist(merged_plist, output)
def ExecCodeSignBundle(self, key, resource_rules, entitlements, provisioning):
"""Code sign a bundle.
This function tries to code sign an iOS bundle, following the same
algorithm as Xcode:
1. copy ResourceRules.plist from the user or the SDK into the bundle,
2. pick the provisioning profile that best match the bundle identifier,
and copy it into the bundle as embedded.mobileprovision,
3. copy Entitlements.plist from user or SDK next to the bundle,
4. code sign the bundle.
"""
resource_rules_path = self._InstallResourceRules(resource_rules)
substitutions, overrides = self._InstallProvisioningProfile(
provisioning, self._GetCFBundleIdentifier())
entitlements_path = self._InstallEntitlements(
entitlements, substitutions, overrides)
subprocess.check_call([
'codesign', '--force', '--sign', key, '--resource-rules',
resource_rules_path, '--entitlements', entitlements_path,
os.path.join(
os.environ['TARGET_BUILD_DIR'],
os.environ['FULL_PRODUCT_NAME'])])
def _InstallResourceRules(self, resource_rules):
"""Installs ResourceRules.plist from user or SDK into the bundle.
Args:
resource_rules: string, optional, path to the ResourceRules.plist file
to use, default to "${SDKROOT}/ResourceRules.plist"
Returns:
Path to the copy of ResourceRules.plist into the bundle.
"""
source_path = resource_rules
target_path = os.path.join(
os.environ['BUILT_PRODUCTS_DIR'],
os.environ['CONTENTS_FOLDER_PATH'],
'ResourceRules.plist')
if not source_path:
source_path = os.path.join(
os.environ['SDKROOT'], 'ResourceRules.plist')
shutil.copy2(source_path, target_path)
return target_path
def _InstallProvisioningProfile(self, profile, bundle_identifier):
"""Installs embedded.mobileprovision into the bundle.
Args:
profile: string, optional, short name of the .mobileprovision file
to use, if empty or the file is missing, the best file installed
will be used
bundle_identifier: string, value of CFBundleIdentifier from Info.plist
Returns:
A tuple containing two dictionary: variables substitutions and values
to overrides when generating the entitlements file.
"""
source_path, provisioning_data, team_id = self._FindProvisioningProfile(
profile, bundle_identifier)
target_path = os.path.join(
os.environ['BUILT_PRODUCTS_DIR'],
os.environ['CONTENTS_FOLDER_PATH'],
'embedded.mobileprovision')
shutil.copy2(source_path, target_path)
substitutions = self._GetSubstitutions(bundle_identifier, team_id + '.')
return substitutions, provisioning_data['Entitlements']
def _FindProvisioningProfile(self, profile, bundle_identifier):
"""Finds the .mobileprovision file to use for signing the bundle.
Checks all the installed provisioning profiles (or if the user specified
the PROVISIONING_PROFILE variable, only consult it) and select the most
specific that correspond to the bundle identifier.
Args:
profile: string, optional, short name of the .mobileprovision file
to use, if empty or the file is missing, the best file installed
will be used
bundle_identifier: string, value of CFBundleIdentifier from Info.plist
Returns:
A tuple of the path to the selected provisioning profile, the data of
the embedded plist in the provisioning profile and the team identifier
to use for code signing.
Raises:
SystemExit: if no .mobileprovision can be used to sign the bundle.
"""
profiles_dir = os.path.join(
os.environ['HOME'], 'Library', 'MobileDevice', 'Provisioning Profiles')
if not os.path.isdir(profiles_dir):
print('cannot find mobile provisioning for %s' % (bundle_identifier), file=sys.stderr)
sys.exit(1)
provisioning_profiles = None
if profile:
profile_path = os.path.join(profiles_dir, profile + '.mobileprovision')
if os.path.exists(profile_path):
provisioning_profiles = [profile_path]
if not provisioning_profiles:
provisioning_profiles = glob.glob(
os.path.join(profiles_dir, '*.mobileprovision'))
valid_provisioning_profiles = {}
for profile_path in provisioning_profiles:
profile_data = self._LoadProvisioningProfile(profile_path)
app_id_pattern = profile_data.get(
'Entitlements', {}).get('application-identifier', '')
for team_identifier in profile_data.get('TeamIdentifier', []):
app_id = '%s.%s' % (team_identifier, bundle_identifier)
if fnmatch.fnmatch(app_id, app_id_pattern):
valid_provisioning_profiles[app_id_pattern] = (
profile_path, profile_data, team_identifier)
if not valid_provisioning_profiles:
print('cannot find mobile provisioning for %s' % (bundle_identifier), file=sys.stderr)
sys.exit(1)
# If the user has multiple provisioning profiles installed that can be
# used for ${bundle_identifier}, pick the most specific one (ie. the
# provisioning profile whose pattern is the longest).
selected_key = max(valid_provisioning_profiles, key=lambda v: len(v))
return valid_provisioning_profiles[selected_key]
def _LoadProvisioningProfile(self, profile_path):
"""Extracts the plist embedded in a provisioning profile.
Args:
profile_path: string, path to the .mobileprovision file
Returns:
Content of the plist embedded in the provisioning profile as a dictionary.
"""
with tempfile.NamedTemporaryFile() as temp:
subprocess.check_call([
'security', 'cms', '-D', '-i', profile_path, '-o', temp.name])
return self._LoadPlistMaybeBinary(temp.name)
def _MergePlist(self, merged_plist, plist):
"""Merge |plist| into |merged_plist|."""
for key, value in plist.items():
if isinstance(value, dict):
merged_value = merged_plist.get(key, {})
if isinstance(merged_value, dict):
self._MergePlist(merged_value, value)
merged_plist[key] = merged_value
else:
merged_plist[key] = value
else:
merged_plist[key] = value
def _LoadPlistMaybeBinary(self, plist_path):
"""Loads into a memory a plist possibly encoded in binary format.
This is a wrapper around plistlib.readPlist that tries to convert the
plist to the XML format if it can't be parsed (assuming that it is in
the binary format).
Args:
plist_path: string, path to a plist file, in XML or binary format
Returns:
Content of the plist as a dictionary.
"""
try:
# First, try to read the file using plistlib that only supports XML,
# and if an exception is raised, convert a temporary copy to XML and
# load that copy.
return plistlib.readPlist(plist_path)
except:
pass
with tempfile.NamedTemporaryFile() as temp:
shutil.copy2(plist_path, temp.name)
subprocess.check_call(['plutil', '-convert', 'xml1', temp.name])
return plistlib.readPlist(temp.name)
def _GetSubstitutions(self, bundle_identifier, app_identifier_prefix):
"""Constructs a dictionary of variable substitutions for Entitlements.plist.
Args:
bundle_identifier: string, value of CFBundleIdentifier from Info.plist
app_identifier_prefix: string, value for AppIdentifierPrefix
Returns:
Dictionary of substitutions to apply when generating Entitlements.plist.
"""
return {
'CFBundleIdentifier': bundle_identifier,
'AppIdentifierPrefix': app_identifier_prefix,
}
def _GetCFBundleIdentifier(self):
"""Extracts CFBundleIdentifier value from Info.plist in the bundle.
Returns:
Value of CFBundleIdentifier in the Info.plist located in the bundle.
"""
info_plist_path = os.path.join(
os.environ['TARGET_BUILD_DIR'],
os.environ['INFOPLIST_PATH'])
info_plist_data = self._LoadPlistMaybeBinary(info_plist_path)
return info_plist_data['CFBundleIdentifier']
def _InstallEntitlements(self, entitlements, substitutions, overrides):
"""Generates and install the ${BundleName}.xcent entitlements file.
Expands variables "$(variable)" pattern in the source entitlements file,
add extra entitlements defined in the .mobileprovision file and the copy
the generated plist to "${BundlePath}.xcent".
Args:
entitlements: string, optional, path to the Entitlements.plist template
to use, defaults to "${SDKROOT}/Entitlements.plist"
substitutions: dictionary, variable substitutions
overrides: dictionary, values to add to the entitlements
Returns:
Path to the generated entitlements file.
"""
source_path = entitlements
target_path = os.path.join(
os.environ['BUILT_PRODUCTS_DIR'],
os.environ['PRODUCT_NAME'] + '.xcent')
if not source_path:
source_path = os.path.join(
os.environ['SDKROOT'],
'Entitlements.plist')
shutil.copy2(source_path, target_path)
data = self._LoadPlistMaybeBinary(target_path)
data = self._ExpandVariables(data, substitutions)
if overrides:
for key in overrides:
if key not in data:
data[key] = overrides[key]
plistlib.writePlist(data, target_path)
return target_path
def _ExpandVariables(self, data, substitutions):
"""Expands variables "$(variable)" in data.
Args:
data: object, can be either string, list or dictionary
substitutions: dictionary, variable substitutions to perform
Returns:
Copy of data where each references to "$(variable)" has been replaced
by the corresponding value found in substitutions, or left intact if
the key was not found.
"""
if isinstance(data, str):
for key, value in substitutions.items():
data = data.replace('$(%s)' % key, value)
return data
if isinstance(data, list):
return [self._ExpandVariables(v, substitutions) for v in data]
if isinstance(data, dict):
return {k: self._ExpandVariables(data[k], substitutions) for k in data}
return data
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))

195
build/nodeaudio.target.mk Normal file
View file

@ -0,0 +1,195 @@
# This file is generated by gyp; do not edit.
TOOLSET := target
TARGET := nodeaudio
DEFS_Debug := \
'-DNODE_GYP_MODULE_NAME=nodeaudio' \
'-DUSING_UV_SHARED=1' \
'-DUSING_V8_SHARED=1' \
'-DV8_DEPRECATION_WARNINGS=1' \
'-DV8_DEPRECATION_WARNINGS' \
'-DV8_IMMINENT_DEPRECATION_WARNINGS' \
'-D_DARWIN_USE_64_BIT_INODE=1' \
'-D_LARGEFILE_SOURCE' \
'-D_FILE_OFFSET_BITS=64' \
'-DOPENSSL_NO_PINSHARED' \
'-DOPENSSL_THREADS' \
'-DBUILDING_NODE_EXTENSION' \
'-DDEBUG' \
'-D_DEBUG' \
'-DV8_ENABLE_CHECKS'
# Flags passed to all source files.
CFLAGS_Debug := \
-O0 \
-gdwarf-2 \
-mmacosx-version-min=10.13 \
-arch x86_64 \
-Wall \
-Wendif-labels \
-W \
-Wno-unused-parameter
# Flags passed to only C files.
CFLAGS_C_Debug := \
-fno-strict-aliasing
# Flags passed to only C++ files.
CFLAGS_CC_Debug := \
-std=gnu++1y \
-stdlib=libc++ \
-fno-rtti \
-fno-exceptions \
-fno-strict-aliasing
# Flags passed to only ObjC files.
CFLAGS_OBJC_Debug :=
# Flags passed to only ObjC++ files.
CFLAGS_OBJCC_Debug :=
INCS_Debug := \
-I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/include/node \
-I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/src \
-I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/deps/openssl/config \
-I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/deps/openssl/openssl/include \
-I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/deps/uv/include \
-I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/deps/zlib \
-I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/deps/v8/include \
-I/usr/local/include
DEFS_Release := \
'-DNODE_GYP_MODULE_NAME=nodeaudio' \
'-DUSING_UV_SHARED=1' \
'-DUSING_V8_SHARED=1' \
'-DV8_DEPRECATION_WARNINGS=1' \
'-DV8_DEPRECATION_WARNINGS' \
'-DV8_IMMINENT_DEPRECATION_WARNINGS' \
'-D_DARWIN_USE_64_BIT_INODE=1' \
'-D_LARGEFILE_SOURCE' \
'-D_FILE_OFFSET_BITS=64' \
'-DOPENSSL_NO_PINSHARED' \
'-DOPENSSL_THREADS' \
'-DBUILDING_NODE_EXTENSION'
# Flags passed to all source files.
CFLAGS_Release := \
-O3 \
-gdwarf-2 \
-mmacosx-version-min=10.13 \
-arch x86_64 \
-Wall \
-Wendif-labels \
-W \
-Wno-unused-parameter
# Flags passed to only C files.
CFLAGS_C_Release := \
-fno-strict-aliasing
# Flags passed to only C++ files.
CFLAGS_CC_Release := \
-std=gnu++1y \
-stdlib=libc++ \
-fno-rtti \
-fno-exceptions \
-fno-strict-aliasing
# Flags passed to only ObjC files.
CFLAGS_OBJC_Release :=
# Flags passed to only ObjC++ files.
CFLAGS_OBJCC_Release :=
INCS_Release := \
-I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/include/node \
-I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/src \
-I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/deps/openssl/config \
-I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/deps/openssl/openssl/include \
-I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/deps/uv/include \
-I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/deps/zlib \
-I/Users/adgundersen/Library/Caches/node-gyp/14.17.0/deps/v8/include \
-I/usr/local/include
OBJS := \
$(obj).target/$(TARGET)/src/nodeaudio.o \
$(obj).target/$(TARGET)/src/audio_handler.o \
$(obj).target/$(TARGET)/src/frame_buffer.o
# Add to the list of files we specially track dependencies for.
all_deps += $(OBJS)
# CFLAGS et al overrides must be target-local.
# See "Target-specific Variable Values" in the GNU Make manual.
$(OBJS): TOOLSET := $(TOOLSET)
$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE))
$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE))
$(OBJS): GYP_OBJCFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(CFLAGS_OBJC_$(BUILDTYPE))
$(OBJS): GYP_OBJCXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(CFLAGS_OBJCC_$(BUILDTYPE))
# Suffix rules, putting all outputs into $(obj).
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.c FORCE_DO_CMD
@$(call do_cmd,cc,1)
# Try building from generated source, too.
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD
@$(call do_cmd,cc,1)
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.c FORCE_DO_CMD
@$(call do_cmd,cc,1)
# End of this set of suffix rules
### Rules for final target.
LDFLAGS_Debug := \
-undefined dynamic_lookup \
-Wl,-no_pie \
-Wl,-search_paths_first \
-mmacosx-version-min=10.13 \
-arch x86_64 \
-L$(builddir) \
-stdlib=libc++
LIBTOOLFLAGS_Debug := \
-undefined dynamic_lookup \
-Wl,-no_pie \
-Wl,-search_paths_first
LDFLAGS_Release := \
-undefined dynamic_lookup \
-Wl,-no_pie \
-Wl,-search_paths_first \
-mmacosx-version-min=10.13 \
-arch x86_64 \
-L$(builddir) \
-stdlib=libc++
LIBTOOLFLAGS_Release := \
-undefined dynamic_lookup \
-Wl,-no_pie \
-Wl,-search_paths_first
LIBS := \
-lportaudio
$(builddir)/nodeaudio.node: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))
$(builddir)/nodeaudio.node: LIBS := $(LIBS)
$(builddir)/nodeaudio.node: GYP_LIBTOOLFLAGS := $(LIBTOOLFLAGS_$(BUILDTYPE))
$(builddir)/nodeaudio.node: TOOLSET := $(TOOLSET)
$(builddir)/nodeaudio.node: $(OBJS) FORCE_DO_CMD
$(call do_cmd,solink_module)
all_deps += $(builddir)/nodeaudio.node
# Add target alias
.PHONY: nodeaudio
nodeaudio: $(builddir)/nodeaudio.node
# Short alias for building this executable.
.PHONY: nodeaudio.node
nodeaudio.node: $(builddir)/nodeaudio.node
# Add executable to "all" target.
.PHONY: all
all: $(builddir)/nodeaudio.node

97
node_modules/bindings/README.md generated vendored Normal file
View file

@ -0,0 +1,97 @@
node-bindings
=============
### Helper module for loading your native module's .node file
This is a helper module for authors of Node.js native addon modules.
It is basically the "swiss army knife" of `require()`ing your native module's
`.node` file.
Throughout the course of Node's native addon history, addons have ended up being
compiled in a variety of different places, depending on which build tool and which
version of node was used. To make matters worse, now the _gyp_ build tool can
produce either a _Release_ or _Debug_ build, each being built into different
locations.
This module checks _all_ the possible locations that a native addon would be built
at, and returns the first one that loads successfully.
Installation
------------
Install with `npm`:
``` bash
$ npm install bindings
```
Or add it to the `"dependencies"` section of your _package.json_ file.
Example
-------
`require()`ing the proper bindings file for the current node version, platform
and architecture is as simple as:
``` js
var bindings = require('bindings')('binding.node')
// Use your bindings defined in your C files
bindings.your_c_function()
```
Nice Error Output
-----------------
When the `.node` file could not be loaded, `node-bindings` throws an Error with
a nice error message telling you exactly what was tried. You can also check the
`err.tries` Array property.
```
Error: Could not load the bindings file. Tried:
→ /Users/nrajlich/ref/build/binding.node
→ /Users/nrajlich/ref/build/Debug/binding.node
→ /Users/nrajlich/ref/build/Release/binding.node
→ /Users/nrajlich/ref/out/Debug/binding.node
→ /Users/nrajlich/ref/Debug/binding.node
→ /Users/nrajlich/ref/out/Release/binding.node
→ /Users/nrajlich/ref/Release/binding.node
→ /Users/nrajlich/ref/build/default/binding.node
→ /Users/nrajlich/ref/compiled/0.8.2/darwin/x64/binding.node
at bindings (/Users/nrajlich/ref/node_modules/bindings/bindings.js:84:13)
at Object.<anonymous> (/Users/nrajlich/ref/lib/ref.js:5:47)
at Module._compile (module.js:449:26)
at Object.Module._extensions..js (module.js:467:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
...
```
License
-------
(The MIT License)
Copyright (c) 2012 Nathan Rajlich &lt;nathan@tootallnate.net&gt;
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

166
node_modules/bindings/bindings.js generated vendored Normal file
View file

@ -0,0 +1,166 @@
/**
* Module dependencies.
*/
var fs = require('fs')
, path = require('path')
, join = path.join
, dirname = path.dirname
, exists = fs.existsSync || path.existsSync
, defaults = {
arrow: process.env.NODE_BINDINGS_ARROW || ' → '
, compiled: process.env.NODE_BINDINGS_COMPILED_DIR || 'compiled'
, platform: process.platform
, arch: process.arch
, version: process.versions.node
, bindings: 'bindings.node'
, try: [
// node-gyp's linked version in the "build" dir
[ 'module_root', 'build', 'bindings' ]
// node-waf and gyp_addon (a.k.a node-gyp)
, [ 'module_root', 'build', 'Debug', 'bindings' ]
, [ 'module_root', 'build', 'Release', 'bindings' ]
// Debug files, for development (legacy behavior, remove for node v0.9)
, [ 'module_root', 'out', 'Debug', 'bindings' ]
, [ 'module_root', 'Debug', 'bindings' ]
// Release files, but manually compiled (legacy behavior, remove for node v0.9)
, [ 'module_root', 'out', 'Release', 'bindings' ]
, [ 'module_root', 'Release', 'bindings' ]
// Legacy from node-waf, node <= 0.4.x
, [ 'module_root', 'build', 'default', 'bindings' ]
// Production "Release" buildtype binary (meh...)
, [ 'module_root', 'compiled', 'version', 'platform', 'arch', 'bindings' ]
]
}
/**
* The main `bindings()` function loads the compiled bindings for a given module.
* It uses V8's Error API to determine the parent filename that this function is
* being invoked from, which is then used to find the root directory.
*/
function bindings (opts) {
// Argument surgery
if (typeof opts == 'string') {
opts = { bindings: opts }
} else if (!opts) {
opts = {}
}
opts.__proto__ = defaults
// Get the module root
if (!opts.module_root) {
opts.module_root = exports.getRoot(exports.getFileName())
}
// Ensure the given bindings name ends with .node
if (path.extname(opts.bindings) != '.node') {
opts.bindings += '.node'
}
var tries = []
, i = 0
, l = opts.try.length
, n
, b
, err
for (; i<l; i++) {
n = join.apply(null, opts.try[i].map(function (p) {
return opts[p] || p
}))
tries.push(n)
try {
b = opts.path ? require.resolve(n) : require(n)
if (!opts.path) {
b.path = n
}
return b
} catch (e) {
if (!/not find/i.test(e.message)) {
throw e
}
}
}
err = new Error('Could not locate the bindings file. Tried:\n'
+ tries.map(function (a) { return opts.arrow + a }).join('\n'))
err.tries = tries
throw err
}
module.exports = exports = bindings
/**
* Gets the filename of the JavaScript file that invokes this function.
* Used to help find the root directory of a module.
* Optionally accepts an filename argument to skip when searching for the invoking filename
*/
exports.getFileName = function getFileName (calling_file) {
var origPST = Error.prepareStackTrace
, origSTL = Error.stackTraceLimit
, dummy = {}
, fileName
Error.stackTraceLimit = 10
Error.prepareStackTrace = function (e, st) {
for (var i=0, l=st.length; i<l; i++) {
fileName = st[i].getFileName()
if (fileName !== __filename) {
if (calling_file) {
if (fileName !== calling_file) {
return
}
} else {
return
}
}
}
}
// run the 'prepareStackTrace' function above
Error.captureStackTrace(dummy)
dummy.stack
// cleanup
Error.prepareStackTrace = origPST
Error.stackTraceLimit = origSTL
return fileName
}
/**
* Gets the root directory of a module, given an arbitrary filename
* somewhere in the module tree. The "root directory" is the directory
* containing the `package.json` file.
*
* In: /home/nate/node-native-module/lib/index.js
* Out: /home/nate/node-native-module
*/
exports.getRoot = function getRoot (file) {
var dir = dirname(file)
, prev
while (true) {
if (dir === '.') {
// Avoids an infinite loop in rare cases, like the REPL
dir = process.cwd()
}
if (exists(join(dir, 'package.json')) || exists(join(dir, 'node_modules'))) {
// Found the 'package.json' file or 'node_modules' dir; we're done
return dir
}
if (prev === dir) {
// Got to the top
throw new Error('Could not find module root given file: "' + file
+ '". Do you have a `package.json` file? ')
}
// Try the parent dir next
prev = dir
dir = join(dir, '..')
}
}

29
node_modules/bindings/package.json generated vendored Normal file
View file

@ -0,0 +1,29 @@
{
"name": "bindings",
"description": "Helper module for loading your native module's .node file",
"keywords": [
"native",
"addon",
"bindings",
"gyp",
"waf",
"c",
"c++"
],
"version": "1.2.1",
"author": "Nathan Rajlich <nathan@tootallnate.net> (http://tootallnate.net)",
"repository": {
"type": "git",
"url": "git://github.com/TooTallNate/node-bindings.git"
},
"main": "./bindings.js",
"bugs": {
"url": "https://github.com/TooTallNate/node-bindings/issues"
},
"homepage": "https://github.com/TooTallNate/node-bindings",
"license": "MIT"
,"_resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz"
,"_integrity": "sha1-FK1hE4EtLTfXLme0ystLtyZQXxE="
,"_from": "bindings@1.2.1"
}

13
package-lock.json generated Normal file
View file

@ -0,0 +1,13 @@
{
"name": "function_arguments",
"version": "0.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"bindings": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz",
"integrity": "sha1-FK1hE4EtLTfXLme0ystLtyZQXxE="
}
}
}

14
package.json Normal file
View file

@ -0,0 +1,14 @@
{
"name": "function_arguments",
"version": "0.0.0",
"description": "Node.js Addons Example #2",
"main": "binding.js",
"private": true,
"dependencies": {
"bindings": "~1.2.1"
},
"scripts": {
"test": "node binding.js"
},
"gypfile": true
}

BIN
src/.DS_Store vendored Normal file

Binary file not shown.

BIN
src/a.out Executable file

Binary file not shown.

259
src/audio_handler.c Normal file
View file

@ -0,0 +1,259 @@
#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <portaudio.h>
#include "audio_handler.h"
#include "frame_buffer.h"
bool sys_on;
bool recording = false;
int write_to_o_buff = 0;
bool initialized = false;
frame_buffer inputFrameBuffer;
frame_buffer outputFrameBuffer;
typedef short SAMPLE;
static int min_int (int a, int b) {
return (a < b) ? a : b;
}
#define SAMPLE_RATE (16000)
#define BUFFER_mSECS (30)
#define FRAMES_PER_BUFFER (160 * (BUFFER_mSECS/10))
/* input -> inputFrameBuffer */
static int input_callback( const void* input,
void* output,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void* frameBuffer )
{
(void) output;
char *i_buf = (char *) input;
frame_buffer* fb = (frame_buffer*) frameBuffer;
if (!recording) {
rewrite_frames_fb( fb,
i_buf,
framesPerBuffer*sizeof(SAMPLE) );
} else {
rewrite_frames_fb( fb,
i_buf,
framesPerBuffer*sizeof(SAMPLE) );
}
return paContinue;
}
/* outputFrameBuffer -> output */
static int output_callback( const void *input,
void* output,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void* frameBuffer )
{
(void) input;
char* o_buf = (char *) output;
frame_buffer* fb = (frame_buffer*) frameBuffer;
if (write_to_o_buff == 0) {
write_to_o_buff = 2;
if (fb->frames_used > 0) {
int frames_to_read = min_int( framesPerBuffer,
fb->frames_used );
/* read each frame in outputFrameBuffer and write to output */
for (int i = 0; i < frames_to_read; i++) {
*((SAMPLE *)o_buf) = *((SAMPLE *) read_frame_at_cursor_fb(fb));
o_buf += sizeof(SAMPLE);
}
} else {
*((SAMPLE *)o_buf) = 0;
}
write_to_o_buff = 0;
}
return paContinue;
}
static void setParameters( PaDeviceIndex device,
PaStreamParameters* parameters )
{
parameters->device = Pa_GetDefaultInputDevice();
parameters->channelCount = 1;
parameters->hostApiSpecificStreamInfo = NULL;
parameters->suggestedLatency = Pa_GetDeviceInfo(device)->defaultHighInputLatency;
}
static PaError set_stream( int type, // 0 = input, 1 = output
PaStream* stream,
PaDeviceIndex device,
PaStreamCallback* callback,
void* frameBuffer )
{
PaError err;
PaStreamParameters streamParameters;
setParameters(device, &streamParameters);
if (type == 0) {
err = Pa_OpenStream( &stream,
NULL,
&streamParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff,
callback,
frameBuffer );
} else {
err = Pa_OpenStream( &stream,
&streamParameters,
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff,
callback,
frameBuffer );
}
if (err != paNoError) return err;
err = Pa_StartStream(stream);
return err;
}
void AH_initialize()
{
PaError err = paNoError;
PaDeviceIndex inputDevice;
PaDeviceIndex outputDevice;
PaDeviceIndex defaultOutputDevice;
PaDeviceIndex defaultInputDevice;
PaStream* inputStream = NULL;
PaStream* outputStream = NULL;
init_fb(&inputFrameBuffer);
init_fb(&outputFrameBuffer);
sys_on = true;
while (sys_on) {
Pa_Initialize();
defaultInputDevice = Pa_GetDefaultInputDevice();
defaultOutputDevice = Pa_GetDefaultOutputDevice();
printf("Input: %i, Output: %i\n", defaultInputDevice, defaultOutputDevice);
if (inputDevice != defaultInputDevice) {
inputDevice = defaultInputDevice;
err = set_stream( 0,
inputStream,
inputDevice,
&input_callback,
&inputFrameBuffer );
}
if (outputDevice != defaultOutputDevice) {
outputDevice = defaultOutputDevice;
err = set_stream( 1,
outputStream,
outputDevice,
&output_callback,
&outputFrameBuffer );
}
sleep(1);
}
Pa_AbortStream(inputStream);
Pa_AbortStream(outputStream);
delete_fb(&inputFrameBuffer);
delete_fb(&outputFrameBuffer);
Pa_Terminate();
initialized = false;
}
void AH_terminate()
{
printf("Terminating PA\n");
sys_on = false;
while(initialized) {
struct timespec t;
t.tv_sec = 0;
t.tv_nsec = 5000;
nanosleep(&t, &t);
}
}
void AH_fillInputBuffer()
{
recording = true;
}
void AH_resetInputBuffer(void* data, size_t* byte_length)
{
size_t len = sizeof(inputFrameBuffer.data);
char audioData[len];
data = audioData; // points to 0th el of audioData
byte_length = &len;
for(int i = 0; i < (int) len; i++) {
audioData[i] = inputFrameBuffer.data[i];
}
recording = false;
clear_fb(&inputFrameBuffer);
}
void AH_feedOutputBuffer(void* data, size_t* byte_length)
{
// can't intert into buffer while it's being read from
while (write_to_o_buff == 2) {
struct timespec t;
t.tv_sec = 0;
t.tv_nsec = 5000;
nanosleep(&t, &t);
}
write_to_o_buff = 1;
insert_frames_fb(&outputFrameBuffer, data, (int) &byte_length);
write_to_o_buff = 0;
}

16
src/audio_handler.h Normal file
View file

@ -0,0 +1,16 @@
#ifndef AUDIO_HANDLER
#define AUDIO_HANDLER
void AH_initialize ();
void AH_fillInputBuffer ();
void AH_resetInputBuffer(void* data, size_t* byte_length);
void AH_feedOutputBuffer(void* data, size_t* byte_length);
void AH_terminate ();
#endif

BIN
src/audio_handler.o Normal file

Binary file not shown.

120
src/frame_buffer.c Normal file
View file

@ -0,0 +1,120 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "frame_buffer.h"
#define BYTES_PER_FRAME (2)
#define INITIAL_FRAME_COUNT (480*100)
#define ALPHA (2)
void init_fb ( frame_buffer *fb ) {
fb->frames_used = 0;
fb->frames_free = INITIAL_FRAME_COUNT;
fb->data = (char *) malloc(BYTES_PER_FRAME*INITIAL_FRAME_COUNT);
fb->cursor = fb->data;
}
void delete_fb ( frame_buffer *fb ) {
fb->frames_used = 0;
fb->frames_free = 0;
fb->cursor = NULL;
free(fb->data);
}
void clear_fb ( frame_buffer *fb ) {
free(fb->data);
fb->data = (char *) malloc(BYTES_PER_FRAME*INITIAL_FRAME_COUNT);
fb->cursor = fb->data;
fb->frames_free = INITIAL_FRAME_COUNT;
fb->frames_used = 0;
}
void insert_frames_fb ( frame_buffer *fb, char *frame_data, int frames_len ) {
int frames = frames_len / BYTES_PER_FRAME;
int cursor_diff = fb->cursor - fb->data;
int data_len = (fb->frames_free+fb->frames_used)*BYTES_PER_FRAME;
if (frames > fb->frames_free) {
fb->data = realloc(fb->data, (data_len+frames_len)*ALPHA);
fb->cursor = fb->data+cursor_diff;
if (fb->frames_used*BYTES_PER_FRAME > data_len-cursor_diff) {
memmove(fb->data+data_len, fb->data, (fb->frames_used*BYTES_PER_FRAME)-(data_len-cursor_diff));
}
fb->frames_used += frames;
fb->frames_free = fb->frames_used;
}
data_len = (fb->frames_free+fb->frames_used)*BYTES_PER_FRAME;
if (fb->frames_used*BYTES_PER_FRAME > data_len-cursor_diff) {
memcpy(fb->data+((fb->frames_used*BYTES_PER_FRAME)-(data_len-cursor_diff)), frame_data, frames_len);
fb->frames_free -= frames;
fb->frames_used += frames;
return;
}
if (frames_len+(fb->frames_used*BYTES_PER_FRAME) > data_len-cursor_diff) {
int non_overlap = data_len - (cursor_diff+(fb->frames_used*BYTES_PER_FRAME));
int overlap = frames_len - non_overlap;
memcpy(fb->cursor+(fb->frames_used*BYTES_PER_FRAME), frame_data, non_overlap);
memcpy(fb->data, &(frame_data[non_overlap]), overlap);
fb->frames_free -= frames;
fb->frames_used += frames;
return;
}
memcpy(fb->cursor+(fb->frames_used*BYTES_PER_FRAME), frame_data, frames_len);
fb->frames_free -= frames;
fb->frames_used += frames;
}
void rewrite_frames_fb ( frame_buffer *fb, char *frame_data, int frames_len ) {
int frames = frames_len / BYTES_PER_FRAME;
if (frames > (fb->frames_used + fb->frames_free)) {
fb->data = realloc(fb->data, frames_len*ALPHA);
fb->frames_free = frames;
fb->frames_used = frames;
} else {
fb->frames_free = fb->frames_free + fb->frames_used - frames;
fb->frames_used = frames;
}
memcpy(fb->data, frame_data, frames_len);
fb->cursor = fb->data;
}
char* read_frame_at_cursor_fb ( frame_buffer *fb ) {
char *begin = fb->cursor;
int frames_free = fb->frames_free;
int frames_used = fb->frames_used;
char *data = fb->data;
if (frames_used == 0) {
return NULL;
}
fb->cursor += BYTES_PER_FRAME;
if (fb->cursor >= data + ((frames_free+frames_used)*BYTES_PER_FRAME)) {
fb->cursor = data;
}
fb->frames_free += 1;
fb->frames_used -= 1;
return begin;
}

24
src/frame_buffer.h Normal file
View file

@ -0,0 +1,24 @@
#ifndef FRAME_BUFFER
#define FRAME_BUFFER
typedef struct {
int frames_free, frames_used;
char *data, *cursor;
} frame_buffer;
void init_fb ( frame_buffer *fb );
void delete_fb ( frame_buffer *fb );
void clear_fb ( frame_buffer *fb );
void insert_frames_fb ( frame_buffer *fb, char *frame_data, int frames_len );
void rewrite_frames_fb ( frame_buffer *fb, char *frame_data, int frames_len );
char* read_frame_at_cursor_fb ( frame_buffer *fb );
#endif

BIN
src/frame_buffer.o Normal file

Binary file not shown.

26
src/makefile Normal file
View file

@ -0,0 +1,26 @@
IDIR =../include
CC=gcc
CFLAGS=-I$(IDIR)
ODIR=obj
LDIR =-L/usr/local/lib -L/usr/lib
LIBS=-lportaudio
_DEPS = audio_handler.h frame_buffer.h
DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS))
_OBJ = audio_handler.o frame_buffer.o
OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ))
$(ODIR)/%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)
nodeaudio: $(OBJ)
$(CC) -o $@ $^ $(CFLAGS) $(LDIR) $(LIBS)
.PHONY: clean
clean:
rm -f $(ODIR)/*.o *~ core $(INCDIR)/*~

BIN
src/nodeaudio Executable file

Binary file not shown.

90
src/nodeaudio.c Normal file
View file

@ -0,0 +1,90 @@
#include <assert.h>
#include <node_api.h>
#include <stdio.h>
#include "audio_handler.h"
#define DECLARE_NAPI_METHOD(name, func) { name, 0, func, 0, 0, 0, napi_default, 0 }
napi_value Initialize(napi_env env, napi_callback_info info)
{
(void) info;
AH_initialize();
return NULL;
}
napi_value FillInputBuffer(napi_env env, napi_callback_info info)
{
(void) info;
AH_fillInputBuffer();
return NULL;
}
napi_value ReturnInputBuffer(napi_env env, napi_callback_info info)
{
(void) info;
napi_status status;
void* data;
size_t byte_length;
AH_resetInputBuffer(&data, &byte_length);
napi_value buffer;
status = napi_create_arraybuffer( env,
byte_length,
&data,
&buffer );
assert(status == napi_ok);
return buffer;
}
static napi_value FeedOutputBuffer(napi_env env, napi_callback_info info)
{
napi_status status;
void* data;
size_t byte_length;
napi_value arraybuffer;
size_t argc = 1; /* parse args */
napi_value args[1];
status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
assert(status == napi_ok);
arraybuffer = args[0];
status = napi_get_arraybuffer_info( env,
arraybuffer,
&data,
&byte_length );
assert(status == napi_ok);
AH_feedOutputBuffer(data, &byte_length);
return NULL;
}
napi_value Init(napi_env env, napi_value exports)
{
napi_status status;
napi_property_descriptor desc[] = {
DECLARE_NAPI_METHOD("fill_input_buffer", FillInputBuffer),
DECLARE_NAPI_METHOD("return_input_buffer", ReturnInputBuffer),
DECLARE_NAPI_METHOD("feed_output_buffer", FeedOutputBuffer)
};
status = napi_define_properties(env, exports, 1, desc);
AH_initialize();
return exports;
}
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

44
src/test.c Normal file
View file

@ -0,0 +1,44 @@
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
// #include "audio_handler.h"
#include <portaudio.h>
// int main() {
// printf("PA init test.\n");
// pthread_t in_th;
// printf("Creating audio thread.\n");
// pthread_create(&in_th, NULL, (void *) AH_initialize, NULL);
// sleep(10);
// AH_terminate();
// return 0;
// }
int main ()
{
printf("Initializing portaudio\n");
Pa_Initialize();
for (int i = 0; i < 10; ++i)
{
PaHostApiIndex hostApi = Pa_GetDefaultHostApi();
const PaHostApiInfo* hostApiInfo = Pa_GetHostApiInfo(hostApi);
printf("Default input id: %i\n", hostApiInfo->defaultInputDevice);
sleep(2);
}
Pa_Terminate();
return 0;
}