diff --git a/.gitignore b/.gitignore index aecd59fd31b47a426d32933287a4ecd426a80fd4..591570d7d1ee31359a5560c062f5f67af96f5feb 100644 --- a/.gitignore +++ b/.gitignore @@ -3,17 +3,21 @@ # Compiler output Unix IVAS_cod IVAS_dec +IVAS_rend +IVAS_crend_unit_test obj/ *.a *.o *.P # default CMake -build/**/* +build*/**/* # Compiler output VS2017 IVAS_cod.exe IVAS_dec.exe +IVAS_rend.exe +IVAS_crend_unit_test.exe *.user .vs/ Debug_*/ @@ -31,13 +35,10 @@ scripts/ivas_pytests/tests/unit_tests/crend/Release_*/ scripts/td_object_renderer/object_renderer_standalone/renderer_standalone scripts/td_object_renderer/object_renderer_standalone/renderer_standalone.exe -# Prerenderer -scripts/prerenderer/IVAS_prerenderer -scripts/prerenderer/IVAS_prerenderer.exe - # General/scripts .DS_Store .vscode +.cache *.log *.bak scripts/c-code_instrument/ @@ -46,6 +47,8 @@ scripts/ref/ scripts/test/ scripts/out/ scripts/self_test_summary.txt +tests/renderer/cut +tests/renderer/ref tests/dut tests/ref @@ -53,3 +56,6 @@ tests/ref __pycache__/ *.py[cod] *$py.class + +#history +.history/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1ad216d9eae807d5029631bbbe234f2b9fdc19fd..bfe23c7fe711d5ce240794926ef575c1eba8281b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -45,8 +45,21 @@ stages: .get-previous-merge-commit-sha: &get-previous-merge-commit-sha - previous_merge_commit=$(git --no-pager log --merges HEAD~1 -n 1 --pretty=format:%H) -.merge_request_comparison_setup: &merge_request_comparison_setup - ### build test binaries, initial clean for paranoia reasons +.mr-fetch-target-branch: &mr-fetch-target-branch + # first delete local target branch to avoid conflicts when branch is cached and there are merge conflicts during fetching + # depending on chaching, the branch may not be there, so prevent failure of this command -> should maybe be done smarter later + - git branch -D $CI_MERGE_REQUEST_TARGET_BRANCH_NAME || true + # needed when depth is lower than the number of commits in the branch + - git fetch origin $CI_MERGE_REQUEST_TARGET_BRANCH_NAME:$CI_MERGE_REQUEST_TARGET_BRANCH_NAME + +.mr-get-target-commit: &mr-get-target-commit + # compare to last target branch commit before pipeline was created + - target_commit=$(git log $CI_MERGE_REQUEST_TARGET_BRANCH_NAME -1 --oneline --before=${CI_PIPELINE_CREATED_AT} --format=%H) + + + +.merge-request-comparison-setup-codec: + &merge-request-comparison-setup-codec ### build test binaries, initial clean for paranoia reasons - make clean - mkdir build - cd build @@ -54,6 +67,7 @@ stages: - make -j - mv IVAS_cod ../IVAS_cod_test - mv IVAS_dec ../IVAS_dec_test + - mv IVAS_rend .. - cd .. - rm -rf build/* @@ -61,14 +75,9 @@ stages: - source_branch_commit_sha=$(git rev-parse HEAD) ### checkout version to compare against - # first delete local target branch to avoid conflicts when branch is cached and there are merge conflicts during fetching - # depending on chaching, the branch may not be there, so prevent failure of this command -> should maybe be done smarter later - - git branch -D $CI_MERGE_REQUEST_TARGET_BRANCH_NAME || true - # needed when depth is lower than the number of commits in the branch - - git fetch origin $CI_MERGE_REQUEST_TARGET_BRANCH_NAME:$CI_MERGE_REQUEST_TARGET_BRANCH_NAME + - *mr-fetch-target-branch - ### compare to last target branch commit before pipeline was created - - target_commit=$(git log $CI_MERGE_REQUEST_TARGET_BRANCH_NAME -1 --oneline --before=${CI_PIPELINE_CREATED_AT} --format=%H) + - *mr-get-target-commit - git checkout $target_commit ### build reference binaries @@ -84,10 +93,10 @@ stages: - mv IVAS_dec_test IVAS_dec .merge-request-comparison-check: &merge-request-comparison-check - - if [ $zero_errors != 1 ]; then echo "Run errors encountered!"; exit $EXIT_CODE_FAIL; fi - - if [ $exit_code -eq 1 ] && [ $non_be_flag == 0 ]; then echo "Non-bitexact cases without non-BE tag encountered!"; exit $EXIT_CODE_FAIL; fi - - if [ $exit_code -eq 1 ] && [ $non_be_flag != 0 ]; then echo "Non-bitexact cases with non-BE tag encountered"; exit $EXIT_CODE_NON_BE; fi - - exit 0 + - if [ $zero_errors != 1 ]; then echo "Run errors encountered!"; exit $EXIT_CODE_FAIL; fi + - if [ $exit_code -eq 1 ] && [ $non_be_flag == 0 ]; then echo "Non-bitexact cases without non-BE tag encountered!"; exit $EXIT_CODE_FAIL; fi + - if [ $exit_code -eq 1 ] && [ $non_be_flag != 0 ]; then echo "Non-bitexact cases with non-BE tag encountered"; exit $EXIT_CODE_NON_BE; fi + - exit 0 .update-ltv-repo: &update-ltv-repo - cd $LTV_DIR @@ -181,7 +190,7 @@ check-if-branch-is-up-to-date-with-main: # --------------------------------------------------------------- build-codec-linux-make: - extends: + extends: - .build-job-with-check-for-warnings - .rules-basis script: @@ -191,7 +200,7 @@ build-codec-linux-make: - ci/check_for_warnings.py $BUILD_OUTPUT || exit $? build-unittests-linux: - extends: + extends: - .build-job-with-check-for-warnings - .rules-basis script: @@ -200,18 +209,8 @@ build-unittests-linux: # need to use the "|| exit $?" suffix to get the allowed_failure return code, otherwise the job fails with code 1...< - ci/check_for_warnings.py $BUILD_OUTPUT || exit $? -build-prerenderer-linux: - extends: - - .build-job-with-check-for-warnings - - .rules-basis - script: - - *print-common-info - - make -C scripts/prerenderer -j 2>&1 | tee $BUILD_OUTPUT - # need to use the "|| exit $?" suffix to get the allowed_failure return code, otherwise the job fails with code 1...< - - ci/check_for_warnings.py $BUILD_OUTPUT || exit $? - build-td-object-renderer-standalone-linux: - extends: + extends: - .build-job-with-check-for-warnings - .rules-basis script: @@ -221,7 +220,7 @@ build-td-object-renderer-standalone-linux: - ci/check_for_warnings.py $BUILD_OUTPUT || exit $? build-codec-linux-cmake: - extends: + extends: - .build-job-with-check-for-warnings - .rules-basis script: @@ -235,7 +234,7 @@ build-codec-linux-cmake: - ci/check_for_warnings.py $BUILD_OUTPUT || exit $? build-codec-instrumented-linux: - extends: + extends: - .build-job-linux - .rules-basis script: @@ -244,26 +243,25 @@ build-codec-instrumented-linux: # make sure that the codec builds with msan, asan and usan build-codec-sanitizers-linux: - extends: + extends: - .build-job-linux - .rules-basis script: - *print-common-info - bash ci/build_codec_sanitizers_linux.sh - # --------------------------------------------------------------- # Test jobs for merge requests # --------------------------------------------------------------- # test that runs all modes with 1s input signals codec-smoke-test: - extends: + extends: - .test-job-linux-needs-testv-dir - .rules-merge-request timeout: "5 minutes" stage: test - needs: [ "build-codec-linux-cmake" ] + needs: ["build-codec-linux-cmake"] script: - *print-common-info - bash ci/smoke_test.sh @@ -276,16 +274,15 @@ codec-smoke-test: - out/logs/ - smoke_test_output.txt - smoke_test_output_plc.txt - expose_as: 'Smoke test results' - + expose_as: "Smoke test results" # code selftest testvectors with memory-sanitizer binaries msan-on-merge-request-linux: - extends: + extends: - .test-job-linux - .rules-merge-request stage: test - needs: [ "build-codec-sanitizers-linux" ] + needs: ["build-codec-sanitizers-linux"] script: - *print-common-info - make clean @@ -298,16 +295,15 @@ msan-on-merge-request-linux: paths: - scripts/ref/logs/ - test_output.txt - expose_as: 'Msan selftest results' - + expose_as: "Msan selftest results" # code selftest testvectors with address-sanitizer binaries asan-on-merge-request-linux: - extends: + extends: - .test-job-linux - .rules-merge-request stage: test - needs: [ "build-codec-sanitizers-linux" ] + needs: ["build-codec-sanitizers-linux"] script: - *print-common-info - make clean @@ -320,20 +316,161 @@ asan-on-merge-request-linux: paths: - scripts/ref/logs/ - test_output.txt - expose_as: 'Asan selftest results' + expose_as: "Asan selftest results" + +# test external renderer executable +external-renderer-make-pytest: + extends: + - .test-job-linux + - .rules-merge-request + needs: ["build-codec-linux-make"] + stage: test + script: + - make -j IVAS_rend + - make -j unittests + - make -j --directory scripts/td_object_renderer/object_renderer_standalone + - python3 -m pytest -q --log-level ERROR -n auto -rA --junit-xml=report-junit.xml tests/renderer/test_renderer.py + artifacts: + name: "mr-$CI_MERGE_REQUEST_IID--sha-$CI_COMMIT_SHORT_SHA--job-$CI_JOB_NAME--results" + when: always + paths: + - report-junit.xml + expose_as: "external renderer make pytest results" + reports: + junit: + - report-junit.xml + +# test external renderer executable with cmake + asan +external-renderer-cmake-asan-pytest: + extends: + - .test-job-linux + - .rules-merge-request + needs: ["build-codec-linux-cmake"] + stage: test + script: + - python3 ci/disable_ram_counting.py + - cmake -B cmake-build -G "Unix Makefiles" -DCLANG=asan -DCOPY_EXECUTABLES_FROM_BUILD_DIR=true + - cmake --build cmake-build -- -j + - python3 -m pytest -q --log-level ERROR -n auto -rA --junit-xml=report-junit.xml tests/renderer/test_renderer.py + artifacts: + name: "mr-$CI_MERGE_REQUEST_IID--sha-$CI_COMMIT_SHORT_SHA--job-$CI_JOB_NAME--results" + when: always + paths: + - report-junit.xml + expose_as: "external renderer cmake asan pytest results" + reports: + junit: + - report-junit.xml + +# test external renderer executable with cmake + msan +external-renderer-cmake-msan-pytest: + extends: + - .test-job-linux + - .rules-merge-request + needs: ["build-codec-linux-cmake"] + stage: test + script: + - python3 ci/disable_ram_counting.py + - cmake -B cmake-build -G "Unix Makefiles" -DCLANG=msan -DCOPY_EXECUTABLES_FROM_BUILD_DIR=true + - cmake --build cmake-build -- -j + - python3 -m pytest -q --log-level ERROR -n auto -rA --junit-xml=report-junit.xml tests/renderer/test_renderer.py + artifacts: + name: "mr-$CI_MERGE_REQUEST_IID--sha-$CI_COMMIT_SHORT_SHA--job-$CI_JOB_NAME--results" + when: always + paths: + - report-junit.xml + expose_as: "external renderer cmake msan pytest results" + reports: + junit: + - report-junit.xml + +# test external renderer executable with cmake vs decoder renderer +# TODO @tmu @knj @sgi -> converted to script, decide whether to re-enable later +.external-renderer-cmake-vs-decoder-pytest: + extends: + - .test-job-linux + - .rules-merge-request + needs: ["build-codec-linux-cmake"] + stage: test + script: + - cmake -B cmake-build -G "Unix Makefiles" -DCOPY_EXECUTABLES_FROM_BUILD_DIR=true -DDEC_TO_REND_FLOAT_DUMP=true + - cmake --build cmake-build -- -j + - python3 -m pytest -q --log-level ERROR -n 1 -rA --junit-xml=report-junit.xml tests/renderer/test_renderer_vs_decoder.py + artifacts: + name: "mr-$CI_MERGE_REQUEST_IID--sha-$CI_COMMIT_SHORT_SHA--job-$CI_JOB_NAME--results" + when: always + paths: + - report-junit.xml + expose_as: "external renderer cmake vs decoder results" + reports: + junit: + - report-junit.xml + +# compare external renderer bitexactness between target and source branch +# TODO: to be tested after first merge +.external-renderer-pytest-on-merge-request: + extends: + - .test-job-linux + - .rules-merge-request + needs: ["build-codec-linux-make"] + # TODO: set reasonable timeout, will most likely take less + timeout: "20 minutes" + stage: compare + script: + - *print-common-info + + # some helper variables - "|| true" to prevent failures from grep not finding anything + - non_be_flag=$(echo $CI_MERGE_REQUEST_TITLE | grep -c --ignore-case "\[rend(erer)*[ -]*non[ -]*be\]") || true + # TODO: needs splitting the test between reference and cut generation + #- ref_using_main=$(echo $CI_MERGE_REQUEST_TITLE | grep -c --ignore-case "\[ref[ -]*using[ -]*main\]") || true + # store the current commit hash + - source_branch_commit_sha=$(git rev-parse HEAD) + + - *mr-fetch-target-branch + - *mr-get-target-commit + - git checkout $target_commit + + # build reference binaries + - make -j IVAS_rend + - mv IVAS_rend IVAS_rend_ref + + # back to source branch + - git checkout $source_branch_commit_sha + - make clean + - make -j IVAS_rend + + # run test + - exit_code=0 + - python3 -m pytest -q --log-level ERROR -n auto -rA --junit-xml=report-junit.xml tests/renderer/test_renderer_be_comparison.py || exit_code=$? + - zero_errors=$(cat report-junit.xml | grep -c 'errors="0"') || true + + - *merge-request-comparison-check + + allow_failure: + exit_codes: + - 123 + artifacts: + name: "mr-$CI_MERGE_REQUEST_IID--sha-$CI_COMMIT_SHORT_SHA--job-$CI_JOB_NAME--results" + when: always + paths: + - report-junit.xml + expose_as: "pytest external renderer results" + reports: + junit: + - report-junit.xml # compare bit exactness between target and source branch ivas-pytest-on-merge-request: - extends: + extends: - .test-job-linux - .rules-merge-request stage: compare - needs: [ "build-codec-linux-cmake", "codec-smoke-test" ] + needs: ["build-codec-linux-cmake", "codec-smoke-test"] timeout: "10 minutes" script: - *print-common-info - - *merge_request_comparison_setup + - *merge-request-comparison-setup-codec # some helper variables - "|| true" to prevent failures from grep not finding anything - non_be_flag=$(echo $CI_MERGE_REQUEST_TITLE | grep -c --ignore-case "\[non[ -]*be\]") || true @@ -367,21 +504,21 @@ ivas-pytest-on-merge-request: when: always paths: - report-junit.xml - expose_as: 'pytest ivas results' + expose_as: "pytest ivas results" reports: junit: - report-junit.xml evs-pytest-on-merge-request: - extends: + extends: - .test-job-linux - .rules-merge-request stage: compare - needs: [ "build-codec-linux-cmake", "codec-smoke-test" ] + needs: ["build-codec-linux-cmake", "codec-smoke-test"] timeout: "10 minutes" script: - *print-common-info - - *merge_request_comparison_setup + - *merge-request-comparison-setup-codec # some helper variables - "|| true" to prevent failures from grep not finding anything - non_be_flag=$(echo $CI_MERGE_REQUEST_TITLE | grep -c --ignore-case "\[evs[ -]*non[ -]*be\]") || true @@ -412,7 +549,7 @@ evs-pytest-on-merge-request: when: always paths: - report-junit-evs.xml - expose_as: 'pytest evs results' + expose_as: "pytest evs results" reports: junit: - report-junit-evs.xml @@ -473,7 +610,7 @@ be-2-evs-linux: tags: - be-2-evs-temp stage: test - needs: [ "build-codec-linux-cmake" ] + needs: ["build-codec-linux-cmake"] timeout: "20 minutes" # To be revisited script: - *print-common-info @@ -492,13 +629,12 @@ be-2-evs-linux: - cd evs_be_test - python3 ../ci/run_evs_be_test.py - codec-comparison-on-main-push: extends: - .test-job-linux - .rules-main-push stage: compare - needs: [ "build-codec-linux-cmake" ] + needs: ["build-codec-linux-cmake"] timeout: "30 minutes" # To be revisited script: - *print-common-info @@ -508,7 +644,7 @@ codec-comparison-on-main-push: - git --no-pager diff --stat $previous_merge_commit..$latest_commit # Rest is more or less placeholder adapted from MR self test. This should be replaced with more complex tests. - + ### build test binaries, initial clean for paranoia reasons - make clean - mkdir build @@ -567,11 +703,10 @@ codec-comparison-on-main-push: when: always paths: - report-junit.xml - expose_as: 'Results of comparison to previous merge commit' + expose_as: "Results of comparison to previous merge commit" reports: junit: report-junit.xml - # --------------------------------------------------------------- # Scheduled jobs on main # --------------------------------------------------------------- @@ -742,15 +877,15 @@ sanitizer-test-planarsba: # GCOV/LCOV coverage analysis of self_test suite coverage-test-on-main-scheduled: - extends: + extends: - .test-job-linux-needs-testv-dir - - .rules-main-scheduled + - .rules-main-scheduled tags: - coverage-test stage: test rules: # only run in scheduled pipeline that passes this env vars - - if: $COVERAGE_TEST + - if: $COVERAGE_TEST script: - *print-common-info - make GCOV=1 -j @@ -779,10 +914,10 @@ pull-from-3gpp-forge: script: # Set up git LFS for mirroring (see: https://github.com/git-lfs/git-lfs/issues/1762) - git lfs install --skip-smudge --local - + # Check out mirror branch - by default the runner checks out by commit hash, which results in detached head state - git checkout $CI_COMMIT_BRANCH - + # Pull commits from upstream - git remote add upstream https://forge.3gpp.org/rep/ivas-codec-pc/ivas-codec.git - git pull --ff-only --tags upstream $MIRROR_SOURCE_BRANCH @@ -790,4 +925,3 @@ pull-from-3gpp-forge: # Push to mirror, include tags. Option `-o ci.skip` tells GitLab to skip CI for the pushed commits (assumed already tested upstream) - git push --tags -o ci.skip "https://${GITLAB_USER_LOGIN}:${MIRROR_ACCESS_TOKEN}@${CI_REPOSITORY_URL#*@}" "HEAD:${CI_COMMIT_BRANCH}" - diff --git a/CMakeLists.txt b/CMakeLists.txt index ad70b858448a5fad0aebaf21c90c28419f70fbe7..a048f400a67e211190f73c7a5e41f2014ab20224 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -108,6 +108,9 @@ if(WMOPS) add_definitions("-DWMOPS=1") endif() +if(DEC_TO_REND_FLOAT_DUMP) + add_compile_definitions(DEC_TO_REND_FLOAT_DUMP) +endif() project(stereo-evs) set_property(GLOBAL PROPERTY USE_FOLDERS ON) # make Visual Studio projects look nicer @@ -118,6 +121,7 @@ include_directories( lib_debug lib_dec lib_enc + lib_rend lib_util ) @@ -131,21 +135,35 @@ endif() file(GLOB libDebugSrcs "lib_debug/*.c") file(GLOB libDebugHeaders "lib_debug/*.h") add_library(lib_debug ${libDebugSrcs} ${libDebugHeaders}) +target_link_libraries(lib_debug lib_com) file(GLOB libEncSrcs "lib_enc/*.c") file(GLOB libEncHeaders "lib_enc/*.h") add_library(lib_enc ${libEncSrcs} ${libEncHeaders}) target_link_libraries(lib_enc lib_com lib_debug) +file(GLOB libRendSrcs "lib_rend/*.c") +file(GLOB libRendHeaders "lib_rend/*.h") +add_library(lib_rend ${libRendSrcs} ${libRendHeaders}) +target_link_libraries(lib_rend lib_com lib_debug) + file(GLOB libDecSrcs "lib_dec/*.c") file(GLOB libDecHeaders "lib_dec/*.h") add_library(lib_dec ${libDecSrcs} ${libDecHeaders}) -target_link_libraries(lib_dec lib_com lib_debug) +target_link_libraries(lib_dec lib_com lib_rend lib_debug) file(GLOB libUtilSrcs "lib_util/*.c") file(GLOB libUtilHeaders "lib_util/*.h") add_library(lib_util ${libUtilSrcs} ${libUtilHeaders}) +file(GLOB unitTestCRendSrcs "scripts/ivas_pytests/tests/unit_tests/crend/*.c") +file(GLOB unitTestCRendHeaders "scripts/ivas_pytests/tests/unit_tests/crend/*.h") +add_executable(IVAS_crend_unit_test ${unitTestCRendSrcs} ${unitTestCRendHeaders}) +target_link_libraries(IVAS_crend_unit_test lib_rend lib_dec lib_util lib_com lib_debug) + +add_executable(renderer_standalone "scripts/td_object_renderer/object_renderer_standalone/object_renderer_standalone/renderer_standalone.c") +target_link_libraries(renderer_standalone lib_rend lib_dec lib_util lib_com lib_debug) + add_executable(IVAS_cod apps/encoder.c) target_link_libraries(IVAS_cod lib_enc lib_util) if(WIN32) @@ -158,11 +176,14 @@ if(WIN32) target_link_libraries(IVAS_dec Ws2_32) endif() -if(${IVAS_BUILD_PRERENDERER}) - add_executable(IVAS_prerenderer - scripts/prerenderer/prerenderer.c - ${libEncSrcs} - ${libDecSrcs} - ) - target_link_libraries(IVAS_prerenderer lib_com lib_dec lib_debug lib_util) +add_executable(IVAS_rend apps/renderer.c) +target_link_libraries(IVAS_rend lib_rend lib_util) + +if(COPY_EXECUTABLES_FROM_BUILD_DIR) + # Optionally copy executables to the same place where Make puts them (useful for tests that expect executables in specific places) + add_custom_command(TARGET IVAS_cod POST_BUILD VERBATIM COMMAND "${CMAKE_COMMAND}" -E copy "$" "${CMAKE_CURRENT_SOURCE_DIR}/") + add_custom_command(TARGET IVAS_dec POST_BUILD VERBATIM COMMAND "${CMAKE_COMMAND}" -E copy "$" "${CMAKE_CURRENT_SOURCE_DIR}/") + add_custom_command(TARGET IVAS_rend POST_BUILD VERBATIM COMMAND "${CMAKE_COMMAND}" -E copy "$" "${CMAKE_CURRENT_SOURCE_DIR}/") + add_custom_command(TARGET IVAS_crend_unit_test POST_BUILD VERBATIM COMMAND "${CMAKE_COMMAND}" -E copy "$" "${CMAKE_CURRENT_SOURCE_DIR}/scripts/ivas_pytests/tests/unit_tests/crend/") + add_custom_command(TARGET renderer_standalone POST_BUILD VERBATIM COMMAND "${CMAKE_COMMAND}" -E copy "$" "${CMAKE_CURRENT_SOURCE_DIR}/scripts/td_object_renderer/object_renderer_standalone/") endif() diff --git a/Makefile b/Makefile index 76c916c1d2cd05f1d4bbc38bbff20f9a9b43ab19..9514e9a582437a1e60735a29b3a0d5c637d560c0 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,7 @@ SRC_LIBCOM = lib_com SRC_LIBDEBUG = lib_debug SRC_LIBDEC = lib_dec SRC_LIBENC = lib_enc +SRC_LIBREND = lib_rend SRC_LIBUTIL = lib_util SRC_APP = apps BUILD = build @@ -19,15 +20,17 @@ UTESTS_CREND_DIR = $(UTESTS_DIR)/crend SRC_UTESTS = $(UTESTS_CREND_DIR) -SRC_DIRS = $(sort -u $(SRC_LIBCOM) $(SRC_LIBDEBUG) $(SRC_LIBDEC) $(SRC_LIBENC) $(SRC_LIBUTIL) $(SRC_APP) $(SRC_UTESTS)) +SRC_DIRS = $(sort -u $(SRC_LIBCOM) $(SRC_LIBDEBUG) $(SRC_LIBDEC) $(SRC_LIBENC) $(SRC_LIBREND) $(SRC_LIBUTIL) $(SRC_APP) $(SRC_UTESTS)) # Name of CLI binaries CLI_APIDEC ?= IVAS_dec CLI_APIENC ?= IVAS_cod +CLI_APIREND ?= IVAS_rend LIB_LIBCOM ?= libivascom.a LIB_LIBDEBUG ?= libivasdebug.a LIB_LIBDEC ?= libivasdec.a LIB_LIBENC ?= libivasenc.a +LIB_LIBREND ?= libivasrend.a LIB_LIBUTIL ?= libivasutil.a CLI_UTESTS_CREND ?= IVAS_crend_unit_test @@ -125,6 +128,7 @@ SRCS_LIBCOM = $(foreach DIR,$(SRC_LIBCOM),$(patsubst $(DIR)/%,%,$(wildcard $(D SRCS_LIBDEBUG = $(foreach DIR,$(SRC_LIBDEBUG),$(patsubst $(DIR)/%,%,$(wildcard $(DIR)/*.c))) SRCS_LIBDEC = $(foreach DIR,$(SRC_LIBDEC),$(patsubst $(DIR)/%,%,$(wildcard $(DIR)/*.c))) SRCS_LIBENC = $(foreach DIR,$(SRC_LIBENC),$(patsubst $(DIR)/%,%,$(wildcard $(DIR)/*.c))) +SRCS_LIBREND = $(foreach DIR,$(SRC_LIBREND),$(patsubst $(DIR)/%,%,$(wildcard $(DIR)/*.c))) SRCS_LIBUTIL = $(foreach DIR,$(SRC_LIBUTIL),$(patsubst $(DIR)/%,%,$(wildcard $(DIR)/*.c))) SRCS_UTESTS_CREND = $(foreach DIR,$(UTESTS_CREND_DIR),$(patsubst $(DIR)/%,%,$(wildcard $(DIR)/*.c))) @@ -133,9 +137,11 @@ OBJS_LIBCOM = $(addprefix $(OBJDIR)/,$(SRCS_LIBCOM:.c=.o)) OBJS_LIBDEBUG = $(addprefix $(OBJDIR)/,$(SRCS_LIBDEBUG:.c=.o)) OBJS_LIBDEC = $(addprefix $(OBJDIR)/,$(SRCS_LIBDEC:.c=.o)) OBJS_LIBENC = $(addprefix $(OBJDIR)/,$(SRCS_LIBENC:.c=.o)) +OBJS_LIBREND = $(addprefix $(OBJDIR)/,$(SRCS_LIBREND:.c=.o)) OBJS_LIBUTIL = $(addprefix $(OBJDIR)/,$(SRCS_LIBUTIL:.c=.o)) OBJS_CLI_APIDEC = $(OBJDIR)/decoder.o OBJS_CLI_APIENC = $(OBJDIR)/encoder.o +OBJS_CLI_APPREND = $(OBJDIR)/renderer.o OBJS_CLI_UTESTS_CREND = $(addprefix $(OBJDIR)/,$(SRCS_UTESTS_CREND:.c=.o)) @@ -149,7 +155,7 @@ DEPS = $(addprefix $(OBJDIR)/,$(SRCS_LIBCOM:.c=.P) $(SRCS_LIBDEBUG:.c=.P) $(SRCS .PHONY: all clean clean_unittests clean_all -all: $(CLI_APIENC) $(CLI_APIDEC) +all: $(CLI_APIENC) $(CLI_APIDEC) $(CLI_APIREND) $(OBJDIR): $(QUIET)mkdir -p $(OBJDIR) @@ -157,7 +163,7 @@ $(OBJDIR): $(LIB_LIBCOM): $(OBJS_LIBCOM) $(QUIET_AR)$(AR) rcs $@ $^ -$(LIB_LIBDEC): $(OBJS_LIBDEC) +$(LIB_LIBDEC): $(OBJS_LIBDEC) $(OBJS_LIBREND) $(QUIET_AR)$(AR) rcs $@ $^ $(LIB_LIBDEBUG): $(OBJS_LIBDEBUG) @@ -166,27 +172,33 @@ $(LIB_LIBDEBUG): $(OBJS_LIBDEBUG) $(LIB_LIBENC): $(OBJS_LIBENC) $(QUIET_AR)$(AR) rcs $@ $^ +$(LIB_LIBREND): $(OBJS_LIBREND) + $(QUIET_AR)$(AR) rcs $@ $^ + $(LIB_LIBUTIL): $(OBJS_LIBUTIL) $(QUIET_AR)$(AR) rcs $@ $^ -$(CLI_APIENC): $(LIB_LIBENC) $(LIB_LIBDEBUG) $(LIB_LIBCOM) $(LIB_LIBUTIL) $(OBJS_CLI_APIENC) - $(QUIET_LINK)$(CC) $(LDFLAGS) $(OBJS_CLI_APIENC) -L. -livasutil -livasenc -livascom -livasdebug $(LDLIBS) -o $(CLI_APIENC) +$(CLI_APIENC): $(OBJS_CLI_APIENC) $(LIB_LIBENC) $(LIB_LIBCOM) $(LIB_LIBUTIL) $(LIB_LIBDEBUG) + $(QUIET_LINK)$(CC) $(LDFLAGS) $(OBJS_CLI_APIENC) -L. -livasenc -livascom -livasutil -livasdebug $(LDLIBS) -o $(CLI_APIENC) + +$(CLI_APIDEC): $(OBJS_CLI_APIDEC) $(LIB_LIBDEC) $(LIB_LIBCOM) $(LIB_LIBUTIL) $(LIB_LIBDEBUG) + $(QUIET_LINK)$(CC) $(LDFLAGS) $(OBJS_CLI_APIDEC) -L. -livasdec -livascom -livasutil -livasdebug $(LDLIBS) -o $(CLI_APIDEC) -$(CLI_APIDEC): $(LIB_LIBDEC) $(LIB_LIBDEBUG) $(LIB_LIBCOM) $(LIB_LIBUTIL) $(OBJS_CLI_APIDEC) - $(QUIET_LINK)$(CC) $(OBJS_CLI_APIDEC) $(LDFLAGS) -L. -livasutil -livasdec -livascom -livasdebug $(LDLIBS) -o $(CLI_APIDEC) +$(CLI_APIREND): $(OBJS_CLI_APPREND) $(LIB_LIBREND) $(LIB_LIBCOM) $(LIB_LIBUTIL) $(LIB_LIBDEBUG) + $(QUIET_LINK)$(CC) $(LDFLAGS) $(OBJS_CLI_APPREND) -L. -livasrend -livasutil -livasdebug -livascom $(LDLIBS) -o $(CLI_APIREND) -$(CLI_UTESTS_CREND): $(LIB_LIBDEC) $(LIB_LIBDEBUG) $(LIB_LIBCOM) $(LIB_LIBENC) $(LIB_LIBUTIL) $(OBJS_CLI_UTESTS_CREND) - $(QUIET_LINK)$(CC) $(OBJS_CLI_UTESTS_CREND) $(LDFLAGS) -L. -livasutil -livasdec -livascom -livasdebug $(LDLIBS) -o $(UTESTS_CREND_DIR)/$(CLI_UTESTS_CREND) +$(CLI_UTESTS_CREND): $(OBJS_CLI_UTESTS_CREND) $(LIB_LIBDEC) $(LIB_LIBCOM) $(LIB_LIBUTIL) $(LIB_LIBDEBUG) + $(QUIET_LINK)$(CC) $(LDFLAGS) $(OBJS_CLI_UTESTS_CREND) -L. -livasdec -livascom -livasutil -livasdebug $(LDLIBS) -o $(UTESTS_CREND_DIR)/$(CLI_UTESTS_CREND) unittests: $(CLI_UTESTS_CREND) -libs: $(LIB_LIBENC) $(LIB_LIBDEBUG) $(LIB_LIBCOM) $(LIB_LIBDEC) $(LIB_LIBUTIL) +libs: $(LIB_LIBENC) $(LIB_LIBDEBUG) $(LIB_LIBCOM) $(LIB_LIBDEC) $(LIB_LIBREND) $(LIB_LIBUTIL) clean: clean_unittests $(QUIET)$(RM) $(OBJS_LIBENC) $(OBJS_LIBDEC) $(DEPS) $(QUIET)$(RM) $(DEPS:.P=.d) $(QUIET)test ! -d $(OBJDIR) || rm -rf $(OBJDIR) - $(QUIET)$(RM) $(CLI_APIENC) $(CLI_APIDEC) $(CLI_PRD) $(LIB_LIBENC) $(LIB_LIBDEBUG) $(LIB_LIBCOM) $(LIB_LIBDEC) $(LIB_LIBUTIL) + $(QUIET)$(RM) $(CLI_APIENC) $(CLI_APIDEC) $(CLI_APIREND) $(LIB_LIBENC) $(LIB_LIBDEBUG) $(LIB_LIBCOM) $(LIB_LIBDEC) $(LIB_LIBUTIL) $(LIB_LIBREND) clean_unittests: $(QUIET)$(RM) $(OBJS_UTESTS) diff --git a/Workspace_msvc/Workspace_msvc.sln b/Workspace_msvc/Workspace_msvc.sln index be693e81ccdd6c6a725c8b9697cc74ccd8bfdf75..32f41bb6dd91999f52bcb317ce711aecb7619d07 100644 --- a/Workspace_msvc/Workspace_msvc.sln +++ b/Workspace_msvc/Workspace_msvc.sln @@ -8,6 +8,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib_enc", "lib_enc.vcxproj" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib_com", "lib_com.vcxproj", "{39EC200D-7795-4FF8-B214-B24EDA5526AE}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib_rend", "lib_rend.vcxproj", "{718DE063-A18B-BB72-9150-62B892E6FFA6}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib_util", "lib_util.vcxproj", "{2FA8F384-0775-F3B7-F8C3-85209222FC70}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib_debug", "lib_debug.vcxproj", "{54509728-928B-44D9-A118-A6F92F08B34F}" @@ -16,6 +18,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "decoder", "decoder.vcxproj" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "encoder", "encoder.vcxproj", "{B3FC9DFC-7268-8660-7C0D-B60BAF02C554}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "renderer", "renderer.vcxproj", "{12B4C8A5-1E06-4E30-B443-D1F916F52B47}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{51160D4C-55C9-4C16-A792-D94507225746}" ProjectSection(SolutionItems) = preProject ..\.clang-format = ..\.clang-format @@ -60,6 +64,15 @@ Global {39EC200D-7795-4FF8-B214-B24EDA5526AE}.Unittests|Win32.ActiveCfg = Unittests|Win32 {39EC200D-7795-4FF8-B214-B24EDA5526AE}.Unittests|Win32.Build.0 = Unittests|Win32 {39EC200D-7795-4FF8-B214-B24EDA5526AE}.Unittests|x64.ActiveCfg = Unittests|Win32 + {718DE063-A18B-BB72-9150-62B892E6FFA6}.Debug|Win32.ActiveCfg = Debug|Win32 + {718DE063-A18B-BB72-9150-62B892E6FFA6}.Debug|Win32.Build.0 = Debug|Win32 + {718DE063-A18B-BB72-9150-62B892E6FFA6}.Debug|x64.ActiveCfg = Debug|Win32 + {718DE063-A18B-BB72-9150-62B892E6FFA6}.Release|Win32.ActiveCfg = Release|Win32 + {718DE063-A18B-BB72-9150-62B892E6FFA6}.Release|Win32.Build.0 = Release|Win32 + {718DE063-A18B-BB72-9150-62B892E6FFA6}.Release|x64.ActiveCfg = Release|Win32 + {718DE063-A18B-BB72-9150-62B892E6FFA6}.Unittests|Win32.ActiveCfg = Unittests|Win32 + {718DE063-A18B-BB72-9150-62B892E6FFA6}.Unittests|Win32.Build.0 = Unittests|Win32 + {718DE063-A18B-BB72-9150-62B892E6FFA6}.Unittests|x64.ActiveCfg = Release|Win32 {2FA8F384-0775-F3B7-F8C3-85209222FC70}.Debug|Win32.ActiveCfg = Debug|Win32 {2FA8F384-0775-F3B7-F8C3-85209222FC70}.Debug|Win32.Build.0 = Debug|Win32 {2FA8F384-0775-F3B7-F8C3-85209222FC70}.Debug|x64.ActiveCfg = Debug|Win32 @@ -96,6 +109,15 @@ Global {B3FC9DFC-7268-8660-7C0D-B60BAF02C554}.Unittests|Win32.ActiveCfg = Unittests|Win32 {B3FC9DFC-7268-8660-7C0D-B60BAF02C554}.Unittests|Win32.Build.0 = Unittests|Win32 {B3FC9DFC-7268-8660-7C0D-B60BAF02C554}.Unittests|x64.ActiveCfg = Unittests|Win32 + {12B4C8A5-1E06-4E30-B443-D1F916F52B47}.Debug|Win32.ActiveCfg = Debug|Win32 + {12B4C8A5-1E06-4E30-B443-D1F916F52B47}.Debug|Win32.Build.0 = Debug|Win32 + {12B4C8A5-1E06-4E30-B443-D1F916F52B47}.Debug|x64.ActiveCfg = Debug|Win32 + {12B4C8A5-1E06-4E30-B443-D1F916F52B47}.Release|Win32.ActiveCfg = Release|Win32 + {12B4C8A5-1E06-4E30-B443-D1F916F52B47}.Release|Win32.Build.0 = Release|Win32 + {12B4C8A5-1E06-4E30-B443-D1F916F52B47}.Release|x64.ActiveCfg = Release|Win32 + {12B4C8A5-1E06-4E30-B443-D1F916F52B47}.Unittests|Win32.ActiveCfg = Unittests|Win32 + {12B4C8A5-1E06-4E30-B443-D1F916F52B47}.Unittests|Win32.Build.0 = Unittests|Win32 + {12B4C8A5-1E06-4E30-B443-D1F916F52B47}.Unittests|x64.ActiveCfg = Release|Win32 {32354377-ACA7-40F9-9A0E-87FC956F0B78}.Debug|Win32.ActiveCfg = Debug|Win32 {32354377-ACA7-40F9-9A0E-87FC956F0B78}.Debug|x64.ActiveCfg = Debug|Win32 {32354377-ACA7-40F9-9A0E-87FC956F0B78}.Release|Win32.ActiveCfg = Release|Win32 diff --git a/Workspace_msvc/lib_com.vcxproj b/Workspace_msvc/lib_com.vcxproj index ed091573fc0f72621a89a53be938267e4d4289c0..994b1ec4291fcd5b010f022167796fcb4921f7f9 100644 --- a/Workspace_msvc/lib_com.vcxproj +++ b/Workspace_msvc/lib_com.vcxproj @@ -78,7 +78,7 @@ Disabled - ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_util;%(AdditionalIncludeDirectories) + ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_rend;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;$(Macros);WIN32;%(PreprocessorDefinitions) EnableFastChecks @@ -109,7 +109,7 @@ Disabled - ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_util;%(AdditionalIncludeDirectories) + ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_rend;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;$(Macros);WIN32;%(PreprocessorDefinitions) @@ -147,7 +147,7 @@ Neither false false - ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_util;%(AdditionalIncludeDirectories) + ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_rend;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;$(Macros);%(PreprocessorDefinitions) true @@ -358,4 +358,4 @@ - \ No newline at end of file + diff --git a/Workspace_msvc/lib_debug.vcxproj b/Workspace_msvc/lib_debug.vcxproj index d8efa0879d63594f1c3c56f85acf340c61f14e25..5dc36d4633eb0aece3fd91393381e3d75c90a07e 100644 --- a/Workspace_msvc/lib_debug.vcxproj +++ b/Workspace_msvc/lib_debug.vcxproj @@ -73,7 +73,7 @@ Disabled - ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_util;%(AdditionalIncludeDirectories) + ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_rend;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;$(Macros);%(PreprocessorDefinitions) false @@ -97,7 +97,7 @@ Disabled - ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_util;%(AdditionalIncludeDirectories) + ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_rend;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;$(Macros);%(PreprocessorDefinitions) false @@ -124,7 +124,7 @@ AnySuitable false false - ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_util;%(AdditionalIncludeDirectories) + ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_rend;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;$(Macros);%(PreprocessorDefinitions) true @@ -166,4 +166,4 @@ - \ No newline at end of file + diff --git a/Workspace_msvc/lib_dec.vcxproj b/Workspace_msvc/lib_dec.vcxproj index 5860b0a9708d869b8a4f66c31a0022ea33a2e12e..1d7e5cd133b6cf63cff36b48e53ab0384751ffdd 100644 --- a/Workspace_msvc/lib_dec.vcxproj +++ b/Workspace_msvc/lib_dec.vcxproj @@ -89,7 +89,7 @@ Disabled - ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_util;%(AdditionalIncludeDirectories) + ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_util;..\lib_rend;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;$(Macros);WIN32;%(PreprocessorDefinitions) EnableFastChecks @@ -126,7 +126,7 @@ Disabled - ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_util;%(AdditionalIncludeDirectories) + ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_util;..\lib_rend;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;$(Macros);WIN32;%(PreprocessorDefinitions) @@ -169,7 +169,7 @@ Neither false false - ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_util;%(AdditionalIncludeDirectories) + ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_util;..\lib_rend;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;$(Macros);WIN32;%(PreprocessorDefinitions) true @@ -264,13 +264,9 @@ - - - - @@ -279,17 +275,13 @@ - - - - @@ -297,35 +289,15 @@ - - - - - - - - - - - - - - - - - - - - @@ -393,10 +365,7 @@ - - - @@ -419,6 +388,10 @@ {54509728-928b-44d9-a118-a6f92f08b34f} false + + {718DE063-A18B-BB72-9150-62B892E6FFA6} + false + diff --git a/Workspace_msvc/lib_dec.vcxproj.filters b/Workspace_msvc/lib_dec.vcxproj.filters index 5476cf558887abd6d7b549246ce9da5696876e3b..ffc999a2f16e3cf5b0ea2aa58246f4c7f265bd41 100644 --- a/Workspace_msvc/lib_dec.vcxproj.filters +++ b/Workspace_msvc/lib_dec.vcxproj.filters @@ -28,24 +28,6 @@ dec_ivas_c - - dec_ivas_c - - - dec_ivas_c - - - dec_ivas_c - - - dec_ivas_c - - - dec_ivas_c - - - dec_ivas_c - dec_ivas_c @@ -406,24 +388,9 @@ dec_ivas_c - - dec_ivas_c - - - dec_ivas_c - dec_ivas_c - - dec_ivas_c - - - dec_ivas_c - - - dec_ivas_c - dec_ivas_c @@ -448,15 +415,9 @@ dec_ivas_c - - dec_ivas_c - dec_ivas_c - - dec_ivas_c - dec_ivas_c @@ -512,42 +473,21 @@ dec_ivas_c - - dec_ivas_c - - - dec_ivas_c - - - dec_ivas_c - dec_ivas_c - + dec_ivas_c dec_ivas_c - - dec_ivas_c - - + dec_ivas_c dec_ivas_c - - dec_ivas_c - - - dec_ivas_c - - - dec_ivas_c - dec_ivas_c @@ -560,24 +500,6 @@ dec_ivas_c - - dec_ivas_c - - - dec_ivas_c - - - dec_ivas_c - - - dec_ivas_c - - - dec_ivas_c - - - dec_ivas_c - dec_ivas_c @@ -598,9 +520,6 @@ dec_h - - dec_h - dec_h @@ -626,12 +545,6 @@ dec_h - - dec_h - - - dec_h - diff --git a/Workspace_msvc/lib_enc.vcxproj b/Workspace_msvc/lib_enc.vcxproj index c1144daf310d41455179ea56895d8a361d7e902b..3d46808cf38ef7369609e9ab86aab569d20025d7 100644 --- a/Workspace_msvc/lib_enc.vcxproj +++ b/Workspace_msvc/lib_enc.vcxproj @@ -89,7 +89,7 @@ Disabled - ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_util;%(AdditionalIncludeDirectories) + ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_rend;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;$(Macros);WIN32;%(PreprocessorDefinitions) EnableFastChecks @@ -129,7 +129,7 @@ Disabled - ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_util;%(AdditionalIncludeDirectories) + ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_rend;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;$(Macros);WIN32;%(PreprocessorDefinitions) @@ -176,7 +176,7 @@ Neither false false - ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_util;%(AdditionalIncludeDirectories) + ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_rend;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;$(Macros);%(PreprocessorDefinitions) true @@ -423,4 +423,4 @@ - \ No newline at end of file + diff --git a/Workspace_msvc/lib_rend.vcxproj b/Workspace_msvc/lib_rend.vcxproj new file mode 100644 index 0000000000000000000000000000000000000000..c3a12686948da841c8373af7cbda5f337af9c5e5 --- /dev/null +++ b/Workspace_msvc/lib_rend.vcxproj @@ -0,0 +1,258 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Unittests + Win32 + + + + lib_rend + {718DE063-A18B-BB72-9150-62B892E6FFA6} + evs_dec + 10.0.17763.0 + + + StaticLibrary + v141 + false + MultiByte + + + StaticLibrary + v141 + false + MultiByte + + + + StaticLibrary + v141 + false + MultiByte + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>15.0.27428.2015 + + + .\Debug_$(ProjectName)\ + .\Debug_$(ProjectName)\ + false + false + libivasrend + + + .\Debug_$(ProjectName)\ + .\Debug_$(ProjectName)\ + false + false + libivasrend + + + .\Release_$(ProjectName)\ + .\Release_$(ProjectName)\ + false + false + libivasrend + + + + + + + .\Debug\$(ProjectName).tlb + + + + Disabled + ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;$(Macros);WIN32;%(PreprocessorDefinitions) + + EnableFastChecks + MultiThreadedDebug + false + + + $(IntDir)$(ProjectName).pdb + Level4 + true + OldStyle + Default + %(DisableSpecificWarnings) + + + _DEBUG;%(PreprocessorDefinitions) + 0x0c0c + + + WS2_32.lib; %(AdditionalDependencies) + $(OutDir)$(TargetName).lib + true + + + + + + + + + .\Debug\$(ProjectName).tlb + + + + + Disabled + ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_util;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;$(Macros);WIN32;%(PreprocessorDefinitions) + + + EnableFastChecks + MultiThreadedDebug + false + + + + + $(IntDir)$(ProjectName).pdb + Level4 + true + OldStyle + Default + %(DisableSpecificWarnings) + + + _DEBUG;%(PreprocessorDefinitions) + 0x0c0c + + + WS2_32.lib; %(AdditionalDependencies) + $(OutDir)$(TargetName).lib + true + + + + + + + + $(IntDir)$(ProjectName).tlb + + + + MaxSpeed + AnySuitable + false + Neither + false + false + ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;$(Macros);WIN32;%(PreprocessorDefinitions) + true + + Default + MultiThreaded + true + false + + + $(IntDir)$(ProjectName).pdb + Level4 + true + + Default + %(DisableSpecificWarnings) + + + NDEBUG;%(PreprocessorDefinitions) + 0x0c0c + + + WS2_32.lib; %(AdditionalDependencies) + $(OutDir)$(TargetName).lib + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {39ec200d-7795-4ff8-b214-b24eda5526ae} + false + + + {54509728-928b-44d9-a118-a6f92f08b34f} + false + + + + + + + + + + + \ No newline at end of file diff --git a/Workspace_msvc/lib_util.vcxproj b/Workspace_msvc/lib_util.vcxproj index 0bb399c1534927e071e7c0bb8f64d634ca0e624a..3e269dab4bf714878ae0e69689dab54d7d2f5eb9 100644 --- a/Workspace_msvc/lib_util.vcxproj +++ b/Workspace_msvc/lib_util.vcxproj @@ -73,7 +73,7 @@ Disabled - ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_util;%(AdditionalIncludeDirectories) + ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_util;..\lib_rend;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;$(Macros);ZLIB_WINAPI;%(PreprocessorDefinitions) false @@ -93,7 +93,7 @@ Disabled - ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_util;%(AdditionalIncludeDirectories) + ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_util;..\lib_rend;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;$(Macros);ZLIB_WINAPI;%(PreprocessorDefinitions) false @@ -117,7 +117,7 @@ AnySuitable false false - ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_util;%(AdditionalIncludeDirectories) + ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_util;..\lib_rend;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;$(Macros);ZLIB_WINAPI;%(PreprocessorDefinitions) true @@ -141,6 +141,7 @@ + @@ -162,6 +163,7 @@ + diff --git a/scripts/prerenderer/Workspace_msvc/prerenderer.vcxproj b/Workspace_msvc/renderer.vcxproj similarity index 58% rename from scripts/prerenderer/Workspace_msvc/prerenderer.vcxproj rename to Workspace_msvc/renderer.vcxproj index ed2ad4ff8582f1c09860287725d8087e8fc511b0..69ae363370566772ddf75f77dd9e8c23fc3e85c7 100644 --- a/scripts/prerenderer/Workspace_msvc/prerenderer.vcxproj +++ b/Workspace_msvc/renderer.vcxproj @@ -9,11 +9,15 @@ Release Win32 + + Unittests + Win32 + - prerenderer - {12b4c8a5-1e06-4e30-b443-d1f916f52b47} - prerenderer + renderer + {12B4C8A5-1E06-4E30-B443-D1F916F52B47} + renderer 10.0.17763.0 @@ -23,6 +27,12 @@ false MultiByte + + Application + v141 + false + MultiByte + Application v141 @@ -34,27 +44,40 @@ + + + + + + <_ProjectFileVersion>15.0.27428.2015 - .. + ..\ + .\Debug_$(ProjectName)\ + false + false + IVAS_rend + + + ..\ .\Debug_$(ProjectName)\ false false - IVAS_prerenderer + IVAS_rend - .. + ..\ .\Release_$(ProjectName)\ false false - IVAS_prerenderer + IVAS_rend @@ -63,8 +86,8 @@ Disabled - ..\..\..\lib_com;..\..\..\lib_debug;..\..\..\lib_dec;..\..\..\lib_enc;..\..\..\lib_util;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_util;..\lib_rend;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;WIN32;$(Macros);%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebug @@ -76,7 +99,7 @@ true OldStyle Default - 4100;%(DisableSpecificWarnings) + %(DisableSpecificWarnings) _DEBUG;%(PreprocessorDefinitions) @@ -87,16 +110,64 @@ $(OutDir)$(TargetName).exe true - false true $(IntDir)$(ProjectName).pdb Console - false MachineX86 + + + + + + + $(IntDir)$(ProjectName).tlb + + + + + Disabled + ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_util;..\lib_rend;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;WIN32;$(Macros);%(PreprocessorDefinitions) + + + EnableFastChecks + MultiThreadedDebug + false + + + + + $(IntDir)$(ProjectName).pdb + Level4 + true + OldStyle + Default + %(DisableSpecificWarnings) + + + _DEBUG;%(PreprocessorDefinitions) + 0x0c0c + + + + + $(OutDir)$(TargetName).exe + true + + + true + $(IntDir)$(ProjectName).pdb + Console + false + + + MachineX86 + + $(IntDir)$(ProjectName).tlb @@ -109,8 +180,8 @@ Neither false false - ..\..\..\lib_com;..\..\..\lib_debug;..\..\..\lib_dec;..\..\..\lib_enc;..\..\..\lib_util;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_util;..\lib_rend;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;$(Macros);%(PreprocessorDefinitions) true Default @@ -125,7 +196,7 @@ true Default - 4100;%(DisableSpecificWarnings) + %(DisableSpecificWarnings) NDEBUG;%(PreprocessorDefinitions) @@ -141,43 +212,30 @@ false MachineX86 + libcmtd.lib - + + + + {54509728-928B-44D9-A118-A6F92F08B34F} false - + {2FA8F384-0775-F3B7-F8C3-85209222FC70} false - + {39ec200d-7795-4ff8-b214-b24eda5526ae} false - - {E822DDAF-0F5F-4CD0-A694-38AE69DE74D3} + + {718DE063-A18B-BB72-9150-62B892E6FFA6} false - - - - - - - - - - - - - - - - - diff --git a/apps/encoder.c b/apps/encoder.c index df0cf8f9cac33660d8744d8573734d3864664ed6..7d2de90dd07c2f6b6d1ab5e5d7fbd6450cb0cd0a 100644 --- a/apps/encoder.c +++ b/apps/encoder.c @@ -264,12 +264,18 @@ int main( /*------------------------------------------------------------------------------------------* * Open input audio file *------------------------------------------------------------------------------------------*/ - - if ( AudioFileReader_open( &audioReader, arg.inputWavFilename, arg.inputFs ) != IVAS_ERR_OK ) + int32_t inFileSampleRate = 0; + if ( AudioFileReader_open( &audioReader, arg.inputWavFilename, &inFileSampleRate ) != IVAS_ERR_OK ) { fprintf( stderr, "\nCan't open %s\n\n", arg.inputWavFilename ); goto cleanup; } + if ( inFileSampleRate != 0 && /* inFileSampleRate will remain zero if input file is raw PCM */ + inFileSampleRate != arg.inputFs ) + { + fprintf( stderr, "Sampling rate mismatch: %d Hz requested, but %d Hz found in file %s\n", arg.inputFs, inFileSampleRate, arg.inputWavFilename ); + goto cleanup; + } /*------------------------------------------------------------------------------------------* * Open output bitstream file diff --git a/apps/renderer.c b/apps/renderer.c new file mode 100644 index 0000000000000000000000000000000000000000..8097424cec34869269043c7360a1a457fed15216 --- /dev/null +++ b/apps/renderer.c @@ -0,0 +1,2405 @@ +/****************************************************************************************************** + + (C) 2022 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository. All Rights Reserved. + + This software is protected by copyright law and by international treaties. + The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository retain full ownership rights in their respective contributions in + the software. This notice grants no license of any kind, including but not limited to patent + license, nor is any license granted by implication, estoppel or otherwise. + + Contributors are required to enter into the IVAS codec Public Collaboration agreement before making + contributions. + + This software is provided "AS IS", without any express or implied warranties. The software is in the + development stage. It is intended exclusively for experts who have experience with such software and + solely for the purpose of inspection. All implied warranties of non-infringement, merchantability + and fitness for a particular purpose are hereby disclaimed and excluded. + + Any dispute, controversy or claim arising under or in relation to providing this software shall be + submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in + accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and + the United Nations Convention on Contracts on the International Sales of Goods. + +*******************************************************************************************************/ + +#include "options.h" +#include "debug.h" +#include "audio_file_reader.h" +#include "audio_file_writer.h" +#include "cmdl_tools.h" +#include "cmdln_parser.h" +#include "common_api_types.h" +#include "head_rotation_file_reader.h" +#include "hrtf_file_reader.h" +#include "ism_file_reader.h" +#include "lib_rend.h" +#include "ls_custom_file_reader.h" +#include "masa_file_reader.h" +#include "prot.h" +#include "render_config_reader.h" +#ifdef WMOPS +#include "PROM_Size_lib_rend.h" +#include "wmops.h" +#endif +#ifdef RAM_COUNTING_TOOL +#include "mem_count.h" +#endif +#include +#include +#include +#include +#include +#include +#include + +#ifdef EXT_RENDERER +#ifndef count_malloc +#ifdef RAM_COUNTING_TOOL +#define count_malloc( n1 ) MALLOC_FCT_CALL( n1 ) +#define count_calloc( n1, n2 ) CALLOC_FCT_CALL( n1, n2 ) +#define count_free( ptr ) FREE_FCT_CALL( ptr ) +#else +#define count_malloc( n1 ) malloc( n1 ) +#define count_calloc( n1, n2 ) calloc( n1, n2 ) +#define count_free( ptr ) free( ptr ) +#endif +#endif + +#ifndef min +#define min( x, y ) ( ( x ) < ( y ) ? ( x ) : ( y ) ) +#endif + +#ifndef max +#define max( x, y ) ( ( x ) > ( y ) ? ( x ) : ( y ) ) +#endif + +#define RENDERER_MAX_CLI_ARG_LENGTH ( FILENAME_MAX ) +#define RENDERER_MAX_METADATA_LENGTH 8192 +#define RENDERER_MAX_METADATA_LINE_LENGTH 1024 + +#if !defined( DEBUGGING ) && !defined( WMOPS ) +static +#endif + int32_t frame = 0; + +#ifdef _WIN32 +#define SEP_FOLDER '\\' +#else +#define SEP_FOLDER '/' +#endif + +#ifdef WMOPS +void print_stack_call_tree( void ); +int Const_Data_Size_ivas_rom_rend( void ); +extern int16_t *ptr_base_stack; +extern int16_t *ptr_max_stack; +extern int32_t wc_frame; +extern char location_max_stack[256]; + +/*------------------------------------------------------------------------------------------* + * Function to print complexity & memory estimates + *------------------------------------------------------------------------------------------*/ +static void print_mem_renderer( size_t SRAM_size ) +{ + fprintf( stdout, "\n\n --- Renderer memory usage --- \n\n" ); + + fprintf( stdout, "PROM size (renderer): %d words (or instructions)\n", PROM_Size_lib_rend ); + fprintf( stdout, "Stack size: %d words in %s() in frame #%d\n", ( ptr_base_stack - ptr_max_stack ) * sizeof( int16_t ) / sizeof( float ), location_max_stack, wc_frame ); + fprintf( stdout, "Table ROM size(renderer): %d words\n", ( Const_Data_Size_ivas_rom_rend() ) / sizeof( float ) ); + fprintf( stdout, "Table ROM size (binaural renderer): %ld words\n", ( Const_Data_Size_ivas_rom_binauralRen() + Const_Data_Size_ivas_rom_TdBinauralR() + Const_Data_Size_ivas_rom_binaural_cr() ) / sizeof( float ) ); +#ifdef RAM_COUNTING_TOOL + fprintf( stdout, "Static RAM size: %d words\n\n", SRAM_size ); +#endif + print_stack_call_tree(); + + fprintf( stdout, "Note: this is an optimistic estimate of the memory consumption assuming\n" ); + fprintf( stdout, " that each variable (short, long or float) in the codec requires\n" ); + fprintf( stdout, " 32 bits of memory and may therefore be represented by 1 word.\n" ); + fprintf( stdout, " The following formula is used: sizeof('memory array')/sizeof(float)\n\n" ); +} +#endif + +typedef struct +{ + uint32_t frameCounter; + uint16_t numObjects; + IsmFileReader *ismReaders[RENDERER_MAX_ISM_INPUTS]; + uint32_t numPositions[RENDERER_MAX_ISM_INPUTS]; + IVAS_REND_AudioObjectPosition *positions[RENDERER_MAX_ISM_INPUTS]; /* size: [RENDERER_MAX_ISM_INPUTS][numPositions[object_index]] */ + uint16_t *positionDurations[RENDERER_MAX_ISM_INPUTS]; /* size: [RENDERER_MAX_ISM_INPUTS][numPositions[object_index]] */ + uint32_t currentPositionIdxs[RENDERER_MAX_ISM_INPUTS]; /* Index of current position as listed in the metadata file */ + uint16_t durationCounters[RENDERER_MAX_ISM_INPUTS]; /* Number of frames spent at current position */ +} IsmPositionProvider; + +typedef struct +{ + IVAS_REND_AudioConfig audioConfig; + int32_t inputChannelIndex; + float gain_dB; +} RendererInput; + +typedef struct +{ + IVAS_REND_AudioObjectPosition positions[RENDERER_MAX_ISM_INPUTS]; + int16_t numObjects; +} ObjectPositionBuffer; + +typedef struct +{ + RendererInput audioObjects[RENDERER_MAX_ISM_INPUTS]; + uint16_t numAudioObjects; + RendererInput multiChannelBuses[RENDERER_MAX_MC_INPUTS]; + uint16_t numMultiChannelBuses; + RendererInput ambisonicsBuses[RENDERER_MAX_SBA_INPUTS]; + uint16_t numAmbisonicsBuses; + IVAS_CUSTOM_LS_DATA inSetupCustom; + RendererInput masaBuses[RENDERER_MAX_MASA_INPUTS]; + uint16_t numMasaBuses; +} InputConfig; + +typedef struct +{ + IVAS_REND_AudioConfig audioConfig; + IVAS_CUSTOM_LS_DATA outSetupCustom; +} OutputConfig; + +typedef struct +{ + char executableName[RENDERER_MAX_CLI_ARG_LENGTH]; + char inputFilePath[RENDERER_MAX_CLI_ARG_LENGTH]; + char outputFilePath[RENDERER_MAX_CLI_ARG_LENGTH]; + int32_t sampleRate; + InputConfig inConfig; + OutputConfig outConfig; + char inMetadataFilePaths[RENDERER_MAX_ISM_INPUTS][RENDERER_MAX_CLI_ARG_LENGTH]; + int16_t numInMetadataFiles; + char headRotationFilePath[RENDERER_MAX_CLI_ARG_LENGTH]; + char customHrtfFilePath[RENDERER_MAX_CLI_ARG_LENGTH]; + char renderConfigFilePath[RENDERER_MAX_CLI_ARG_LENGTH]; + int8_t orientationTracking; + float noDiegeticPan; + bool delayCompensationEnabled; + bool quietModeEnabled; + bool sceneDescriptionInput; + float inputGainGlobal; /* Linear gain (not in dB) */ +} CmdlnArgs; + +typedef enum +{ + CmdLnOptionId_inputFile = 1, + CmdLnOptionId_inputFormat, + CmdLnOptionId_outputFile, + CmdLnOptionId_outputFormat, + CmdLnOptionId_sampleRate, + CmdLnOptionId_trajFile, + CmdLnOptionId_customHrtfFile, + CmdLnOptionId_renderConfigFile, + CmdLnOptionId_noDiegeticPan, + CmdLnOptionId_orientationTracking, + CmdLnOptionId_customLfeRouting, + CmdLnOptionId_noDelayCmp, + CmdLnOptionId_quietModeEnabled, + CmdLnOptionId_inputMetadata, + CmdLnOptionId_listFormats, + CmdLnOptionId_inputGain, +} CmdLnOptionId; + +static const CmdLnParser_Option cliOptions[] = { + { + .id = CmdLnOptionId_inputFile, + .match = "input_file", + .matchShort = "i", + .description = "Path to the input file (WAV, raw PCM or scene description file)", + }, + { + .id = CmdLnOptionId_inputFormat, + .match = "input_format", + .matchShort = "if", + .description = "Audio format of input file (e.g. 5_1 or HOA3 or META, use -l for a list)", + }, + { + .id = CmdLnOptionId_inputMetadata, + .match = "input_metadata", + .matchShort = "im", + .description = "Space-separated list of path to metadata files for ISM or MASA inputs", + }, + { + .id = CmdLnOptionId_outputFile, + .match = "output_file", + .matchShort = "o", + .description = "Path to the output file", + }, + { + .id = CmdLnOptionId_outputFormat, + .match = "output_format", + .matchShort = "of", + .description = "Output format to render.\nAlternatively, can be a custom loudspeaker layout file", + }, + { + .id = CmdLnOptionId_sampleRate, + .match = "sample_rate", + .matchShort = "fs", + .description = "Input sampling rate in kHz (16, 32, 48) - required only with raw PCM inputs", /* TODO(sgi): Add sampling rate to scene description files */ + }, + { + .id = CmdLnOptionId_trajFile, + .match = "trajectory_file", + .matchShort = "tf", + .description = "Head rotation trajectory file for simulation of head tracking (only for BINAURAL and BINAURAL_ROOM outputs)", + }, + { + .id = CmdLnOptionId_customHrtfFile, + .match = "custom_hrtf", + .matchShort = "hrtf", + .description = "Custom HRTF file for binaural rendering (only for BINAURAL and BINAURAL_ROOM outputs)", + }, + { + .id = CmdLnOptionId_renderConfigFile, + .match = "render_config", + .matchShort = "rc", + .description = "Binaural renderer configuration file (only for BINAURAL and BINAURAL_ROOM outputs)", + }, + { + .id = CmdLnOptionId_noDiegeticPan, + .match = "no_diegetic_pan", + .matchShort = "ndp", + .description = "Panning mono no diegetic sound to stereo -1<= pan <= 1\nleft or l or 1->left, right or r or -1->right, center or c or 0 ->middle\n(todo: implementation)", + }, + { + .id = CmdLnOptionId_orientationTracking, + .match = "tracking_type", + .matchShort = "otr", + .description = "Head orientation tracking type: 'ref' or 'avg' (only for BINAURAL and BINAURAL_ROOM) (todo: check implementation)", + }, + { + /* TODO(sgi): Replace with more configurable input, e.g. ask for a list of triplets: (gain, azimuth, elevation) to place LFE signal */ + /* rename to "lfeHandling" */ + .id = CmdLnOptionId_customLfeRouting, + .match = "neverDropLfe", + .matchShort = "ndl", + .description = "[flag] If set, renderer tries to render LFE into other channels in an optimal way when rendering to configs w/o LFE", + }, + { + .id = CmdLnOptionId_noDelayCmp, + .match = "no_delay_cmp", + .matchShort = "ndc", + .description = "[flag] Turn off delay compensation", + }, + { + .id = CmdLnOptionId_quietModeEnabled, + .match = "quiet", + .matchShort = "q", + .description = "[flag] Limit printouts to terminal", + }, + { + .id = CmdLnOptionId_inputGain, + .match = "gain", + .matchShort = "g", + .description = "Input gain (linear, not in dB) to be applied to input audio file", + }, + { + .id = CmdLnOptionId_listFormats, + .match = "list", + .matchShort = "l", + .description = "List supported audio formats", + }, +}; + +static const int32_t numCliOptions = sizeof( cliOptions ) / sizeof( CmdLnParser_Option ); + +static IVAS_REND_AudioConfig ambisonicsOrderToEnum( + const int16_t order ); + +static void parseSceneDescriptionFile( + char *path, + char *audioFilePath, + InputConfig *inConfig, + IsmPositionProvider *positionProvider, + MasaFileReader **masaReaders ); + +static ivas_error parseCustomLayoutFile( + const char *filePath, + IVAS_CUSTOM_LS_DATA *pLsSetupCustom ); + +static CmdlnArgs parseCmdlnArgs( + const int argc, + char **argv ); + +static IsmPositionProvider *IsmPositionProvider_open( + void ); + +static void IsmPositionProvider_getNextFrame( + IsmPositionProvider *positionProvider, + ObjectPositionBuffer *objectMetadataBuffer ); + +static void IsmPositionProvider_close( + IsmPositionProvider *positionProvider ); + +static void readFromShorthandMetadata( + IsmPositionProvider *positionProvider, + ObjectPositionBuffer *objectMetadataBuffer, + const uint32_t objIdx ); + +void getMetadataFromFileReader( + IsmFileReader *ismReader, + ObjectPositionBuffer *objectMetadataBuffer, + const uint32_t objIdx ); + +static void splitConfigFile( + const char *mdfFilePath, + char *metadataString, + uint32_t *metadataStringLength, + char *wavFileName, + uint32_t *wavFileNameLength ); + +static char *readNextMetadataChunk( + char *line, + const char *delimiter ); + +static void parseUint8( + const char *line, + uint8_t *ret ); + +static void parseUint16( + const char *line, + uint16_t *ret ); + +static int8_t parseUint32( + const char *line, + uint32_t *ret ); + +static int8_t parseInt32( + const char *line, + int32_t *ret ); + +static void parseObjectPosition( + char *line, + IVAS_REND_AudioObjectPosition *position, + uint16_t *positionDuration ); + +static void parseMetadata( + char *metadataString, + char *inDir, + InputConfig *inConfig, + IsmPositionProvider *positionProvider, + MasaFileReader **masaReaders ); + +static void convert_backslash( char *str ); + +static void remove_cr( char *str ); + +static void clearString( char *str ); + +static bool isEmptyString( const char *str ); + +static void printSupportedAudioConfigs( void ); + +static IVAS_REND_AudioConfig parseAudioConfig( const char *configString ); + +static void convertInputBuffer( const int16_t *intBuffer, const int16_t numIntSamplesPerChannel, const int16_t numFloatSamplesPerChannel, const int16_t numChannels, float *floatBuffer ); + +static void convertOutputBuffer( const float *floatBuffer, const int16_t numSamplesPerChannel, const int16_t numChannels, int16_t *intBuffer ); + + +static IVAS_REND_ReadOnlyAudioBuffer getReadOnlySubBuffer( IVAS_REND_AudioBuffer buffer, const int16_t chBeginIdx, const int16_t numChannels ) +{ + IVAS_REND_ReadOnlyAudioBuffer subBuffer; + + subBuffer.config = buffer.config; + subBuffer.config.numChannels = numChannels; + subBuffer.data = buffer.data + subBuffer.config.numSamplesPerChannel * chBeginIdx; + + return subBuffer; +} + +static int16_t getTotalNumInChannels( + IVAS_REND_HANDLE hIvasRend, + IVAS_REND_InputId mcIds[RENDERER_MAX_MC_INPUTS], + IVAS_REND_InputId ismIds[RENDERER_MAX_ISM_INPUTS], + IVAS_REND_InputId sbaIds[RENDERER_MAX_SBA_INPUTS] ) +{ + int16_t totalNumInChannels = 0; + int16_t i, numInputChannels; + ivas_error error; + + for ( i = 0; i < RENDERER_MAX_MC_INPUTS; ++i ) + { + if ( mcIds[i] == 0 ) + { + /* Skip inactive inputs */ + continue; + } + + if ( ( error = IVAS_REND_GetInputNumChannels( hIvasRend, mcIds[i], &numInputChannels ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "Error: %s\n", ivas_error_to_string( error ) ); + exit( -1 ); + } + totalNumInChannels += numInputChannels; + } + + for ( i = 0; i < RENDERER_MAX_ISM_INPUTS; ++i ) + { + if ( ismIds[i] == 0 ) + { + /* Skip inactive inputs */ + continue; + } + + if ( ( error = IVAS_REND_GetInputNumChannels( hIvasRend, ismIds[i], &numInputChannels ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "Error: %s\n", ivas_error_to_string( error ) ); + exit( -1 ); + } + totalNumInChannels += numInputChannels; + } + + for ( i = 0; i < RENDERER_MAX_SBA_INPUTS; ++i ) + { + if ( sbaIds[i] == 0 ) + { + /* Skip inactive inputs */ + continue; + } + + + if ( ( error = IVAS_REND_GetInputNumChannels( hIvasRend, sbaIds[i], &numInputChannels ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "Error: %s\n", ivas_error_to_string( error ) ); + exit( -1 ); + } + totalNumInChannels += numInputChannels; + } + + return totalNumInChannels; +} + +static void setupWithSingleFormatInput( + CmdlnArgs args, + char *audioFilePath, + IsmPositionProvider *positionProvider, + MasaFileReader **masaReaders ) +{ + /* With single-format input, inputFilePath is the path to input audio file. */ + strncpy( audioFilePath, args.inputFilePath, FILENAME_MAX - 1 ); + + /* Depending on input format, prepare metadata reading for ISM or MASA */ + if ( args.inConfig.numMasaBuses != 0 ) + { + if ( args.inConfig.numMasaBuses != args.numInMetadataFiles ) + { + fprintf( stderr, "Error: all MASA inputs must have a corresponding metadata file" ); + exit( -1 ); + } + + for ( int32_t i = 0; i < args.numInMetadataFiles; ++i ) + { + masaReaders[i] = MasaFileReader_open( args.inMetadataFilePaths[i] ); + if ( masaReaders[i] == NULL ) + { + fprintf( stderr, "Could not open MASA metadata file %s\n", args.inMetadataFilePaths[i] ); + exit( -1 ); + } + } + } + else if ( args.inConfig.numAudioObjects != 0 ) + { + positionProvider->numObjects = args.inConfig.numAudioObjects; + for ( int16_t i = 0; i < positionProvider->numObjects; ++i ) + { + /* It is allowed on CLI to have no metadata for an ISM input - skip opening if string is empty or contains "NULL" */ + char charBuf[FILENAME_MAX]; + strncpy( charBuf, args.inMetadataFilePaths[i], min( FILENAME_MAX, RENDERER_MAX_CLI_ARG_LENGTH ) - 1 ); + to_upper( charBuf ); + if ( isEmptyString( args.inMetadataFilePaths[i] ) || strncmp( charBuf, "NULL", 4 ) == 0 ) + { + continue; + } + + positionProvider->ismReaders[i] = IsmFileReader_open( args.inMetadataFilePaths[i] ); + if ( positionProvider->ismReaders[i] == NULL ) + { + fprintf( stderr, "Could not open object metadata file %s\n", args.inMetadataFilePaths[i] ); + exit( -1 ); + } + } + } +} + +static float dBToLin( + const float gain_dB ) +{ + return powf( 10.0f, gain_dB / 20.0f ); +} + + +/* ============================================================================ */ + +int main( + int argc, + char **argv ) +{ + IVAS_REND_HANDLE hIvasRend; + HeadRotFileReader *headRotReader = NULL; + hrtfFileReader *hrtfFileReader = NULL; + IsmPositionProvider *positionProvider; + RenderConfigReader *renderConfigReader = NULL; + MasaFileReader *masaReaders[RENDERER_MAX_MASA_INPUTS]; + IVAS_MASA_METADATA_HANDLE hMasaMetadata[RENDERER_MAX_MASA_INPUTS]; + char audioFilePath[FILENAME_MAX]; + AudioFileReader *audioReader = NULL; + AudioFileWriter *audioWriter; + int32_t inBufferSize; + int32_t outBufferSize; + int16_t *inpInt16Buffer; + float *inFloatBuffer; + int16_t *outInt16Buffer; + float *outFloatBuffer; + IVAS_REND_AudioBuffer inBuffer; + IVAS_REND_AudioBuffer outBuffer; + int16_t numSamplesRead; + int16_t delayNumSamples = -1; + int16_t delayNumSamples_orig = 0; + int16_t zeroPad = 0; + int32_t delayTimeScale = 0; + int16_t i, numChannels; + ivas_error error = IVAS_ERR_OK; +#ifdef WMOPS + size_t SRAM_size; +#endif + +#ifdef WMOPS + reset_wmops(); + reset_stack(); +#endif + +#ifdef RAM_COUNTING_TOOL + mem_count_init( 0, USE_32BITS ); +#endif + + for ( i = 0; i < RENDERER_MAX_MASA_INPUTS; ++i ) + { + masaReaders[i] = NULL; + hMasaMetadata[i] = NULL; + } + + CmdlnArgs args = parseCmdlnArgs( argc, argv ); + + positionProvider = IsmPositionProvider_open(); + + convert_backslash( args.inputFilePath ); + convert_backslash( args.outputFilePath ); + convert_backslash( args.headRotationFilePath ); + + if ( !isEmptyString( args.headRotationFilePath ) ) + { + HeadRotationFileReader_open( args.headRotationFilePath, &headRotReader ); + } + + if ( !isEmptyString( args.customHrtfFilePath ) ) + { + hrtfFileReader_open( args.customHrtfFilePath, &hrtfFileReader ); + } + + if ( !isEmptyString( args.renderConfigFilePath ) ) + { + RenderConfigReader_open( args.renderConfigFilePath, &renderConfigReader ); + } + + /* Initialize main input files, i.e. audio and metadata */ + if ( args.sceneDescriptionInput ) + { + /* With scene description input, inputFilePath is the path to the scene description file. Parse it. */ + parseSceneDescriptionFile( args.inputFilePath, audioFilePath, &args.inConfig, positionProvider, masaReaders ); + } + else + { + /* With single-format input, all information is given on command line. */ + setupWithSingleFormatInput( args, audioFilePath, positionProvider, masaReaders ); + } + + int32_t inFileSampleRate = 0; + if ( AudioFileReader_open( &audioReader, audioFilePath, &inFileSampleRate ) != IVAS_ERR_OK ) + { + fprintf( stderr, "Error opening file: %s\n", audioFilePath ); + exit( -1 ); + } + if ( args.sampleRate == 0 && inFileSampleRate == 0 ) + { + fprintf( stderr, "Sampling rate must be specified on command line when using raw PCM input\n" ); + exit( -1 ); + } + if ( args.sampleRate != 0 && inFileSampleRate != 0 && args.sampleRate != inFileSampleRate ) + { + fprintf( stderr, "Sampling rate mismatch: %d Hz requested, but %d Hz found in file %s\n", args.sampleRate, inFileSampleRate, args.inputFilePath ); + exit( -1 ); + } + if ( args.sampleRate == 0 ) + { + args.sampleRate = inFileSampleRate; + } + const int16_t frameSize_smpls = (int16_t) ( 20 * args.sampleRate / 1000 ); + + IVAS_REND_InputId mcIds[RENDERER_MAX_MC_INPUTS] = { 0 }; + IVAS_REND_InputId ismIds[RENDERER_MAX_ISM_INPUTS] = { 0 }; + IVAS_REND_InputId sbaIds[RENDERER_MAX_SBA_INPUTS] = { 0 }; + + if ( ( error = IVAS_REND_Open( &hIvasRend, args.sampleRate, args.outConfig.audioConfig ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "Error opening renderer handle: %s\n", ivas_error_to_string( error ) ); + exit( -1 ); + } + + /* === Configure === */ + if ( ( error = IVAS_REND_InitConfig( hIvasRend, strlen( args.renderConfigFilePath ) != 0 ) ) != IVAS_ERR_OK ) + { + exit( -1 ); + } + + if ( args.renderConfigFilePath[0] != '\0' ) + { + IVAS_RENDER_CONFIG_DATA renderConfig; + + /* sanity check */ + if ( args.outConfig.audioConfig != IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM ) + { + fprintf( stderr, "\nExternal Renderer Config is supported only when BINAURAL_ROOM is used as output. Exiting. \n" ); + exit( -1 ); + } + + if ( ( error = IVAS_REND_GetRenderConfig( hIvasRend, &renderConfig ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "\nIVAS_DEC_GetRenderConfig failed\n" ); + exit( -1 ); + } + + if ( RenderConfigReader_read( renderConfigReader, &renderConfig ) != IVAS_ERR_OK ) + { + fprintf( stderr, "Failed to read renderer configuration from file %s\n", args.renderConfigFilePath ); + exit( -1 ); + } + + if ( ( error = IVAS_REND_FeedRenderConfig( hIvasRend, renderConfig ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "\nIVAS_DEC_FeedRenderConfig failed\n" ); + exit( -1 ); + } + } + + /* Set up output custom layout configuration */ + if ( args.outConfig.audioConfig == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM ) + { + if ( ( error = IVAS_REND_ConfigureCustomOutputLoudspeakerLayout( hIvasRend, args.outConfig.outSetupCustom ) ) != IVAS_ERR_OK ) + { + return error; + } + } + + for ( i = 0; i < args.inConfig.numMultiChannelBuses; ++i ) + { + if ( ( error = IVAS_REND_AddInput( hIvasRend, args.inConfig.multiChannelBuses[i].audioConfig, &mcIds[i] ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "Error: %s\n", ivas_error_to_string( error ) ); + exit( -1 ); + } + + if ( ( error = IVAS_REND_SetInputGain( hIvasRend, mcIds[i], args.inputGainGlobal * dBToLin( args.inConfig.multiChannelBuses[i].gain_dB ) ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "Error: %s\n", ivas_error_to_string( error ) ); + exit( -1 ); + } + + /* TODO(sgi): Command line only supports one custom LS input for now, extend */ + if ( args.inConfig.multiChannelBuses[i].audioConfig == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM ) + { + if ( ( error = IVAS_REND_ConfigureCustomInputLoudspeakerLayout( hIvasRend, mcIds[i], args.inConfig.inSetupCustom ) ) != IVAS_ERR_OK ) + { + return error; + } + } + + /* TODO(sgi): Test custom LFE routing here */ + } + + for ( i = 0; i < args.inConfig.numAudioObjects; ++i ) + { + if ( ( error = IVAS_REND_AddInput( hIvasRend, IVAS_REND_AUDIO_CONFIG_OBJECT, &ismIds[i] ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "Error: %s\n", ivas_error_to_string( error ) ); + exit( -1 ); + } + + if ( ( error = IVAS_REND_SetInputGain( hIvasRend, ismIds[i], args.inputGainGlobal * dBToLin( args.inConfig.audioObjects[i].gain_dB ) ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "Error: %s\n", ivas_error_to_string( error ) ); + exit( -1 ); + } + } + + for ( i = 0; i < args.inConfig.numAmbisonicsBuses; ++i ) + { + if ( ( error = IVAS_REND_AddInput( hIvasRend, args.inConfig.ambisonicsBuses[i].audioConfig, &sbaIds[i] ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "Error: %s\n", ivas_error_to_string( error ) ); + exit( -1 ); + } + + if ( ( error = IVAS_REND_SetInputGain( hIvasRend, sbaIds[i], args.inputGainGlobal * dBToLin( args.inConfig.ambisonicsBuses[i].gain_dB ) ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "Error: %s\n", ivas_error_to_string( error ) ); + exit( -1 ); + } + } + + const int16_t totalNumInChannels = getTotalNumInChannels( hIvasRend, mcIds, ismIds, sbaIds ); + if ( AudioFileReader_getNumChannels( audioReader ) != 0 /* If input file is raw PCM, audio reader has no info about number of channels */ + && totalNumInChannels != AudioFileReader_getNumChannels( audioReader ) ) + { + fprintf( stderr, "Number of channels in input file does not match selected configuration\n" ); + exit( -1 ); + } + + for ( i = 0; i < args.inConfig.numMasaBuses; ++i ) + { + if ( masaReaders[i] != NULL ) + { + hMasaMetadata[i] = MasaFileReader_getMetadataHandle( masaReaders[i] ); + } + } + + int16_t numOutChannels; + if ( ( error = IVAS_REND_NumOutChannels( hIvasRend, &numOutChannels ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "Error: %s\n", ivas_error_to_string( error ) ); + exit( -1 ); + } + if ( AudioFileWriter_open( &audioWriter, args.outputFilePath, args.sampleRate, numOutChannels ) != IVAS_ERR_OK ) + { + fprintf( stderr, "Failed to open file: %s\n", args.outputFilePath ); + exit( -1 ); + } +#ifdef DEC_TO_REND_FLOAT_DUMP + printf( "Warning: Renderer executable built with DEC_TO_REND_FLOAT_DUMP enabled!\n" ); + printf( " Float dump file (./float_out.wav) will be forced as input.\n" ); +#endif + + inBufferSize = frameSize_smpls * totalNumInChannels; + outBufferSize = frameSize_smpls * numOutChannels; + inpInt16Buffer = count_malloc( inBufferSize * sizeof( int16_t ) ); + inFloatBuffer = count_malloc( inBufferSize * sizeof( float ) ); + outInt16Buffer = count_malloc( outBufferSize * sizeof( int16_t ) ); + outFloatBuffer = count_malloc( outBufferSize * sizeof( float ) ); + + inBuffer.config.numSamplesPerChannel = (int16_t) frameSize_smpls; + inBuffer.config.numChannels = (int16_t) totalNumInChannels; + inBuffer.data = inFloatBuffer; + + outBuffer.config.numSamplesPerChannel = (int16_t) frameSize_smpls; + outBuffer.config.numChannels = (int16_t) numOutChannels; + outBuffer.data = outFloatBuffer; + +#ifdef WMOPS + reset_wmops(); +#endif + + if ( !args.quietModeEnabled ) + { + fprintf( stdout, "\n------ Running the renderer ------\n\n" ); + fprintf( stdout, "Frames processed: " ); + } + else + { + fprintf( stdout, "\n\n-- Start the renderer (quiet mode) --\n\n" ); + } + + while ( 1 ) + { + int16_t num_in_channels; + num_in_channels = inBuffer.config.numChannels; + + /* Read the input data */ + if ( ( error = AudioFileReader_read( audioReader, inpInt16Buffer, (int16_t) inBufferSize, &numSamplesRead ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "\nError reading from file %s\n", audioFilePath ); + exit( -1 ); + } + + if ( numSamplesRead == 0 ) + { + /* end of input data */ + break; + } + + /* Convert from int to float and from interleaved to packed */ + convertInputBuffer( inpInt16Buffer, numSamplesRead, frameSize_smpls, num_in_channels, inFloatBuffer ); +#ifdef DEC_TO_REND_FLOAT_DUMP + /* Overwrite from dump file */ + float tmp[960 * 16]; + dbgread( tmp, sizeof( float ), numSamplesRead, "./float_out.raw" ); + + /* Conversion from interleaved to packed still necessary */ + for ( int32_t i = 0; i < numSamplesRead / num_in_channels; ++i ) + { + for ( int32_t c = 0; c < num_in_channels; ++c ) + { + inFloatBuffer[c * frameSize_smpls + i] = tmp[i * num_in_channels + c]; + } + } +#endif + + for ( i = 0; i < RENDERER_MAX_MASA_INPUTS; ++i ) + { + if ( masaReaders[i] != NULL ) + { + MasaFileReader_readNextFrame( masaReaders[i] ); + /* TODO: Feed MASA metadata here once MASA inputs are supported. + For now avoid unused var warning */ + (void) hMasaMetadata; + } + } + + ObjectPositionBuffer mtdBuffer; + IsmPositionProvider_getNextFrame( positionProvider, &mtdBuffer ); + + /* Read from head rotation trajectory file if specified */ + if ( headRotReader != NULL ) + { + IVAS_QUATERNION quatBuffer[RENDERER_HEAD_POSITIONS_PER_FRAME]; + HeadRotationFileReading( headRotReader, quatBuffer, frame ); + IVAS_REND_SetHeadRotation( hIvasRend, quatBuffer ); + } + else + { + IVAS_REND_SetHeadRotation( hIvasRend, NULL ); + } + + for ( i = 0; i < args.inConfig.numMultiChannelBuses; ++i ) + { + if ( ( error = IVAS_REND_GetInputNumChannels( hIvasRend, mcIds[i], &numChannels ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "Error: %s\n", ivas_error_to_string( error ) ); + exit( -1 ); + } + IVAS_REND_ReadOnlyAudioBuffer tmpBuffer = getReadOnlySubBuffer( inBuffer, (int16_t) args.inConfig.multiChannelBuses[i].inputChannelIndex, numChannels ); + + if ( ( error = IVAS_REND_FeedInputAudio( hIvasRend, mcIds[i], tmpBuffer ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "Error: %s\n", ivas_error_to_string( error ) ); + exit( -1 ); + } + } + + for ( i = 0; i < args.inConfig.numAudioObjects; ++i ) + { + IVAS_REND_ReadOnlyAudioBuffer tmpBuffer = getReadOnlySubBuffer( inBuffer, (int16_t) args.inConfig.audioObjects[i].inputChannelIndex, 1 ); + + if ( ( error = IVAS_REND_FeedInputAudio( hIvasRend, ismIds[i], tmpBuffer ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "Error: %s\n", ivas_error_to_string( error ) ); + exit( -1 ); + } + + if ( ( error = IVAS_REND_FeedInputObjectMetadata( hIvasRend, ismIds[i], mtdBuffer.positions[i] ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "Error: %s\n", ivas_error_to_string( error ) ); + exit( -1 ); + } + } + + for ( i = 0; i < args.inConfig.numAmbisonicsBuses; ++i ) + { + if ( ( error = IVAS_REND_GetInputNumChannels( hIvasRend, sbaIds[i], &numChannels ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "Error: %s\n", ivas_error_to_string( error ) ); + exit( -1 ); + } + IVAS_REND_ReadOnlyAudioBuffer tmpBuffer = getReadOnlySubBuffer( inBuffer, (int16_t) args.inConfig.ambisonicsBuses[i].inputChannelIndex, numChannels ); + + if ( ( error = IVAS_REND_FeedInputAudio( hIvasRend, sbaIds[i], tmpBuffer ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "Error: %s\n", ivas_error_to_string( error ) ); + exit( -1 ); + } + } + + IVAS_REND_GetSamples( hIvasRend, outBuffer ); + + int16_t num_out_channels; + num_out_channels = outBuffer.config.numChannels; + + /* Convert from float to int and from packed to interleaved. + * Values in outFloatBuffer are guaranteed to be within range INT16_MIN:INT16_MAX */ + convertOutputBuffer( outFloatBuffer, frameSize_smpls, num_out_channels, outInt16Buffer ); + + if ( delayNumSamples == -1 ) + { + if ( args.delayCompensationEnabled ) + { + if ( IVAS_REND_GetDelay( hIvasRend, &delayNumSamples, &delayTimeScale ) != IVAS_ERR_OK ) + { + fprintf( stderr, "\nUnable to get delay of renderer!\n" ); + exit( -1 ); + } + delayNumSamples_orig = delayNumSamples; + } + else + { + delayNumSamples = 0; + } + zeroPad = delayNumSamples; + } + + if ( delayNumSamples < outBufferSize ) + { + if ( AudioFileWriter_write( audioWriter, &outInt16Buffer[delayNumSamples * num_out_channels], outBufferSize - ( delayNumSamples * num_out_channels ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "Error writing audio file %s\n", args.outputFilePath ); + exit( -1 ); + } + delayNumSamples = 0; + } + else + { + delayNumSamples -= (int16_t) outBufferSize; + } + + frame++; + if ( !args.quietModeEnabled ) + { + fprintf( stdout, "%-8d\b\b\b\b\b\b\b\b", frame ); + } + +#ifdef WMOPS + update_wmops(); +#endif + } + + /* add zeros at the end to have equal length of synthesized signals */ + memset( outInt16Buffer, 0, zeroPad * outBuffer.config.numChannels * sizeof( int16_t ) ); + if ( ( error = AudioFileWriter_write( audioWriter, outInt16Buffer, zeroPad * outBuffer.config.numChannels ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "\nOutput audio file writer error\n" ); + exit( -1 ); + } + + if ( !args.quietModeEnabled && args.delayCompensationEnabled ) + { + fprintf( stdout, "\nRenderer delay: %-5u [samples] - Timescale: %5u\n", delayNumSamples_orig, delayTimeScale ); + } + + fprintf( stdout, "\n\nRendering of %d frames finished\n\n", frame ); + +#ifdef DEBUGGING + int32_t cnt_frames_limited, noClipping; + if ( ( cnt_frames_limited = IVAS_REND_GetCntFramesLimited( hIvasRend ) ) > 0 ) + { + fprintf( stdout, "Limiter applied in %d frames.\n\n", cnt_frames_limited ); + } + if ( ( noClipping = IVAS_REND_GetNoCLipping( hIvasRend ) ) > 0 ) + { + fprintf( stdout, "Clipping (saturation) detected: %d samples clipped!!!\n\n", noClipping ); + } +#endif + + /* === Close === */ + count_free( inpInt16Buffer ); + count_free( inFloatBuffer ); + count_free( outInt16Buffer ); + count_free( outFloatBuffer ); + for ( i = 0; i < RENDERER_MAX_MASA_INPUTS; ++i ) + { + MasaFileReader_close( &masaReaders[i] ); + } + AudioFileReader_close( &audioReader ); + AudioFileWriter_close( &audioWriter ); + HeadRotationFileReader_close( &headRotReader ); + hrtfFileReader_close( &hrtfFileReader ); + IVAS_REND_Close( &hIvasRend ); + IsmPositionProvider_close( positionProvider ); + RenderConfigReader_close( &renderConfigReader ); + +#ifdef RAM_COUNTING_TOOL +#ifdef WMOPS + SRAM_size = +#endif + mem_count_summary( USE_DEFAULT ); +#endif +#ifdef WMOPS + print_wmops(); + print_mem_renderer( SRAM_size ); +#endif + + return 0; +} + +static IVAS_REND_AudioConfig ambisonicsOrderToEnum( + const int16_t order ) +{ + switch ( order ) + { + case 1: + return IVAS_REND_AUDIO_CONFIG_FOA; + case 2: + return IVAS_REND_AUDIO_CONFIG_HOA2; + case 3: + return IVAS_REND_AUDIO_CONFIG_HOA3; + } + + return IVAS_REND_AUDIO_CONFIG_UNKNOWN; +} + +static bool parseInConfig( + const char *inFormatStr, + InputConfig *inConfig, + bool *sceneDescriptionInput ) +{ + char charBuf[FILENAME_MAX]; + + /* Initialize input config struct */ + inConfig->numAudioObjects = 0; + inConfig->numAmbisonicsBuses = 0; + inConfig->numMultiChannelBuses = 0; + inConfig->numMasaBuses = 0; + + /* First check if input is being set to scene description file - this is not covered by parseAudioConfig(). */ + strncpy( charBuf, inFormatStr, sizeof( charBuf ) - 1 ); + to_upper( charBuf ); + if ( strcmp( charBuf, "META" ) == 0 ) + { + *sceneDescriptionInput = true; + /* Parsing the file will be done later. At this point the actual file path + * may not be known as command line parameters are still being parsed. */ + return true; + } + + /* Check for single-format inputs. The given string should map to a member of IVAS_REND_AudioConfig enum. */ + IVAS_REND_AudioConfig audioConfig = parseAudioConfig( inFormatStr ); + switch ( audioConfig ) + { + case IVAS_REND_AUDIO_CONFIG_MONO: + case IVAS_REND_AUDIO_CONFIG_STEREO: + case IVAS_REND_AUDIO_CONFIG_5_1: + case IVAS_REND_AUDIO_CONFIG_7_1: + case IVAS_REND_AUDIO_CONFIG_5_1_2: + case IVAS_REND_AUDIO_CONFIG_5_1_4: + case IVAS_REND_AUDIO_CONFIG_7_1_4: + inConfig->numMultiChannelBuses = 1; + inConfig->multiChannelBuses[0].audioConfig = audioConfig; + inConfig->multiChannelBuses[0].inputChannelIndex = 0; + inConfig->multiChannelBuses[0].gain_dB = 0.0f; + break; + case IVAS_REND_AUDIO_CONFIG_FOA: + case IVAS_REND_AUDIO_CONFIG_HOA2: + case IVAS_REND_AUDIO_CONFIG_HOA3: + inConfig->numAmbisonicsBuses = 1; + inConfig->ambisonicsBuses[0].audioConfig = audioConfig; + inConfig->ambisonicsBuses[0].inputChannelIndex = 0; + inConfig->ambisonicsBuses[0].gain_dB = 0.0f; + break; + case IVAS_REND_AUDIO_CONFIG_MASA1: + case IVAS_REND_AUDIO_CONFIG_MASA2: + inConfig->numMasaBuses = 1; + inConfig->masaBuses[0].audioConfig = audioConfig; + inConfig->masaBuses[0].inputChannelIndex = 0; + inConfig->masaBuses[0].gain_dB = 0.0f; + break; + case IVAS_REND_AUDIO_CONFIG_OBJECT: + /* If input format is objects, parse the characters after "ISM" to get number of objects */ + { + char *ptr = NULL; + inConfig->numAudioObjects = (uint16_t) strtol( inFormatStr + 3, &ptr, 10 ); + if ( ptr == NULL || *ptr != '\0' ) + { + /* Failed to parse string as a number */ + fprintf( stderr, "Cannot parse string \"%s\" as a valid input format", inFormatStr ); + return false; + } + if ( inConfig->numAudioObjects > RENDERER_MAX_ISM_INPUTS ) + { + fprintf( stderr, "Too many objects at input. Max %d supported.", RENDERER_MAX_ISM_INPUTS ); + return false; + } + for ( int16_t i = 0; i < inConfig->numAudioObjects; ++i ) + { + inConfig->audioObjects[i].audioConfig = audioConfig; + inConfig->audioObjects[i].inputChannelIndex = i; + inConfig->audioObjects[i].gain_dB = 0.0f; + } + } + break; + case IVAS_REND_AUDIO_CONFIG_UNKNOWN: + /* This case will be reached if parsing string to IVAS_REND_AudioConfig enum fails. + * Try to use the given string as a path to a custom loudspeaker layout file. */ + { + ivas_error error = parseCustomLayoutFile( inFormatStr, &inConfig->inSetupCustom ); + + if ( error == IVAS_ERR_FAILED_FILE_OPEN ) + { + /* Failed to open with given string - most likely wasn't a file path */ + fprintf( stderr, "Unsupported input format: %s\n", inFormatStr ); + return false; + } + if ( error != IVAS_ERR_OK ) + { + fprintf( stderr, "Error while reading custom loudspeaker layout file %s\n", inFormatStr ); + return false; + } + inConfig->numMultiChannelBuses = 1; + inConfig->multiChannelBuses[0].audioConfig = IVAS_REND_AUDIO_CONFIG_LS_CUSTOM; + inConfig->multiChannelBuses[0].inputChannelIndex = 0; + inConfig->multiChannelBuses[0].gain_dB = 0.0f; + } + break; + default: + /* Default case covers formats that are defined in the IVAS_REND_AudioConfig enum, + * but cannot be used at input, e.g. BINAURAL */ + fprintf( stderr, "Unsupported input format: %s\n", inFormatStr ); + return false; + } + + return true; +} + +static bool parseOutConfig( + const char *outputFormatStr, + OutputConfig *outConfig ) +{ + ivas_error error; + + outConfig->audioConfig = parseAudioConfig( outputFormatStr ); + /* If the string provided is not recognized as a valid output config, + * it's expected to be a path to a custom loudspeaker layout description file. */ + if ( outConfig->audioConfig == IVAS_REND_AUDIO_CONFIG_UNKNOWN ) + { + outConfig->audioConfig = IVAS_REND_AUDIO_CONFIG_LS_CUSTOM; + if ( ( error = parseCustomLayoutFile( outputFormatStr, &outConfig->outSetupCustom ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "Error while parsing output format option\n" ); + return false; + } + } + + return true; +} + +static int8_t parseDiegeticPan( + char *value, + float *noDiegeticPan ) +{ + int8_t success; + success = 1; + to_upper( value ); + + if ( ( strcmp( value, "CENTER" ) == 0 ) || ( strchr( value, 'C' ) != NULL ) ) + { + *noDiegeticPan = 0.f; + } + else if ( ( strcmp( value, "LEFT" ) == 0 ) || ( strchr( value, 'L' ) != NULL ) ) + { + *noDiegeticPan = -1.f; + } + else if ( ( strcmp( value, "RIGHT" ) == 0 ) || ( strchr( value, 'R' ) != NULL ) ) + { + *noDiegeticPan = 1.f; + } + else + { + *noDiegeticPan = (float) atof( value ); + + if ( *noDiegeticPan > 1.0f || *noDiegeticPan < -1.0f ) + { + fprintf( stderr, "Error: Incorrect value for panning option argument specified!\n\n" ); + success = 0; + } + } + return success ? 0 : -1; +} + +static int8_t parseOrientationTracking( + char *value, + int8_t *tracking_type ) +{ + int8_t success; + success = 1; + + to_upper( value ); + + if ( strcmp( value, "REF" ) == 0 ) + { + *tracking_type = IVAS_ORIENT_TRK_REF; + } + else if ( strcmp( value, "AVG" ) == 0 ) + { + *tracking_type = IVAS_ORIENT_TRK_AVG; + } + else + { + fprintf( stderr, "Error: Invalid orientation tracking type %s \n\n", value ); + success = 0; + } + + return success ? 0 : -1; +} + +static IVAS_REND_AudioConfig parseAudioConfig( + const char *configString ) +{ + char charBuf[14]; + charBuf[13] = '\0'; + + strncpy( charBuf, configString, sizeof( charBuf ) - 1 ); + to_upper( charBuf ); + + if ( ( strcmp( charBuf, "MONO" ) == 0 ) || ( strcmp( charBuf, "HOA0" ) == 0 ) || ( strcmp( charBuf, "SBA0" ) == 0 ) ) + { + return IVAS_REND_AUDIO_CONFIG_MONO; + } + if ( ( strcmp( charBuf, "STEREO" ) == 0 ) || ( strcmp( charBuf, "CICP2" ) == 0 ) ) + { + return IVAS_REND_AUDIO_CONFIG_STEREO; + } + if ( ( strcmp( charBuf, "FOA" ) == 0 ) || ( strcmp( charBuf, "SBA1" ) == 0 ) ) + { + return IVAS_REND_AUDIO_CONFIG_FOA; + } + if ( ( strcmp( charBuf, "HOA2" ) == 0 ) || ( strcmp( charBuf, "SBA2" ) == 0 ) ) + { + return IVAS_REND_AUDIO_CONFIG_HOA2; + } + if ( ( strcmp( charBuf, "HOA3" ) == 0 ) || ( strcmp( charBuf, "SBA3" ) == 0 ) ) + { + return IVAS_REND_AUDIO_CONFIG_HOA3; + } + if ( ( strcmp( charBuf, "5_1" ) == 0 ) || ( strcmp( charBuf, "CICP6" ) == 0 ) ) + { + return IVAS_REND_AUDIO_CONFIG_5_1; + } + if ( ( strcmp( charBuf, "7_1" ) == 0 ) || ( strcmp( charBuf, "CICP12" ) == 0 ) ) + { + return IVAS_REND_AUDIO_CONFIG_7_1; + } + if ( ( strcmp( charBuf, "5_1_2" ) == 0 ) || ( strcmp( charBuf, "CICP14" ) == 0 ) ) + { + return IVAS_REND_AUDIO_CONFIG_5_1_2; + } + if ( ( strcmp( charBuf, "5_1_4" ) == 0 ) || ( strcmp( charBuf, "CICP16" ) == 0 ) ) + { + return IVAS_REND_AUDIO_CONFIG_5_1_4; + } + if ( ( strcmp( charBuf, "7_1_4" ) == 0 ) || ( strcmp( charBuf, "CICP19" ) == 0 ) ) + { + return IVAS_REND_AUDIO_CONFIG_7_1_4; + } + if ( strncmp( charBuf, "ISM", 3 ) == 0 ) + { + return IVAS_REND_AUDIO_CONFIG_OBJECT; + } + if ( strncmp( charBuf, "MASA", 4 ) == 0 ) + { + switch ( charBuf[4] ) + { + case '1': + return IVAS_REND_AUDIO_CONFIG_MASA1; + case '2': + return IVAS_REND_AUDIO_CONFIG_MASA2; + default: + return IVAS_REND_AUDIO_CONFIG_UNKNOWN; + } + } + if ( strcmp( charBuf, "BINAURAL_ROOM" ) == 0 ) + { + return IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM; + } + if ( strcmp( charBuf, "BINAURAL" ) == 0 ) + { + return IVAS_REND_AUDIO_CONFIG_BINAURAL; + } + + return IVAS_REND_AUDIO_CONFIG_UNKNOWN; +} + +static const CmdLnParser_Option *findOptionById( + const int32_t id ) +{ + for ( int32_t i = 0; i < numCliOptions; ++i ) + { + if ( cliOptions[i].id == id ) + { + return &cliOptions[i]; + } + } + + return NULL; +} + +static bool checkRequiredArgs( + CmdlnArgs args ) +{ + const CmdLnParser_Option *tmpOption; + + /* Check required arguments */ + bool missingRequiredArg = false; + if ( isEmptyString( args.inputFilePath ) ) + { + tmpOption = findOptionById( CmdLnOptionId_inputFile ); + fprintf( stderr, "Missing required argument: %s (%s)\n", tmpOption->match, tmpOption->matchShort ); + missingRequiredArg = true; + } + const bool singleInputSpecified = args.inConfig.numAudioObjects != 0 || + args.inConfig.numAmbisonicsBuses != 0 || + args.inConfig.numMultiChannelBuses != 0 || + args.inConfig.numMasaBuses != 0; + if ( !args.sceneDescriptionInput && !singleInputSpecified ) + { + /* Neither scene description input nor single-type input was specified on command line */ + tmpOption = findOptionById( CmdLnOptionId_inputFormat ); + fprintf( stderr, "Missing required argument: %s (%s)\n", tmpOption->match, tmpOption->matchShort ); + missingRequiredArg = true; + } + if ( isEmptyString( args.outputFilePath ) ) + { + tmpOption = findOptionById( CmdLnOptionId_outputFile ); + fprintf( stderr, "Missing required argument: %s (%s)\n", tmpOption->match, tmpOption->matchShort ); + missingRequiredArg = true; + } + if ( args.outConfig.audioConfig == IVAS_REND_AUDIO_CONFIG_UNKNOWN ) + { + tmpOption = findOptionById( CmdLnOptionId_outputFormat ); + fprintf( stderr, "Missing required argument: %s (%s)\n", tmpOption->match, tmpOption->matchShort ); + missingRequiredArg = true; + } + if ( missingRequiredArg ) + { + CmdLnParser_printUsage( args.executableName, cliOptions, numCliOptions ); + } + + return !missingRequiredArg; +} + +static CmdlnArgs defaultArgs( + const char *executableName ) +{ + CmdlnArgs args; + + strncpy( args.executableName, executableName, RENDERER_MAX_CLI_ARG_LENGTH ); + clearString( args.inputFilePath ); + clearString( args.outputFilePath ); + args.sampleRate = 0; + + args.inConfig.inSetupCustom.num_spk = 0; + args.inConfig.inSetupCustom.num_lfe = 0; + args.inConfig.numAudioObjects = 0; + args.inConfig.numAmbisonicsBuses = 0; + args.inConfig.numMultiChannelBuses = 0; + args.inConfig.numMasaBuses = 0; + + args.outConfig.audioConfig = IVAS_REND_AUDIO_CONFIG_UNKNOWN; + args.outConfig.outSetupCustom.num_spk = 0; + args.outConfig.outSetupCustom.num_lfe = 0; + + for ( int32_t i = 0; i < RENDERER_MAX_ISM_INPUTS; ++i ) + { + clearString( args.inMetadataFilePaths[i] ); + } + args.numInMetadataFiles = 0; + + clearString( args.headRotationFilePath ); + clearString( args.customHrtfFilePath ); + clearString( args.renderConfigFilePath ); + + args.orientationTracking = IVAS_ORIENT_TRK_REF; + args.noDiegeticPan = 0.0f; + + args.delayCompensationEnabled = true; + args.quietModeEnabled = false; + args.sceneDescriptionInput = false; + args.inputGainGlobal = 1.0f; + + return args; +} + +static void parseOption( + const int32_t optionId, + char **optionValues, + const int16_t numOptionValues, + void *pOutputStruct ) +{ + CmdlnArgs *args = pOutputStruct; + + switch ( optionId ) + { + case CmdLnOptionId_listFormats: + assert( numOptionValues == 0 ); + printSupportedAudioConfigs(); + exit( 0 ); + case CmdLnOptionId_inputFile: + assert( numOptionValues == 1 ); + strncpy( args->inputFilePath, optionValues[0], RENDERER_MAX_CLI_ARG_LENGTH - 1 ); + break; + case CmdLnOptionId_inputFormat: + assert( numOptionValues == 1 ); + if ( !parseInConfig( optionValues[0], &args->inConfig, &args->sceneDescriptionInput ) ) + { + exit( -1 ); /* Error printout handled by failing function */ + } + break; + case CmdLnOptionId_inputMetadata: + assert( numOptionValues <= RENDERER_MAX_ISM_INPUTS ); + for ( int16_t i = 0; i < numOptionValues; ++i ) + { + strncpy( args->inMetadataFilePaths[i], optionValues[i], RENDERER_MAX_CLI_ARG_LENGTH - 1 ); + } + args->numInMetadataFiles = numOptionValues; + break; + case CmdLnOptionId_outputFile: + assert( numOptionValues == 1 ); + strncpy( args->outputFilePath, optionValues[0], RENDERER_MAX_CLI_ARG_LENGTH - 1 ); + break; + case CmdLnOptionId_outputFormat: + assert( numOptionValues == 1 ); + if ( !parseOutConfig( optionValues[0], &args->outConfig ) ) + { + exit( -1 ); /* Error printout handled by failing function */ + } + break; + case CmdLnOptionId_sampleRate: + assert( numOptionValues == 1 ); + args->sampleRate = (int32_t) ( strtol( optionValues[0], NULL, 10 ) * 1000 ); + if ( args->sampleRate == 0 ) + { + fprintf( stderr, "Invalid sampling rate specified\n" ); + exit( -1 ); + } + break; + case CmdLnOptionId_trajFile: + assert( numOptionValues == 1 ); + strncpy( args->headRotationFilePath, optionValues[0], RENDERER_MAX_CLI_ARG_LENGTH - 1 ); + break; + case CmdLnOptionId_customHrtfFile: + assert( numOptionValues == 1 ); + strncpy( args->customHrtfFilePath, optionValues[0], RENDERER_MAX_CLI_ARG_LENGTH - 1 ); + break; + case CmdLnOptionId_renderConfigFile: + assert( numOptionValues == 1 ); + strncpy( args->renderConfigFilePath, optionValues[0], RENDERER_MAX_CLI_ARG_LENGTH - 1 ); + break; + case CmdLnOptionId_noDiegeticPan: + assert( numOptionValues == 1 ); + if ( parseDiegeticPan( optionValues[0], &args->noDiegeticPan ) != 0 ) + { + fprintf( stderr, "Unknown option for diegetic panning: %s\n", optionValues[0] ); + exit( -1 ); + } + break; + case CmdLnOptionId_orientationTracking: + assert( numOptionValues == 1 ); + if ( parseOrientationTracking( optionValues[0], &args->orientationTracking ) != 0 ) + { + fprintf( stderr, "Unknown option for orientation tracking: %s\n", optionValues[0] ); + exit( -1 ); + } + break; + case CmdLnOptionId_customLfeRouting: + assert( 0 && "Not yet implemented in CLI" ); + break; + case CmdLnOptionId_noDelayCmp: + assert( numOptionValues == 0 ); + args->delayCompensationEnabled = false; + break; + case CmdLnOptionId_quietModeEnabled: + assert( numOptionValues == 0 ); + args->quietModeEnabled = true; + break; + case CmdLnOptionId_inputGain: + assert( numOptionValues == 1 ); + args->inputGainGlobal = strtof( optionValues[0], NULL ); + if ( args->inputGainGlobal == 0.0f ) + { + fprintf( stderr, "Invalid input gain specified\n" ); + exit( -1 ); + } + break; + default: + assert( 0 && "This should be unreachable - all command line options should be explicitly handled." ); + break; + } + + return; +} + +static CmdlnArgs parseCmdlnArgs( + const int argc, + char **argv ) +{ + CmdlnArgs args = defaultArgs( argv[0] ); + + if ( CmdLnParser_parseArgs( argc, argv, cliOptions, numCliOptions, &args, parseOption ) != 0 ) + { + exit( -1 ); /* Error printout handled by failing function */ + } + + if ( !checkRequiredArgs( args ) ) + { + exit( -1 ); /* Error printout handled by failing function */ + } + + return args; +} + + +IsmPositionProvider *IsmPositionProvider_open( + void ) +{ + IsmPositionProvider *ipp; + uint16_t i; + + ipp = (IsmPositionProvider *) count_malloc( sizeof( IsmPositionProvider ) ); + ipp->frameCounter = 0; + ipp->numObjects = 0; + + for ( i = 0; i < RENDERER_MAX_ISM_INPUTS; ++i ) + { + ipp->ismReaders[i] = NULL; + ipp->positions[i] = NULL; + ipp->positionDurations[i] = NULL; + ipp->currentPositionIdxs[i] = 0; + ipp->durationCounters[i] = 0; + } + + return ipp; +} + +void getMetadataFromFileReader( + IsmFileReader *ismReader, + ObjectPositionBuffer *objectMetadataBuffer, + const uint32_t objIdx ) +{ + IVAS_ISM_METADATA ismMetadata; + ivas_error error; + + if ( ( error = IsmFileReader_readNextFrame( ismReader, &ismMetadata ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "\nError (%s) while reading ism metadata from: %s\n\n", ivas_error_to_string( error ), IsmFileReader_getFilePath( ismReader ) ); + exit( -1 ); + } + + objectMetadataBuffer->positions[objIdx].azimuth = ismMetadata.azimuth; + objectMetadataBuffer->positions[objIdx].elevation = ismMetadata.elevation; + + return; +} + +void readFromShorthandMetadata( + IsmPositionProvider *positionProvider, + ObjectPositionBuffer *objectMetadataBuffer, + const uint32_t objIdx ) +{ + uint32_t preUpdatePositionIdx; + uint32_t postUpdatePositionIdx; + + preUpdatePositionIdx = positionProvider->currentPositionIdxs[objIdx]; + + if ( positionProvider->durationCounters[objIdx] == positionProvider->positionDurations[objIdx][preUpdatePositionIdx] ) + { + positionProvider->durationCounters[objIdx] = 0; + positionProvider->currentPositionIdxs[objIdx] = ( positionProvider->currentPositionIdxs[objIdx] + 1 ) % positionProvider->numPositions[objIdx]; + } + + ++positionProvider->durationCounters[objIdx]; + + postUpdatePositionIdx = positionProvider->currentPositionIdxs[objIdx]; + + objectMetadataBuffer->positions[objIdx] = positionProvider->positions[objIdx][postUpdatePositionIdx]; + + return; +} + +void IsmPositionProvider_getNextFrame( + IsmPositionProvider *positionProvider, + ObjectPositionBuffer *objectMetadataBuffer ) +{ + uint32_t objIdx; + + objectMetadataBuffer->numObjects = positionProvider->numObjects; + + for ( objIdx = 0; objIdx < positionProvider->numObjects; ++objIdx ) + { + if ( positionProvider->ismReaders[objIdx] != NULL ) + { + getMetadataFromFileReader( positionProvider->ismReaders[objIdx], objectMetadataBuffer, objIdx ); + } + else + { + readFromShorthandMetadata( positionProvider, objectMetadataBuffer, objIdx ); + } + + /* Wrap azimuth to lie within (-180, 180] range */ + while ( objectMetadataBuffer->positions[objIdx].azimuth < 0.0f ) + { + objectMetadataBuffer->positions[objIdx].azimuth += 360.0f; + } + while ( objectMetadataBuffer->positions[objIdx].azimuth >= 360.0f ) + { + objectMetadataBuffer->positions[objIdx].azimuth -= 360.0f; + } + + /* Clamp elevation to lie within [-90, 90] range (can't be wrapped easily) */ + objectMetadataBuffer->positions[objIdx].elevation = min( max( objectMetadataBuffer->positions[objIdx].elevation, -90 ), 90 ); + } + + ++positionProvider->frameCounter; + + return; +} + +void IsmPositionProvider_close( IsmPositionProvider *positionProvider ) +{ + uint32_t i; + + if ( positionProvider == NULL ) + { + assert( !"Can't close IsmPositionProvider - pointer is NULL" ); + } + + for ( i = 0; i < RENDERER_MAX_ISM_INPUTS; ++i ) + { + if ( positionProvider->ismReaders[i] != NULL ) + { + IsmFileReader_close( &positionProvider->ismReaders[i] ); + } + + if ( positionProvider->positions[i] != NULL ) + { + count_free( positionProvider->positions[i] ); + } + + if ( positionProvider->positionDurations[i] != NULL ) + { + count_free( positionProvider->positionDurations[i] ); + } + } + + count_free( positionProvider ); + + return; +} + +static void splitConfigFile( + const char *mdfFilePath, + char *metadataString, + uint32_t *metadataStringLength, + char *wavFileName, + uint32_t *wavFileNameLength ) +{ + FILE *file; + uint32_t bufferlength; + char wavLine[FILENAME_MAX]; + uint32_t currentPositionIdxs; + uint32_t mdlength; + + memset( metadataString, 0, *metadataStringLength ); + memset( wavFileName, 0, (int16_t) *wavFileNameLength ); + + file = fopen( mdfFilePath, "rb" ); + if ( !file ) + { + fprintf( stderr, "Couldn't open metadata file %s\n", mdfFilePath ); + exit( -1 ); + } + + fseek( file, 0, SEEK_END ); + bufferlength = ftell( file ); + fseek( file, 0, SEEK_SET ); + + if ( fgets( wavLine, (int) *wavFileNameLength, file ) == NULL ) + { + fprintf( stderr, "Error reading metadata\n" ); + exit( -1 ); + } + currentPositionIdxs = ftell( file ); + if ( *wavFileNameLength < currentPositionIdxs ) + { + assert( !"Couldn't read wavFileName, string buffer too small" ); + } + if ( !sscanf( wavLine, "%s", wavFileName ) ) + { + fprintf( stderr, "Error reading metadata\n" ); + exit( -1 ); + } + *wavFileNameLength = (uint32_t) strlen( wavFileName ); + + mdlength = bufferlength - currentPositionIdxs; + /* "+1" for null termination */ + if ( *metadataStringLength + 1 < mdlength ) + { + assert( !"Couldn't read metadata string, string buffer too small" ); + } + + fread( metadataString, 1, mdlength, file ); + metadataString[mdlength] = '\0'; + *metadataStringLength = mdlength + 1; + + fclose( file ); + + return; +} + +/* r: pointer to character following last found delimiter */ +static char *readNextMetadataChunkFrom( + char *start_char, + char *line, + const char *delimiter ) +{ + char *token; + + /* start_char can be NULL - it's used to continue parsing with strtok */ + assert( line != NULL && delimiter != NULL && "unexpected NULL ptr given to readNextMetadataChunkFrom()" ); + + token = strtok( start_char, delimiter ); + + /* End of string reached */ + if ( token == NULL ) + { + /* Clear `line` from previous contents and return NULL */ + clearString( line ); + return NULL; + } + + strcpy( line, token ); + + return token + strlen( token ) + 1; +} + +/* r: pointer to character following last found delimiter */ +static char *readNextMetadataChunk( + char *line, + const char *delimiter ) +{ + return readNextMetadataChunkFrom( NULL, line, delimiter ); +} + +static void parseUint8( + const char *line, + uint8_t *ret ) +{ + char *ptr; + ptr = NULL; + + *ret = (uint8_t) strtol( line, &ptr, 10 ); + if ( ptr == NULL || *ptr != '\0' ) + { + fprintf( stderr, "Cannot parse string \"%s\" as an integer value\n", line ); + exit( -1 ); + } + + return; +} + +static int8_t parseUint32( + const char *line, + uint32_t *ret ) +{ + char *ptr; + ptr = NULL; + + *ret = strtol( line, &ptr, 10 ); + if ( ptr == NULL || *ptr != '\0' ) + { + return -1; + } + + return 0; +} + +static int8_t parseInt32( + const char *line, + int32_t *ret ) +{ + char *ptr; + ptr = NULL; + + *ret = strtol( line, &ptr, 10 ); + if ( ptr == NULL || *ptr != '\0' ) + { + return -1; + } + + return 0; +} + +static void parseOptionalInputValues( + char *line, + float *gain_dB ) +{ + char *parse_pos; + char *key; + char *value; + char *endptr; + + endptr = NULL; + + /* Set default values, in case some values are not specified */ + *gain_dB = 0.f; + + /* Save parsing position - will have to be passed to strtok to resume parsing after using strtok with non-NULL value below */ + parse_pos = readNextMetadataChunk( line, "\n" ); + + /* Look for optional metadata until end of string or next input identifier is found */ + while ( parse_pos != NULL && strcmp( line, "MC" ) != 0 && strcmp( line, "SBA" ) != 0 && strcmp( line, "ISM" ) != 0 && strcmp( line, "MASA" ) != 0 ) + { + key = strtok( line, ":" ); + value = strtok( NULL, "\n" ); + + if ( strcmp( key, "gain_dB" ) == 0 ) + { + *gain_dB = (float) strtod( value, &endptr ); + + if ( *endptr != '\0' ) + { + fprintf( stderr, "Cannot parse string string \"%s\" as a float value\n", value ); + exit( -1 ); + } + } + else + { + fprintf( stderr, "Unsupported optional key: %s\n", key ); + exit( -1 ); + } + + parse_pos = readNextMetadataChunkFrom( parse_pos, line, "\n" ); + } + + return; +} + +static void parseObjectPosition( + char *line, + IVAS_REND_AudioObjectPosition *position, + uint16_t *positionDuration ) +{ + char *endptr; + + readNextMetadataChunk( line, "," ); + *positionDuration = (uint16_t) strtol( line, &endptr, 10 ); + + if ( *endptr != '\0' ) + { + fprintf( stderr, "Error reading metadata\n" ); + exit( -1 ); + } + + readNextMetadataChunk( line, "," ); + position->azimuth = strtof( line, &endptr ); + + if ( *endptr != '\0' ) + { + fprintf( stderr, "Error reading metadata\n" ); + exit( -1 ); + } + + readNextMetadataChunk( line, "\n" ); + position->elevation = strtof( line, &endptr ); + if ( *endptr != '\0' ) + { + fprintf( stderr, "Error reading metadata\n" ); + exit( -1 ); + } + + return; +} + +static void parseIsm( + char *line, + char *inDir, + InputConfig *inConfig, + IsmPositionProvider *positionProvider, + const int32_t idx ) +{ + uint32_t numberOfObjectPositionsToRead; + uint32_t i; + + readNextMetadataChunk( line, "\n" ); + parseInt32( line, &inConfig->audioObjects[idx].inputChannelIndex ); + --inConfig->audioObjects[idx].inputChannelIndex; /* Convert from 1-indexing */ + + readNextMetadataChunk( line, "\n" ); + + /* Try to interpret line as number of positions to read */ + if ( parseUint32( line, &numberOfObjectPositionsToRead ) == 0 ) + { + positionProvider->numPositions[idx] = numberOfObjectPositionsToRead; + positionProvider->positions[idx] = count_malloc( numberOfObjectPositionsToRead * sizeof( IVAS_REND_AudioObjectPosition ) ); + positionProvider->positionDurations[idx] = count_malloc( numberOfObjectPositionsToRead * sizeof( uint16_t ) ); + + for ( i = 0; i < numberOfObjectPositionsToRead; ++i ) + { + parseObjectPosition( line, &positionProvider->positions[idx][i], &positionProvider->positionDurations[idx][i] ); + } + } + else /* If not a number, it is a relative path from main metadata file to a metadata file */ + { + char fullpath[FILENAME_MAX]; + fullpath[0] = '\0'; + strncat( fullpath, inDir, strlen( inDir ) ); + strncat( fullpath, line, sizeof( fullpath ) - strlen( fullpath ) - 1 ); + if ( ( positionProvider->ismReaders[idx] = IsmFileReader_open( fullpath ) ) == NULL ) + { + fprintf( stderr, "Error: ISM input metadata file %s could not be opened\n", fullpath ); + exit( -1 ); + } + } + + /* Read optional values */ + parseOptionalInputValues( line, &inConfig->audioObjects[idx].gain_dB ); + + return; +} + +static void parseSba( + char *line, + InputConfig *inConfig, + const int32_t idx ) +{ + uint8_t ambiOrder; + + readNextMetadataChunk( line, "\n" ); + parseInt32( line, &inConfig->ambisonicsBuses[idx].inputChannelIndex ); + --inConfig->ambisonicsBuses[idx].inputChannelIndex; /* Convert from 1-indexing */ + + readNextMetadataChunk( line, "\n" ); + parseUint8( line, &ambiOrder ); + inConfig->ambisonicsBuses[idx].audioConfig = ambisonicsOrderToEnum( ambiOrder ); + + /* Read optional values */ + parseOptionalInputValues( line, &inConfig->ambisonicsBuses[idx].gain_dB ); + + return; +} + +static void parseMc( + char *line, + InputConfig *inConfig, + const int32_t idx ) +{ + readNextMetadataChunk( line, "\n" ); + parseInt32( line, &inConfig->multiChannelBuses[idx].inputChannelIndex ); + --inConfig->multiChannelBuses[idx].inputChannelIndex; /* Convert from 1-indexing */ + + readNextMetadataChunk( line, "\n" ); + IVAS_REND_AudioConfig cfg = parseAudioConfig( line ); + if ( cfg == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM ) + { + parseCustomLayoutFile( line, &inConfig->inSetupCustom ); + } + else + { + inConfig->multiChannelBuses[idx].audioConfig = cfg; + } + + /* Read optional values */ + parseOptionalInputValues( line, &inConfig->multiChannelBuses[idx].gain_dB ); + + return; +} + +static void parseMasa( + char *line, + char *inDir, + InputConfig *inConfig, + MasaFileReader **masaReaders, + const int32_t idx ) +{ + readNextMetadataChunk( line, "\n" ); + parseInt32( line, &inConfig->masaBuses[idx].inputChannelIndex ); + --inConfig->masaBuses[idx].inputChannelIndex; /* Convert from 1-indexing */ + + readNextMetadataChunk( line, "\n" ); + + /* Allow both just the number of TCs or MASAx. parseStrToAudioCfg() only accepts MASAx, so prepend if necessary. */ + if ( strncmp( line, "MASA", 4 ) != 0 ) + { + char numTcs = *line; + sprintf( line, "MASA%c", numTcs ); + } + + inConfig->masaBuses[idx].audioConfig = parseAudioConfig( line ); + + readNextMetadataChunk( line, "\n" ); + + char fullpath[FILENAME_MAX]; + fullpath[0] = '\0'; + strncat( fullpath, inDir, strlen( inDir ) ); + strncat( fullpath, line, sizeof( fullpath ) - strlen( fullpath ) - 1 ); + + if ( ( masaReaders[idx] = MasaFileReader_open( fullpath ) ) == NULL ) + { + fprintf( stderr, "Error: MASA metadata file %s could not be opened\n", fullpath ); + exit( -1 ); + } + + /* Read optional values */ + parseOptionalInputValues( line, &inConfig->masaBuses[idx].gain_dB ); + + return; +} + +static ivas_error parseCustomLayoutFile( + const char *filePath, + IVAS_CUSTOM_LS_DATA *pLsSetupCustom ) +{ + LsCustomFileReader *hLsCustomReader = NULL; + IVAS_CUSTOM_LS_DATA hLsCustomData; + ivas_error error; + + if ( ( error = CustomLsReader_open( filePath, &hLsCustomReader ) ) != IVAS_ERR_OK ) + { + return error; + } + + if ( CustomLsFileReading( hLsCustomReader, &hLsCustomData ) != LS_CUSTOM_FILEREADER_NO_ERROR ) + { + return error; + } + + pLsSetupCustom->num_spk = hLsCustomData.num_spk; + mvr2r( hLsCustomData.azimuth, pLsSetupCustom->azimuth, hLsCustomData.num_spk ); + mvr2r( hLsCustomData.elevation, pLsSetupCustom->elevation, hLsCustomData.num_spk ); + + /* Loudspeaker LFE */ + pLsSetupCustom->num_lfe = hLsCustomData.num_lfe; + mvs2s( hLsCustomData.lfe_idx, pLsSetupCustom->lfe_idx, hLsCustomData.num_lfe ); + + CustomLsReader_close( &hLsCustomReader ); + + return IVAS_ERR_OK; +} + +static void parseMetadata( + char *metadataString, + char *inDir, + InputConfig *inConfig, + IsmPositionProvider *positionProvider, + MasaFileReader **masaReaders ) +{ + char line[RENDERER_MAX_METADATA_LINE_LENGTH]; + char *delimiter; + char *token; + uint8_t totalNumberOfAudioObjects; + uint8_t counterChannelAudioObjects; + uint8_t counterAmbisonicsAudioObjects; + uint8_t counterMonoAudioObjects; + uint8_t counterMasaInputs; + uint8_t num_parsed_inputs; + + delimiter = "\n"; + + token = strtok( metadataString, delimiter ); + if ( token == NULL ) + { + fprintf( stderr, "Unexpected metadata format\n" ); + exit( -1 ); + } + if ( !sscanf( token, "%s", line ) ) + { + fprintf( stderr, "Unexpected metadata format\n" ); + exit( -1 ); + } + + parseUint8( line, &totalNumberOfAudioObjects ); + if ( totalNumberOfAudioObjects <= 0 ) + { + fprintf( stderr, "Invalid metadata: number of inputs should be > 0\n" ); + exit( -1 ); + } + + num_parsed_inputs = 0; + counterChannelAudioObjects = 0; + counterAmbisonicsAudioObjects = 0; + counterMonoAudioObjects = 0; + counterMasaInputs = 0; + + readNextMetadataChunk( line, delimiter ); + + while ( num_parsed_inputs < totalNumberOfAudioObjects ) + { + /* `line` will already contain the identifier ("MC", "SBA" or "ISM") after previous iteration */ + if ( strcmp( line, "MC" ) == 0 ) + { + ++counterChannelAudioObjects; + if ( counterChannelAudioObjects > RENDERER_MAX_MC_INPUTS ) + { + fprintf( stderr, "Metadata exceeds the supported number of MC inputs\n" ); + exit( -1 ); + } + parseMc( line, inConfig, counterChannelAudioObjects - 1 ); + } + else if ( strcmp( line, "SBA" ) == 0 ) + { + ++counterAmbisonicsAudioObjects; + if ( counterAmbisonicsAudioObjects > RENDERER_MAX_SBA_INPUTS ) + { + fprintf( stderr, "Metadata exceeds the supported number of SBA inputs\n" ); + exit( -1 ); + } + parseSba( line, inConfig, counterAmbisonicsAudioObjects - 1 ); + } + else if ( strcmp( line, "ISM" ) == 0 ) + { + ++counterMonoAudioObjects; + if ( counterMonoAudioObjects > RENDERER_MAX_ISM_INPUTS ) + { + fprintf( stderr, "Metadata exceeds the supported number of ISM inputs\n" ); + exit( -1 ); + } + parseIsm( line, inDir, inConfig, positionProvider, counterMonoAudioObjects - 1 ); + } + else if ( strcmp( line, "MASA" ) == 0 ) + { + ++counterMasaInputs; + if ( counterMasaInputs > RENDERER_MAX_MASA_INPUTS ) + { + fprintf( stderr, "Metadata exceeds the supported number of MASA inputs\n" ); + exit( -1 ); + } + parseMasa( line, inDir, inConfig, masaReaders, counterMasaInputs - 1 ); + } + else if ( isEmptyString( line ) ) + { + fprintf( stderr, "Metadata string too short - expected %d inputs, found %d.\n", totalNumberOfAudioObjects, num_parsed_inputs ); + exit( -1 ); + } + else + { + fprintf( stderr, "Unexpected metadata identifier\n" ); + exit( -1 ); + } + + ++num_parsed_inputs; + } + + inConfig->numAudioObjects = counterMonoAudioObjects; + inConfig->numAmbisonicsBuses = counterAmbisonicsAudioObjects; + inConfig->numMultiChannelBuses = counterChannelAudioObjects; + inConfig->numMasaBuses = counterMasaInputs; + positionProvider->numObjects = counterMonoAudioObjects; + + /* check for trailing text */ + token = strtok( NULL, delimiter ); + if ( token != NULL && sscanf( token, "%s", line ) ) + { + fprintf( stderr, "Trailing text in metadata file\n" ); + exit( -1 ); + } + + return; +} + +static void parseSceneDescriptionFile( + char *path, + char *audioFilePath, + InputConfig *inConfig, + IsmPositionProvider *positionProvider, + MasaFileReader **masaReaders ) +{ + uint32_t inAudioFilePathLen; + char inAudioFilePath[FILENAME_MAX]; + uint32_t mtdStrLen; + char mtdStr[RENDERER_MAX_METADATA_LENGTH]; + char inDir[FILENAME_MAX]; + char *lastSlash = NULL; + + inAudioFilePathLen = FILENAME_MAX; + mtdStrLen = RENDERER_MAX_METADATA_LENGTH; + splitConfigFile( path, mtdStr, &mtdStrLen, inAudioFilePath, &inAudioFilePathLen ); + + remove_cr( mtdStr ); + convert_backslash( inAudioFilePath ); + + /* Trim config file path to get path to the dir containing it */ + lastSlash = strrchr( path, SEP_FOLDER ); + inDir[0] = '\0'; + if ( lastSlash != NULL ) + { + strncat( inDir, path, ( lastSlash - path + 1 ) ); + } + + /* Append audio file path (relative to config file location) + * to config file location path to get full absolute path */ + strcpy( audioFilePath, inDir ); + strncat( audioFilePath, inAudioFilePath, inAudioFilePathLen ); + + parseMetadata( mtdStr, inDir, inConfig, positionProvider, masaReaders ); +} + +static void printSupportedAudioConfigs() +{ + uint16_t i; + const char *supportedFormats[] = { + "MONO", + "STEREO", + "5_1", + "5_1_2", + "5_1_4", + "7_1", + "7_1_4", + "Path to Custom Loudspeaker .txt file", + "FOA", + "HOA2", + "HOA3", + "ISMx (input only)", + "MASAx (input only)", + "BINAURAL (output only)", + "BINAURAL_ROOM (output only)", + }; + + fprintf( stdout, "Supported audio formats:\n" ); + for ( i = 0; i < sizeof( supportedFormats ) / sizeof( *supportedFormats ); i++ ) + { + fprintf( stdout, "%s\n", supportedFormats[i] ); + } + + return; +} + +// VE2AT: possibly move these functions to cmdln_parser.c ? +static void convert_backslash( + char *str ) +{ + int16_t i, len; + + /* check that all backslashes are correct on the given platform */ + len = (int16_t) strlen( str ); + + for ( i = 0; i < len; i++ ) + { +#ifdef _WIN32 + if ( str[i] == '/' ) + { + str[i] = '\\'; + } +#else + if ( str[i] == '\\' ) + { + str[i] = '/'; + } +#endif + } + + return; +} + +static void remove_cr( char *str ) +{ + char *pos; + + /* remove all \r characters from the string */ + pos = strchr( str, '\r' ); + while ( pos != NULL ) + { + strcpy( pos, pos + 1 ); + pos = strchr( pos, '\r' ); + } + + return; +} + +static void clearString( + char *str ) +{ + str[0] = '\0'; + + return; +} + +static bool isEmptyString( + const char *str ) +{ + return str[0] == '\0'; +} + +/*--------------------------------------------------------------------------* + * convertInputBuffer() + * + * Convert input buffer from WAV/PCM file (int16_t, interleaved) to a format + * accepted by the renderer (float, packed) + *--------------------------------------------------------------------------*/ + +static void convertInputBuffer( + const int16_t *intBuffer, + const int16_t numIntSamplesPerChannel, + const int16_t numFloatSamplesPerChannel, + const int16_t numChannels, + float *floatBuffer ) +{ + int16_t chnl, smpl, i; + + i = 0; + + for ( smpl = 0; smpl < numFloatSamplesPerChannel; ++smpl ) + { + for ( chnl = 0; chnl < numChannels; ++chnl ) + { + if ( i < numIntSamplesPerChannel ) + { + floatBuffer[chnl * numFloatSamplesPerChannel + smpl] = (float) intBuffer[i]; + } + else + { + floatBuffer[chnl * numFloatSamplesPerChannel + smpl] = 0.f; + } + + ++i; + } + } + + return; +} + +/*--------------------------------------------------------------------------* + * convertOutputBuffer() + * + * Convert output buffer from the renderer (float, packed) to a format ready + * for writing to a WAV/PCM file (int16_t, interleaved) + *--------------------------------------------------------------------------*/ + +static void convertOutputBuffer( + const float *floatBuffer, + const int16_t numSamplesPerChannel, + const int16_t numChannels, + int16_t *intBuffer ) +{ + int16_t chnl, smpl, i; + + i = 0; + + for ( smpl = 0; smpl < numSamplesPerChannel; ++smpl ) + { + for ( chnl = 0; chnl < numChannels; ++chnl ) + { + intBuffer[i] = (int16_t) roundf( floatBuffer[chnl * numSamplesPerChannel + smpl] ); + + ++i; + } + } + + return; +} +#else +int main( + int argc, + char **argv ) +{ + (void) argc; + (void) argv; + fprintf( stderr, "Enable EXT_RENDERER in options.h to use the external renderer.\n" ); + return 0; +} +#endif diff --git a/ci/build_all_linux.sh b/ci/build_all_linux.sh index 65ea1461fad305aedf0d4c78be8783b5752d3bc0..6dcfd92ad9549ba1314a67607b4996cf5b4c4e1f 100755 --- a/ci/build_all_linux.sh +++ b/ci/build_all_linux.sh @@ -9,8 +9,6 @@ fi make clean && make all -j # build unittests make unittests -j -# build prerenderer -make -C scripts/prerenderer -j # build standalone TD object renderer make -C scripts/td_object_renderer/object_renderer_standalone -j diff --git a/ci/disable_ram_counting.py b/ci/disable_ram_counting.py new file mode 100755 index 0000000000000000000000000000000000000000..3c891535081528a249751eed987d88f589e55f9d --- /dev/null +++ b/ci/disable_ram_counting.py @@ -0,0 +1,20 @@ +import re +import os + +FILE_PATH = os.path.join(os.path.dirname(__file__), "..", "lib_com", "options.h") +RE_TO_COMMENT_OUT = re.compile(r"#define\s+RAM_COUNTING_TOOL") + + +def main(): + with open(FILE_PATH, "r", encoding="utf-8") as file: + lines = file.readlines() + + for i, line in enumerate(lines): + lines[i] = RE_TO_COMMENT_OUT.sub(lambda x: f"/* {x.group(0)} */", line) + + with open(FILE_PATH, "w", encoding="utf-8") as file: + file.writelines(lines) + + +if __name__ == "__main__": + main() diff --git a/lib_com/common_api_types.h b/lib_com/common_api_types.h index c27b7bc62852ca914519749fc693f8bf5d3fb54a..0fd3e7c5525a940ceede0b9021bace861ce3ff7a 100644 --- a/lib_com/common_api_types.h +++ b/lib_com/common_api_types.h @@ -47,6 +47,9 @@ #define IVAS_MAX_NUM_OBJECTS 4 #define IVAS_MAX_OUTPUT_CHANNELS 16 #define IVAS_CLDFB_NO_CHANNELS_MAX ( 60 ) +#ifdef EXT_RENDERER +#define IVAS_MAX_INPUT_LFE_CHANNELS 4 +#endif /*----------------------------------------------------------------------------------* * Common API structures @@ -76,7 +79,11 @@ typedef struct _IVAS_ISM_METADATA float gainFactor; } IVAS_ISM_METADATA; +#ifdef EXT_RENDERER +typedef struct +#else typedef struct _IVAS_QUATERNION +#endif { float w, x, y, z; diff --git a/lib_com/ivas_cnst.h b/lib_com/ivas_cnst.h index d56a0bec36c8a3df2f69ae1a379bc8afe81fbf04..0074be76d2a476bc284839a5e2aea8d90695060f 100644 --- a/lib_com/ivas_cnst.h +++ b/lib_com/ivas_cnst.h @@ -107,6 +107,10 @@ typedef enum AUDIO_CONFIG_ISM2, /* ISM2 */ AUDIO_CONFIG_ISM3, /* ISM3 */ AUDIO_CONFIG_ISM4, /* ISM4 */ +#ifdef EXT_RENDERER /* TODO tmu : temporary, or use something like IVAS_ENC input format */ + AUDIO_CONFIG_MASA1, /* MASA1 */ + AUDIO_CONFIG_MASA2, /* MASA2 */ +#endif AUDIO_CONFIG_EXTERNAL /* external renderer */ } AUDIO_CONFIG; @@ -189,6 +193,10 @@ typedef enum #define IVAS_MAX_SBA_ORDER 3 /* Maximum supported Ambisonics order */ +#ifdef EXT_RENDERER +#define IVAS_LIMITER_THRESHOLD 32729 /* -0.01 dBFS */ +#define IVAS_LIMITER_ATTACK_SECONDS 0.005f +#endif #define IVAS_NUM_SUPPORTED_FS 3 /* number of supported sampling-rates in IVAS */ /*----------------------------------------------------------------------------------* @@ -1359,12 +1367,21 @@ typedef enum #define BINAURAL_COHERENCE_DIFFERENCE_BINS 9 /* Number of bins for direction-dependent diffuse-field binaural coherence */ +#ifdef EXT_RENDERER +#define HEADROT_ORDER 3 +#define HEADROT_SHMAT_DIM ( ( HEADROT_ORDER + 1 ) * ( HEADROT_ORDER + 1 ) ) +#define HEADROT_SHMAT_DIM2 ( HEADROT_SHMAT_DIM * HEADROT_SHMAT_DIM ) +#endif /*----------------------------------------------------------------------------------* * TD Binaural Object renderer *----------------------------------------------------------------------------------*/ +#ifdef EXT_RENDERER +#define MAX_NUM_TDREND_CHANNELS 16 /* max. number of channels in TD renderer (objects or loudspeaker channels) */ +#else #define MAX_NUM_TDREND_CHANNELS 11 /* max. number of channels in TD renderer (objects or loudspeaker channels) */ +#endif #define SFX_SPAT_BIN_MAX_NO_OF_OUTPUT_SAMPLES 288 /* 288 = 6 msec @ 48 kHz. */ #define HRTF_MODEL_N_SECTIONS 3 /* No. sections used in approximate evaluation of model */ diff --git a/lib_com/ivas_error.h b/lib_com/ivas_error.h index 8d09e48bbde59f3064243a555449a3aca7f32357..fdf03190dec3689c577357dd56a31e8027f0361d 100644 --- a/lib_com/ivas_error.h +++ b/lib_com/ivas_error.h @@ -59,7 +59,11 @@ typedef enum IVAS_ERR_INVALID_CICP_INDEX, IVAS_ERR_INVALID_BITRATE, IVAS_ERR_INVALID_MASA_CONFIG, +#ifdef EXT_RENDERER + IVAS_ERR_TOO_MANY_INPUTS, +#else IVAS_ERR_TOO_MANY_OBJECT_INPUTS, +#endif IVAS_ERR_INDEX_OUT_OF_BOUNDS, IVAS_ERR_RECONFIGURE_NOT_SUPPORTED, IVAS_ERR_INVALID_FEC_CONFIG, @@ -86,6 +90,13 @@ typedef enum IVAS_ERR_FILE_READER_TIMESTAMP_MISMATCH, IVAS_ERR_ISM_FILE_READER_INVALID_METADATA_FORMAT, IVAS_ERR_INVALID_MASA_FORMAT_METADATA_FILE, +#ifdef EXT_RENDERER + IVAS_ERR_NUM_CHANNELS_UNKNOWN, + IVAS_ERR_INVALID_CUSTOM_LS_LAYOUT, + IVAS_ERR_INVALID_INPUT_ID, + IVAS_ERR_WRONG_NUM_CHANNELS, + IVAS_ERR_INVALID_BUFFER_SIZE, +#endif #ifdef DEBUG_AGC_ENCODER_CMD_OPTION IVAS_ERR_INVALID_AGC, #endif @@ -150,6 +161,20 @@ static inline const char *ivas_error_to_string( ivas_error error_code ) return "Internal error"; case IVAS_ERR_INTERNAL_FATAL: return "Internal fatal error"; +#ifdef EXT_RENDERER + case IVAS_ERR_INVALID_SAMPLING_RATE: + return "Invalid sampling rate"; + case IVAS_ERR_INVALID_OUTPUT_FORMAT: + return "Invalid output format"; + case IVAS_ERR_INVALID_CUSTOM_LS_LAYOUT: + return "Invalid custom loudspeaker layout"; + case IVAS_ERR_INVALID_INPUT_ID: + return "Invalid input ID"; + case IVAS_ERR_WRONG_NUM_CHANNELS: + return "Wrong number of channels"; + case IVAS_ERR_INVALID_BUFFER_SIZE: + return "Invalid buffer size"; +#endif case IVAS_ERR_FAILED_FILE_OPEN: return "File open error"; case IVAS_ERR_FAILED_FILE_WRITE: diff --git a/lib_com/ivas_prot.h b/lib_com/ivas_prot.h index b625646b5ba9602e0f33a9a13afe5f9fb07daf16..eac8ec005aaf66a5f34f8b39e21ed8c2be49e6ee 100644 --- a/lib_com/ivas_prot.h +++ b/lib_com/ivas_prot.h @@ -3144,6 +3144,16 @@ ivas_error ivas_sba_get_hoa_dec_matrix( const int16_t ambisonics_order /* i : Ambisonics order */ ); +#ifdef EXT_RENDERER +void ivas_sba_mtx_mult( + float output_f[][L_FRAME48k], /* i/o: synthesized core-corder transport channels/DirAC output */ + const int16_t output_frame, /* i : frame length per channel */ + const int16_t nchan_in, /* i : Number of ambisonic channels */ + const IVAS_OUTPUT_SETUP output_setup, /* i : Output configuration */ + const float *mtx_hoa_decoder /* o : HOA decoding matrix */ +); +#endif + /*----------------------------------------------------------------------------------* * DirAC prototypes *----------------------------------------------------------------------------------*/ @@ -4523,17 +4533,33 @@ void ivas_binaural_add_LFE( ); void QuatToRotMat( +#ifdef EXT_RENDERER + const IVAS_QUATERNION quat, /* i : quaternion describing the rotation */ +#else const Quaternion quat, /* i : quaternion describing the rotation */ +#endif float Rmat[3][3] /* o : real-space rotation matrix for this rotation */ ); void Quat2Euler( +#ifdef EXT_RENDERER + const IVAS_QUATERNION quat, /* i : quaternion describing the rotation */ +#else const Quaternion quat, /* i : quaternion describing the rotation */ +#endif float *yaw, /* o : yaw */ float *pitch, /* o : pitch */ float *roll /* o : roll */ ); +#ifdef EXT_RENDERER +void SHrotmatgen( + float SHrotmat[SBA_NHARM_HOA3][SBA_NHARM_HOA3], /* o : SHD rotation matrix */ + float Rmat[3][3], /* i : real-space rotation matrix */ + const int16_t order /* i : ambisonics order */ +); +#endif + void rotateAziEle( float azi_in, /* i : output elevation */ float ele_in, /* i : input elevation */ @@ -4558,7 +4584,9 @@ ivas_error ivas_headTrack_open( void rotateFrame_shd( HEAD_TRACK_DATA_HANDLE hHeadTrackData, /* i : head track handle */ float output[][L_FRAME48k], /* i/o: unrotated HOA3 signal buffer in TD */ +#ifndef EXT_RENDERER const int32_t output_Fs, /* i : output sampling frequency */ +#endif const int16_t subframe_len, /* i : subframe length per channel */ const IVAS_OUTPUT_SETUP hTransSetup, /* i : format for rotation */ const int16_t subframe_idx /* i : subframe index */ @@ -4567,7 +4595,9 @@ void rotateFrame_shd( void rotateFrame_sd( HEAD_TRACK_DATA_HANDLE hHeadTrackData, /* i : head track handle */ float output[][L_FRAME48k], /* i/o: unrotated SD signal buffer in TD */ +#ifndef EXT_RENDERER const int32_t output_Fs, /* i : output sampling frequency */ +#endif const int16_t subframe_len, /* i : subframe length per channel */ const IVAS_OUTPUT_SETUP hTransSetup, /* i : format for rotation */ const EFAP_HANDLE hEFAPdata, /* i : EFAP structure */ @@ -4738,7 +4768,11 @@ ivas_error ivas_ls_custom_output_init( void ivas_ls_custom_setup( IVAS_OUTPUT_SETUP_HANDLE hOutSetup, /* o : IVAS output setup handle */ +#ifdef EXT_RENDERER + const LSSETUP_CUSTOM_STRUCT *hLsSetupCustom /* i : Custom loudspeaker setup handle */ +#else const LSSETUP_CUSTOM_HANDLE hLsSetupCustom /* i : Custom loudspeaker setup handle */ +#endif ); diff --git a/lib_com/ivas_tools.c b/lib_com/ivas_tools.c index 3c941ccc770657db2f2a7f6cfabb1d74cd1c55d9..b8fbaa833ff71f8a22d5fb270b93b3f4a5e05d9e 100644 --- a/lib_com/ivas_tools.c +++ b/lib_com/ivas_tools.c @@ -144,6 +144,16 @@ uint32_t ivas_syn_output( } } +#ifdef DEC_TO_REND_FLOAT_DUMP + for ( i = 0; i < output_frame; ++i ) + { + for ( n = 0; n < n_channels; ++n ) + { + dbgwrite( &synth[n][i], sizeof( float ), 1, 1, "./float_out.raw" ); + } + } +#endif + return noClipping; } diff --git a/lib_com/options.h b/lib_com/options.h index 8e8a24dd2ed01e212a06ede2044a6d8b371b92bb..7e32d9532610d0415339b679707f8207c924e119 100644 --- a/lib_com/options.h +++ b/lib_com/options.h @@ -145,6 +145,8 @@ /*#define FIX_I4_OL_PITCH*/ /* fix open-loop pitch used for EVS core switching */ #define FIX_I1_113 /* under review : MCT bit distribution optimization for SBA high bitrates*/ #define PRINT_SBA_ORDER /* Issue 179: print-out also the SBA order of IVAS SBA format to stdout */ +#define EXT_RENDERER /* FhG: external renderer library and standalone application */ +#define FIX_EFAP_MATH /* fix for EFAP: remove angle quantization and a bug in polygon lookup causing incorrect gains. minor tweak for ALLRAD. non-BE for modes using EFAP */ #define FIX_124_DONT_ALLOC_PLCINFO_IN_IVAS /* Issue 124: do not allocate unused plc struct in IVAS modes which is only used in EVS mono */ #define FIX_MCT_PLC_RECOVERY /* Issue 184: scale the old synthesis part correctly in the first good frame after lost frames in MCT modes - to be activated after previous switch is merged */ #define SBA_BR_SWITCHING /* Issue 114: Changes for sba bit rate switching*/ diff --git a/lib_dec/ivas_dirac_dec_binaural_functions.c b/lib_dec/ivas_dirac_dec_binaural_functions.c index ca9f0a4587a985e94d8fedb4b8372f09fb90fffb..14181931391bb3582b822a3237c61af313fb2e0a 100644 --- a/lib_dec/ivas_dirac_dec_binaural_functions.c +++ b/lib_dec/ivas_dirac_dec_binaural_functions.c @@ -29,7 +29,7 @@ the United Nations Convention on Contracts on the International Sales of Goods. *******************************************************************************************************/ - +// VE2AT: move to lib_rend ? #include #include "options.h" #include @@ -37,7 +37,7 @@ #include "prot.h" #include "ivas_prot.h" #include "ivas_cnst.h" -#include "ivas_rom_binauralRenderer.h" +#include "ivas_rom_binauralRenderer.h" // VE2AT: what about to put these includes ust into ivas_rom_rend.c ? #include "ivas_rom_dec.h" #ifdef DEBUGGING #include "debug.h" diff --git a/lib_dec/ivas_ism_renderer.c b/lib_dec/ivas_ism_renderer.c index 0e7f5f864a62bfb02000ac2a05ed1ccf1bf132f1..f27d49b11104ba9b5cadd1874c08717f43099768 100644 --- a/lib_dec/ivas_ism_renderer.c +++ b/lib_dec/ivas_ism_renderer.c @@ -29,7 +29,7 @@ the United Nations Convention on Contracts on the International Sales of Goods. *******************************************************************************************************/ - +// VE2AT: move to lib_rend ? #include #include "options.h" #include "ivas_cnst.h" diff --git a/lib_dec/ivas_mono_dmx_renderer.c b/lib_dec/ivas_mono_dmx_renderer.c index 468b0215b0f90bb7b486581bc5c69eacbdb8cfa0..20b677075eaa648d58268a44ace8c7859e665410 100644 --- a/lib_dec/ivas_mono_dmx_renderer.c +++ b/lib_dec/ivas_mono_dmx_renderer.c @@ -29,7 +29,7 @@ the United Nations Convention on Contracts on the International Sales of Goods. *******************************************************************************************************/ - +// VE2AT: move to lib_rend ? #include #include "options.h" #include diff --git a/lib_dec/ivas_out_setup_conversion.c b/lib_dec/ivas_out_setup_conversion.c index 9ed27e56c3d21f63d1d14675ffcb76bc8c47a52a..55f53bae73a1df246d0879d1dcc52053858f425f 100644 --- a/lib_dec/ivas_out_setup_conversion.c +++ b/lib_dec/ivas_out_setup_conversion.c @@ -29,7 +29,7 @@ the United Nations Convention on Contracts on the International Sales of Goods. *******************************************************************************************************/ - +// VE2AT: move to lib_rend ? #include #include #include "options.h" diff --git a/lib_dec/ivas_rom_dec.c b/lib_dec/ivas_rom_dec.c index 39918776e676d4c1a6ec8dd6b22aff5ef07df2c2..c79bc3801903457fbcf12b9a0815351845d2dc08 100644 --- a/lib_dec/ivas_rom_dec.c +++ b/lib_dec/ivas_rom_dec.c @@ -512,393 +512,11 @@ const int16_t sba_map_tc[8] = 0, 1, 2, 3, 4, 8, 9, 15 }; -/*----------------------------------------------------------------------------------* - * LS Renderer ROM tables - *----------------------------------------------------------------------------------*/ - - /* All matrices are stored with dimensions nchan_in x nchan_out */ - /* Downmix matrices */ -const float ls_conversion_cicpX_mono[12][1] = -{ - {1.00000000f}, - {1.00000000f}, - {0.70710677f}, - {0.70710677f}, - {0.79999995f}, - {0.79999995f}, - {0.79999995f}, - {0.79999995f}, - {0.849999964f}, - {0.849999964f}, - {0.849999964f}, - {0.849999964f} -}; - -const float ls_conversion_cicpX_stereo[12][2] = -{ - {1.00000000f, 0.00000000f}, - {0.00000000f, 1.00000000f}, - {0.70710677f, 0.70710677f}, - {0.70710677f, 0.70710677f}, - {0.79999995f, 0.00000000f}, - {0.00000000f, 0.79999995f}, - {0.79999995f, 0.00000000f}, - {0.00000000f, 0.79999995f}, - {0.849999964f, 0.000000000f}, - {0.000000000f, 0.849999964f}, - {0.849999964f, 0.000000000f}, - {0.000000000f, 0.849999964f} -}; - -const LS_CONVERSION_MATRIX ls_conversion_cicp12_cicp6[] = -{ - /* First row indicates the number of non-zero elements */ - {8, 0.0f}, - /* Index of non-zero element, value of non-zero element*/ - {0, 1.000000000f}, - {7, 1.000000000f}, - {14, 1.000000000f}, - {21, 1.000000000f}, - {28, 1.000000000f}, - {35, 1.000000000f}, - {40, 1.000000000f}, - {47, 1.000000000f} -}; - -const LS_CONVERSION_MATRIX ls_conversion_cicp14_cicp6[] = -{ - /* First row indicates the number of non-zero elements */ - {8, 0.0f}, - /* Index of non-zero element, value of non-zero element*/ - {0, 1.000000000f}, - {7, 1.000000000f}, - {14, 1.000000000f}, - {21, 1.000000000f}, - {28, 1.000000000f}, - {35, 1.000000000f}, - {36, 0.849999964f}, - {43, 0.849999964f} -}; - -const LS_CONVERSION_MATRIX ls_conversion_cicp14_cicp12[] = -{ - /* First row indicates the number of non-zero elements */ - {8, 0.0f}, - /* Index of non-zero element, value of non-zero element*/ - {0, 1.000000000f}, - {9, 1.000000000f}, - {18, 1.000000000f}, - {27, 1.000000000f}, - {36, 1.000000000f}, - {45, 1.000000000f}, - {48, 0.849999964f}, - {57, 0.849999964f} -}; - -const LS_CONVERSION_MATRIX ls_conversion_cicp16_cicp6[] = -{ - /* First row indicates the number of non-zero elements */ - {10, 0.0f}, - /* Index of non-zero element, value of non-zero element*/ - {0, 1.000000000f}, - {7, 1.000000000f}, - {14, 1.000000000f}, - {21, 1.000000000f}, - {28, 1.000000000f}, - {35, 1.000000000f}, - {36, 0.849999964f}, - {43, 0.849999964f}, - {52, 0.849999964f}, - {59, 0.849999964f} -}; - -const LS_CONVERSION_MATRIX ls_conversion_cicp16_cicp12[] = -{ - /* First row indicates the number of non-zero elements */ - {10, 0.0f}, - /* Index of non-zero element, value of non-zero element*/ - {0, 1.000000000f}, - {9, 1.000000000f}, - {18, 1.000000000f}, - {27, 1.000000000f}, - {36, 1.000000000f}, - {45, 1.000000000f}, - {48, 0.849999964f}, - {57, 0.849999964f}, - {68, 0.849999964f}, - {77, 0.849999964f} - -}; - -const LS_CONVERSION_MATRIX ls_conversion_cicp16_cicp14[] = -{ - /* First row indicates the number of non-zero elements */ - {10, 0.0f}, - /* Index of non-zero element, value of non-zero element*/ - {0, 1.000000000f}, - {9, 1.000000000f}, - {18, 1.000000000f}, - {27, 1.000000000f}, - {36, 1.000000000f}, - {45, 1.000000000f}, - {54, 1.000000000f}, - {63, 1.000000000f}, - {68, 0.849999964f}, - {77, 0.849999964f}, -}; - -const LS_CONVERSION_MATRIX ls_conversion_cicp19_cicp6[] = -{ - /* First row indicates the number of non-zero elements */ - {14, 0.0f}, - /* Index of non-zero element, value of non-zero element*/ - {0, 1.000000000f}, - {7, 1.000000000f}, - {14, 1.000000000f}, - {21, 1.000000000f}, - {28, 1.000000000f}, - {35, 1.000000000f}, - {36, 0.367322683f}, - {40, 0.930093586f}, - {43, 0.367322683f}, - {47, 0.930093586f}, - {48, 0.849999964f}, - {55, 0.849999964f}, - {64, 0.849999964f}, - {71, 0.849999964f} -}; - -const LS_CONVERSION_MATRIX ls_conversion_cicp19_cicp12[] = -{ - /* First row indicates the number of non-zero elements */ - {14, 0.0f}, - /* Index of non-zero element, value of non-zero element*/ - {0, 1.000000000f}, - {9, 1.000000000f}, - {18, 1.000000000f}, - {27, 1.000000000f}, - {38, 1.000000000f}, - {47, 1.000000000f}, - {48, 0.367322683f}, - {52, 0.930093586f}, - {57, 0.367322683f}, - {61, 0.930093586f}, - {64, 0.849999964f}, - {73, 0.849999964f}, - {84, 0.849999964f}, - {93, 0.849999964f} -}; - -const LS_CONVERSION_MATRIX ls_conversion_cicp19_cicp14[] = -{ - /* First row indicates the number of non-zero elements */ - {14, 0.0f}, - /* Index of non-zero element, value of non-zero element*/ - {0, 1.000000000f}, - {9, 1.000000000f}, - {18, 1.000000000f}, - {27, 1.000000000f}, - {36, 1.000000000f}, - {45, 1.000000000f}, - {48, 0.367322683f}, - {52, 0.930093586f}, - {57, 0.367322683f}, - {61, 0.930093586f}, - {70, 1.000000000f}, - {79, 1.000000000f}, - {84, 0.849999964f}, - {93, 0.849999964f} -}; - -const LS_CONVERSION_MATRIX ls_conversion_cicp19_cicp16[] = -{ - /* First row indicates the number of non-zero elements */ - {14, 0.0f}, - /* Index of non-zero element, value of non-zero element*/ - {0, 1.000000000f}, - {11, 1.000000000f}, - {22, 1.000000000f}, - {33, 1.000000000f}, - {44, 1.000000000f}, - {55, 1.000000000f}, - {60, 0.367322683f}, - {64, 0.930093586f}, - {71, 0.367322683f}, - {75, 0.930093586f}, - {86, 1.000000000f}, - {97, 1.000000000f}, - {108, 1.000000000f}, - {119, 1.000000000f} -}; - -/* Upmix matrices */ -const LS_CONVERSION_MATRIX ls_conversion_cicp12_cicp14[] = -{ - /* First row indicates the number of non-zero elements */ - {8, 0.0f}, - /* Index of non-zero element, value of non-zero element*/ - {0, 1.0f}, - {9, 1.0f}, - {18, 1.0f}, - {27, 1.0f}, - {36, 1.0f}, - {45, 1.0f}, - {52, 1.0f}, - {61, 1.0f} -}; - -const LS_CONVERSION_MATRIX ls_conversion_cicp12_cicp16[] = -{ - /* First row indicates the number of non-zero elements */ - {8, 0.0f}, - /* Index of non-zero element, value of non-zero element*/ - {0, 1.0f}, - {11, 1.0f}, - {22, 1.0f}, - {33, 1.0f}, - {44, 1.0f}, - {55, 1.0f}, - {64, 1.0f}, - {75, 1.0f} -}; - -const LS_CONVERSION_MATRIX ls_conversion_cicp12_cicp19[] = -{ - /* First row indicates the number of non-zero elements */ - {8, 0.0f}, - /* Index of non-zero element, value of non-zero element*/ - {0, 1.0f}, - {13, 1.0f}, - {26, 1.0f}, - {39, 1.0f}, - {54, 1.0f}, - {67, 1.0f}, - {76, 1.0f}, - {89, 1.0f} -}; - -const LS_CONVERSION_MATRIX ls_conversion_cicp14_cicp19[] = -{ - /* First row indicates the number of non-zero elements */ - {8, 0.0f}, - /* Index of non-zero element, value of non-zero element*/ - {0, 1.0f}, - {13, 1.0f}, - {26, 1.0f}, - {39, 1.0f}, - {52, 1.0f}, - {65, 1.0f}, - {80, 1.0f}, - {93, 1.0f} -}; - -const LS_CONVERSION_MATRIX ls_conversion_cicp16_cicp19[] = -{ - /* First row indicates the number of non-zero elements */ - {10, 0.0f}, - /* Index of non-zero element, value of non-zero element*/ - {0, 1.0f}, - {13, 1.0f}, - {26, 1.0f}, - {39, 1.0f}, - {52, 1.0f}, - {65, 1.0f}, - {80, 1.0f}, - {93, 1.0f}, - {106, 1.0f}, - {119, 1.0f} -}; - -/* - * Mapping table of input config : output config with corresponding matrix - * NULL indicates a 1:1 mapping of existing input channels to output channels ( used for upmix ) - */ - -const LS_CONVERSION_MAPPING ls_conversion_mapping[LS_SETUP_CONVERSION_NUM_MAPPINGS] = -{ - /* Dowmix mappings - NULL is a special case for MONO / STEREO downmix */ - {AUDIO_CONFIG_5_1, AUDIO_CONFIG_MONO, NULL}, - {AUDIO_CONFIG_7_1, AUDIO_CONFIG_MONO, NULL}, - {AUDIO_CONFIG_5_1_2, AUDIO_CONFIG_MONO, NULL}, - {AUDIO_CONFIG_5_1_4, AUDIO_CONFIG_MONO, NULL}, - {AUDIO_CONFIG_7_1_4, AUDIO_CONFIG_MONO, NULL}, - - {AUDIO_CONFIG_5_1, AUDIO_CONFIG_STEREO, NULL}, - {AUDIO_CONFIG_7_1, AUDIO_CONFIG_STEREO, NULL}, - {AUDIO_CONFIG_5_1_2, AUDIO_CONFIG_STEREO, NULL}, - {AUDIO_CONFIG_5_1_4, AUDIO_CONFIG_STEREO, NULL}, - {AUDIO_CONFIG_7_1_4, AUDIO_CONFIG_STEREO, NULL}, - - {AUDIO_CONFIG_7_1, AUDIO_CONFIG_5_1, ls_conversion_cicp12_cicp6}, - - {AUDIO_CONFIG_5_1_2, AUDIO_CONFIG_5_1, ls_conversion_cicp14_cicp6}, - {AUDIO_CONFIG_5_1_2, AUDIO_CONFIG_7_1, ls_conversion_cicp14_cicp12}, - - {AUDIO_CONFIG_5_1_4, AUDIO_CONFIG_5_1, ls_conversion_cicp16_cicp6}, - {AUDIO_CONFIG_5_1_4, AUDIO_CONFIG_7_1, ls_conversion_cicp16_cicp12}, - {AUDIO_CONFIG_5_1_4, AUDIO_CONFIG_5_1_2, ls_conversion_cicp16_cicp14}, - - {AUDIO_CONFIG_7_1_4, AUDIO_CONFIG_5_1, ls_conversion_cicp19_cicp6}, - {AUDIO_CONFIG_7_1_4, AUDIO_CONFIG_7_1, ls_conversion_cicp19_cicp12}, - {AUDIO_CONFIG_7_1_4, AUDIO_CONFIG_5_1_2, ls_conversion_cicp19_cicp14}, - {AUDIO_CONFIG_7_1_4, AUDIO_CONFIG_5_1_4, ls_conversion_cicp19_cicp16}, - - /* Upmix mappings - NULL implies a 1:1 upmix */ - {AUDIO_CONFIG_STEREO, AUDIO_CONFIG_5_1, NULL}, - {AUDIO_CONFIG_STEREO, AUDIO_CONFIG_7_1, NULL}, - {AUDIO_CONFIG_STEREO, AUDIO_CONFIG_5_1_2, NULL}, - {AUDIO_CONFIG_STEREO, AUDIO_CONFIG_5_1_4, NULL}, - {AUDIO_CONFIG_STEREO, AUDIO_CONFIG_7_1_4, NULL}, - - {AUDIO_CONFIG_5_1, AUDIO_CONFIG_7_1, NULL}, - {AUDIO_CONFIG_5_1, AUDIO_CONFIG_5_1_2, NULL}, - {AUDIO_CONFIG_5_1, AUDIO_CONFIG_5_1_4, NULL}, - {AUDIO_CONFIG_5_1, AUDIO_CONFIG_7_1_4, NULL}, - - {AUDIO_CONFIG_7_1, AUDIO_CONFIG_5_1_2, ls_conversion_cicp12_cicp14}, - {AUDIO_CONFIG_7_1, AUDIO_CONFIG_5_1_4, ls_conversion_cicp12_cicp16}, - {AUDIO_CONFIG_7_1, AUDIO_CONFIG_7_1_4, ls_conversion_cicp12_cicp19}, - - {AUDIO_CONFIG_5_1_2, AUDIO_CONFIG_5_1_4, NULL}, - {AUDIO_CONFIG_5_1_2, AUDIO_CONFIG_7_1_4, ls_conversion_cicp14_cicp19}, - - {AUDIO_CONFIG_5_1_4, AUDIO_CONFIG_7_1_4, ls_conversion_cicp16_cicp19}, -}; - /*----------------------------------------------------------------------------------* * FASTCONV and PARAMETRIC binaural renderer ROM tables *----------------------------------------------------------------------------------*/ - -const float dmxmtx[BINAURAL_CHANNELS][11] = -{ - { 1.0f, 0.0f, 0.70709997f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f }, - { 0.0f, 1.0f, 0.70709997f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f }, -}; - -/* -* 0 = 30,0 -* 1 = -30,0 -* 2 = 0,0 -* 3 = 135,0 -* 4 = -135,0 -* 5 = 110,0 -* 6 = -110,0 -* 7 = 90,0 -* 8 = -90,0 -* 9 = 30,35 -* 10 = -30,35 -* 11 = 110,35 -* 12 = -110,35 -* 13 = 135, 35 -* 14 = -135, 35 -*/ -const int16_t channelIndex_CICP6[5] = { 0, 1, 2, 5, 6 }; -const int16_t channelIndex_CICP12[7] = { 0, 1, 2, 5, 6, 3, 4 }; -const int16_t channelIndex_CICP14[7] = { 0, 1, 2, 5, 6, 9, 10 }; -const int16_t channelIndex_CICP16[9] = { 0, 1, 2, 5, 6, 9, 10, 11, 12 }; -const int16_t channelIndex_CICP19[11] = { 0, 1, 2, 3, 4, 7, 8, 9, 10, 13, 14 }; - +// VE2AT: move to in ivas_rom_dec ? const float surCohEne[MASA_NUM_DEFINED_SUR_SPR_COH_ENE_BINS] = { 3.0903f, 2.0053f, 1.0860f, 0.8072f, 0.7079f @@ -934,261 +552,4 @@ const float diffuseFieldCoherenceDifferenceZ[BINAURAL_COHERENCE_DIFFERENCE_BINS] 0.048207f, 0.10796f, 0.11845f, 0.047886f, 0.035917f, 0.045196f, 0.018863f, 0.015547f, 0.014157f }; - -/*----------------------------------------------------------------------------------* - * TD ISm binaural renderer ROM tables - *----------------------------------------------------------------------------------*/ - - /* The maximum target times set to 100 msec. */ -const int16_t TDREND_SRC_REND_MaxTargetTimes[IVAS_NUM_SUPPORTED_FS] = -{ - 1600, 3200, 4800 /* Corresponds to 16kHz, 32kHz, 48kHz */ -}; - -/* The maximum lengths of the blocks internally in the effect. Corresponds to 6 msec. This means also that */ -/* if the length of the input block is just above 6 msec, the block will be divided into two 3 msec blocks. */ -const int16_t TDREND_SRC_REND_MaxBlockLengths[IVAS_NUM_SUPPORTED_FS] = -{ - 96, 192, 288 /* Corresponds to 16kHz, 32kHz, 48kHz */ -}; - -const int16_t TDREND_MaxITD[IVAS_NUM_SUPPORTED_FS] = -{ - 111, 222, 333 /* Corresponds to 16kHz, 32kHz, 48kHz */ -}; - -const float TDREND_MaxITD_Incr[IVAS_NUM_SUPPORTED_FS] = -{ - 0.0925f, 0.1850f, 0.2775f /* Corresponds to 16kHz, 32kHz, 48kHz, e.g. ( ( 2 * MaxITD ) / ( 0.05 * 48000 ) ) */ -}; - -const int16_t HRTF_MODEL_N_CPTS_VAR[HRTF_MODEL_N_SECTIONS] = -{ - 13, 12, 11 -}; - -const float SincTable[321] = -{ - 1.00000000f, 0.99957629f, 0.99830587f, 0.99619078f, 0.99323448f, 0.98944177f, 0.98481881f, 0.97937311f, - 0.97311350f, 0.96605012f, 0.95819441f, 0.94955907f, 0.94015803f, 0.93000645f, 0.91912066f, 0.90751815f, - 0.89521750f, 0.88223838f, 0.86860150f, 0.85432856f, 0.83944219f, 0.82396595f, 0.80792425f, 0.79134231f, - 0.77424608f, 0.75666226f, 0.73861817f, 0.72014174f, 0.70126144f, 0.68200624f, 0.66240553f, 0.64248906f, - 0.62228691f, 0.60182943f, 0.58114713f, 0.56027070f, 0.53923087f, 0.51805843f, 0.49678411f, 0.47543856f, - 0.45405225f, 0.43265547f, 0.41127824f, 0.38995024f, 0.36870081f, 0.34755883f, 0.32655271f, 0.30571035f, - 0.28505905f, 0.26462549f, 0.24443569f, 0.22451493f, 0.20488776f, 0.18557791f, 0.16660829f, 0.14800093f, - 0.12977695f, 0.11195656f, 0.09455895f, 0.07760236f, 0.06110400f, 0.04508003f, 0.02954554f, 0.01451456f, - 0.00000000f, -0.01398631f, -0.02743368f, -0.04033255f, -0.05267447f, -0.06445214f, -0.07565940f, -0.08629121f, - -0.09634367f, -0.10581400f, -0.11470052f, -0.12300268f, -0.13072098f, -0.13785702f, -0.14441345f, -0.15039394f, - -0.15580318f, -0.16064685f, -0.16493160f, -0.16866498f, -0.17185547f, -0.17451243f, -0.17664604f, -0.17826729f, - -0.17938796f, -0.18002054f, -0.18017822f, -0.17987486f, -0.17912493f, -0.17794347f, -0.17634608f, -0.17434883f, - -0.17196824f, -0.16922125f, -0.16612516f, -0.16269761f, -0.15895648f, -0.15491992f, -0.15060625f, -0.14603396f, - -0.14122162f, -0.13618787f, -0.13095139f, -0.12553081f, -0.11994473f, -0.11421163f, -0.10834984f, -0.10237755f, - -0.09631271f, -0.09017300f, -0.08397586f, -0.07773838f, -0.07147731f, -0.06520902f, -0.05894946f, -0.05271415f, - -0.04651815f, -0.04037601f, -0.03430179f, -0.02830902f, -0.02241063f, -0.01661904f, -0.01094605f, -0.00540284f, - -0.00000000f, 0.00525251f, 0.01034538f, 0.01526993f, 0.02001814f, 0.02458266f, 0.02895676f, 0.03313441f, - 0.03711021f, 0.04087943f, 0.04443799f, 0.04778246f, 0.05091003f, 0.05381856f, 0.05650650f, 0.05897292f, - 0.06121749f, 0.06324047f, 0.06504268f, 0.06662549f, 0.06799083f, 0.06914112f, 0.07007930f, 0.07080878f, - 0.07133343f, 0.07165755f, 0.07178588f, 0.07172352f, 0.07147595f, 0.07104902f, 0.07044886f, 0.06968193f, - 0.06875494f, 0.06767485f, 0.06644886f, 0.06508435f, 0.06358888f, 0.06197015f, 0.06023599f, 0.05839432f, - 0.05645314f, 0.05442051f, 0.05230450f, 0.05011320f, 0.04785466f, 0.04553692f, 0.04316793f, 0.04075558f, - 0.03830765f, 0.03583181f, 0.03333557f, 0.03082630f, 0.02831121f, 0.02579730f, 0.02329137f, 0.02080003f, - 0.01832963f, 0.01588629f, 0.01347589f, 0.01110403f, 0.00877607f, 0.00649705f, 0.00427175f, 0.00210467f, - 0.00000000f, -0.00203837f, -0.00400686f, -0.00590216f, -0.00772131f, -0.00946162f, -0.01112072f, -0.01269654f, - -0.01418731f, -0.01559156f, -0.01690810f, -0.01813605f, -0.01927478f, -0.02032396f, -0.02128352f, -0.02215366f, - -0.02293482f, -0.02362769f, -0.02423318f, -0.02475245f, -0.02518686f, -0.02553797f, -0.02580754f, -0.02599752f, - -0.02611000f, -0.02614728f, -0.02611175f, -0.02600597f, -0.02583262f, -0.02559449f, -0.02529446f, -0.02493550f, - -0.02452066f, -0.02405306f, -0.02353586f, -0.02297226f, -0.02236549f, -0.02171881f, -0.02103547f, -0.02031874f, - -0.01957185f, -0.01879802f, -0.01800043f, -0.01718225f, -0.01634655f, -0.01549638f, -0.01463471f, -0.01376443f, - -0.01288838f, -0.01200928f, -0.01112977f, -0.01025241f, -0.00937962f, -0.00851376f, -0.00765705f, -0.00681160f, - -0.00597942f, -0.00516238f, -0.00436225f, -0.00358068f, -0.00281917f, -0.00207914f, -0.00136185f, -0.00066846f, - -0.00000000f, 0.00064260f, 0.00125856f, 0.00184718f, 0.00240790f, 0.00294026f, 0.00344390f, 0.00391857f, - 0.00436413f, 0.00478051f, 0.00516776f, 0.00552600f, 0.00585544f, 0.00615637f, 0.00642915f, 0.00667420f, - 0.00689203f, 0.00708318f, 0.00724827f, 0.00738795f, 0.00750293f, 0.00759395f, 0.00766178f, 0.00770723f, - 0.00773114f, 0.00773435f, 0.00771774f, 0.00768218f, 0.00762857f, 0.00755779f, 0.00747075f, 0.00736831f, - 0.00725138f, 0.00712082f, 0.00697748f, 0.00682221f, 0.00665584f, 0.00647916f, 0.00629295f, 0.00609797f, - 0.00589494f, 0.00568458f, 0.00546754f, 0.00524448f, 0.00501600f, 0.00478270f, 0.00454511f, 0.00430377f, - 0.00405916f, 0.00381176f, 0.00356198f, 0.00331023f, 0.00305690f, 0.00280234f, 0.00254687f, 0.00229079f, - 0.00203440f, 0.00177795f, 0.00152168f, 0.00126584f, 0.00101062f, 0.00075625f, 0.00050289f, 0.00025075f, - 0.00000000f -}; - -const float orange53_left_avg_power[257] = /* 257 == IVAS_REVERB_FFT_SIZE_48K/2 + 1 */ -{ - 0.999231100f, 0.992580175f, 0.969233215f, 0.925614893f, 0.871408045f, 0.826101780f, 0.803222895f, 0.800087631f, 0.802672029f, - 0.801490188f, 0.796555817f, 0.790879488f, 0.784882724f, 0.777585745f, 0.769326210f, 0.761789441f, 0.756145239f, 0.752754092f, - 0.751703024f, 0.752594173f, 0.754317880f, 0.755515277f, 0.754378498f, 0.748860359f, 0.738919020f, 0.727488697f, 0.718792558f, - 0.714865267f, 0.713446736f, 0.711076498f, 0.706021905f, 0.697553098f, 0.684623063f, 0.667031527f, 0.647006035f, 0.627680719f, - 0.609939933f, 0.592472672f, 0.574803054f, 0.558499217f, 0.544599831f, 0.532128096f, 0.520152628f, 0.509682238f, 0.501904130f, - 0.496162385f, 0.491121918f, 0.486813396f, 0.483951330f, 0.482198298f, 0.480713189f, 0.479654074f, 0.479590476f, 0.479965866f, - 0.479589254f, 0.478181243f, 0.476334095f, 0.474199444f, 0.471616089f, 0.469089746f, 0.467486322f, 0.466943622f, 0.467153549f, - 0.468381166f, 0.470996737f, 0.474416614f, 0.477639019f, 0.480612457f, 0.483910263f, 0.487287015f, 0.489909321f, 0.491668850f, - 0.493155539f, 0.494319856f, 0.494512051f, 0.493615031f, 0.492155492f, 0.490116775f, 0.486886710f, 0.482303619f, 0.476902038f, - 0.470775038f, 0.463377595f, 0.454571068f, 0.445130944f, 0.435581058f, 0.425568998f, 0.414717495f, 0.403531373f, 0.392556936f, - 0.381436378f, 0.369506508f, 0.357099295f, 0.345049500f, 0.333368897f, 0.321326375f, 0.308959186f, 0.297232091f, 0.286592871f, - 0.276453108f, 0.266589880f, 0.257950366f, 0.251341701f, 0.246435612f, 0.242861211f, 0.241405189f, 0.242839754f, 0.246688128f, - 0.252115428f, 0.259297341f, 0.268399984f, 0.278481483f, 0.288520366f, 0.298599035f, 0.308846802f, 0.318350822f, 0.326248646f, - 0.332813978f, 0.338464528f, 0.342543274f, 0.344278336f, 0.344031811f, 0.342641503f, 0.339995682f, 0.335437506f, 0.329174429f, - 0.322237372f, 0.315035462f, 0.306967229f, 0.297821850f, 0.288482070f, 0.279766560f, 0.271234214f, 0.262228251f, 0.253214896f, - 0.245183259f, 0.237939596f, 0.230546176f, 0.223051578f, 0.216552779f, 0.211263061f, 0.206180066f, 0.200917527f, 0.196485907f, - 0.193453044f, 0.190857053f, 0.187853232f, 0.185171053f, 0.183685005f, 0.182665780f, 0.180928215f, 0.178784713f, 0.177342966f, - 0.176323384f, 0.174430951f, 0.171496049f, 0.168740034f, 0.166518897f, 0.163711995f, 0.159658119f, 0.155442193f, 0.152056932f, - 0.148795277f, 0.144545168f, 0.139905334f, 0.136263832f, 0.133493021f, 0.130194828f, 0.126240104f, 0.123071767f, 0.121281922f, - 0.119557180f, 0.117016964f, 0.114773229f, 0.114072219f, 0.114103459f, 0.113414355f, 0.112460621f, 0.112842396f, 0.114564091f, - 0.115944758f, 0.116569765f, 0.117913686f, 0.120910525f, 0.124211200f, 0.126575813f, 0.128826424f, 0.132578567f, 0.137430578f, - 0.141675219f, 0.144987956f, 0.148879051f, 0.154273912f, 0.159992099f, 0.164641231f, 0.168560207f, 0.173201621f, 0.178906262f, - 0.184429348f, 0.188756809f, 0.192309171f, 0.196154252f, 0.200732291f, 0.205381230f, 0.209404662f, 0.212832779f, 0.216197237f, - 0.220162451f, 0.225029215f, 0.230637416f, 0.236752108f, 0.243243530f, 0.249900997f, 0.256293535f, 0.261716694f, 0.265186161f, - 0.265652657f, 0.262010813f, 0.253508776f, 0.243198514f, 0.244490802f, 0.255167097f, 0.258825988f, 0.257396817f, 0.256197631f, - 0.256865948f, 0.258354962f, 0.259370565f, 0.259730458f, 0.259894609f, 0.260285556f, 0.260970831f, 0.261650831f, 0.262020200f, - 0.262095064f, 0.262225062f, 0.262741268f, 0.263585031f, 0.264350951f, 0.264654577f, 0.264539272f, 0.264409125f, 0.264633715f, - 0.265172601f, 0.265621960f, 0.265678704f, 0.265469313f, 0.265454412f, 0.265907466f, 0.266625792f, 0.267101586f, 0.266997635f, - 0.266522497f, 0.266185820f, 0.266298562f, 0.266692907f, 0.266907692f -}; - -const float orange53_right_avg_power[257] = -{ - 0.999231100f, 0.992580175f, 0.969233215f, 0.925614893f, 0.871408045f, 0.826101780f, 0.803222895f, 0.800087631f, 0.802672029f, - 0.801490188f, 0.796555817f, 0.790879488f, 0.784882724f, 0.777585745f, 0.769326210f, 0.761789441f, 0.756145239f, 0.752754092f, - 0.751703024f, 0.752594173f, 0.754317880f, 0.755515277f, 0.754378498f, 0.748860359f, 0.738919020f, 0.727488697f, 0.718792558f, - 0.714865267f, 0.713446736f, 0.711076498f, 0.706021905f, 0.697553098f, 0.684623063f, 0.667031527f, 0.647006035f, 0.627680719f, - 0.609939933f, 0.592472672f, 0.574803054f, 0.558499217f, 0.544599831f, 0.532128096f, 0.520152628f, 0.509682238f, 0.501904130f, - 0.496162385f, 0.491121918f, 0.486813396f, 0.483951330f, 0.482198298f, 0.480713189f, 0.479654074f, 0.479590476f, 0.479965866f, - 0.479589254f, 0.478181243f, 0.476334095f, 0.474199444f, 0.471616089f, 0.469089746f, 0.467486322f, 0.466943622f, 0.467153549f, - 0.468381166f, 0.470996737f, 0.474416614f, 0.477639019f, 0.480612457f, 0.483910263f, 0.487287015f, 0.489909321f, 0.491668850f, - 0.493155539f, 0.494319856f, 0.494512051f, 0.493615031f, 0.492155492f, 0.490116775f, 0.486886710f, 0.482303619f, 0.476902038f, - 0.470775038f, 0.463377595f, 0.454571068f, 0.445130944f, 0.435581058f, 0.425568998f, 0.414717495f, 0.403531373f, 0.392556936f, - 0.381436378f, 0.369506508f, 0.357099295f, 0.345049500f, 0.333368897f, 0.321326375f, 0.308959186f, 0.297232091f, 0.286592871f, - 0.276453108f, 0.266589880f, 0.257950366f, 0.251341701f, 0.246435612f, 0.242861211f, 0.241405189f, 0.242839754f, 0.246688128f, - 0.252115428f, 0.259297341f, 0.268399984f, 0.278481483f, 0.288520366f, 0.298599035f, 0.308846802f, 0.318350822f, 0.326248646f, - 0.332813978f, 0.338464528f, 0.342543274f, 0.344278336f, 0.344031811f, 0.342641503f, 0.339995682f, 0.335437506f, 0.329174429f, - 0.322237372f, 0.315035462f, 0.306967229f, 0.297821850f, 0.288482070f, 0.279766560f, 0.271234214f, 0.262228251f, 0.253214896f, - 0.245183259f, 0.237939596f, 0.230546176f, 0.223051578f, 0.216552779f, 0.211263061f, 0.206180066f, 0.200917527f, 0.196485907f, - 0.193453044f, 0.190857053f, 0.187853232f, 0.185171053f, 0.183685005f, 0.182665780f, 0.180928215f, 0.178784713f, 0.177342966f, - 0.176323384f, 0.174430951f, 0.171496049f, 0.168740034f, 0.166518897f, 0.163711995f, 0.159658119f, 0.155442193f, 0.152056932f, - 0.148795277f, 0.144545168f, 0.139905334f, 0.136263832f, 0.133493021f, 0.130194828f, 0.126240104f, 0.123071767f, 0.121281922f, - 0.119557180f, 0.117016964f, 0.114773229f, 0.114072219f, 0.114103459f, 0.113414355f, 0.112460621f, 0.112842396f, 0.114564091f, - 0.115944758f, 0.116569765f, 0.117913686f, 0.120910525f, 0.124211200f, 0.126575813f, 0.128826424f, 0.132578567f, 0.137430578f, - 0.141675219f, 0.144987956f, 0.148879051f, 0.154273912f, 0.159992099f, 0.164641231f, 0.168560207f, 0.173201621f, 0.178906262f, - 0.184429348f, 0.188756809f, 0.192309171f, 0.196154252f, 0.200732291f, 0.205381230f, 0.209404662f, 0.212832779f, 0.216197237f, - 0.220162451f, 0.225029215f, 0.230637416f, 0.236752108f, 0.243243530f, 0.249900997f, 0.256293535f, 0.261716694f, 0.265186161f, - 0.265652657f, 0.262010813f, 0.253508776f, 0.243198514f, 0.244490802f, 0.255167097f, 0.258825988f, 0.257396817f, 0.256197631f, - 0.256865948f, 0.258354962f, 0.259370565f, 0.259730458f, 0.259894609f, 0.260285556f, 0.260970831f, 0.261650831f, 0.262020200f, - 0.262095064f, 0.262225062f, 0.262741268f, 0.263585031f, 0.264350951f, 0.264654577f, 0.264539272f, 0.264409125f, 0.264633715f, - 0.265172601f, 0.265621960f, 0.265678704f, 0.265469313f, 0.265454412f, 0.265907466f, 0.266625792f, 0.267101586f, 0.266997635f, - 0.266522497f, 0.266185820f, 0.266298562f, 0.266692907f, 0.266907692f -}; - -const float orange53_coherence[257] = -{ - 0.929530263f, 0.921171963f, 0.900268972f, 0.876067519f, 0.855227590f, 0.837884128f, 0.823401272f, 0.818804145f, 0.835025251f, - 0.871971071f, 0.911253273f, 0.929330528f, 0.921199203f, 0.900894165f, 0.882577479f, 0.867001534f, 0.849280477f, 0.832460761f, - 0.824062645f, 0.823441386f, 0.820908070f, 0.811902404f, 0.802339375f, 0.798648477f, 0.797345281f, 0.791158736f, 0.779512227f, - 0.768243194f, 0.760565042f, 0.754912853f, 0.751044095f, 0.752276063f, 0.759258866f, 0.766927004f, 0.769716740f, 0.767338514f, - 0.763358235f, 0.759508014f, 0.755201221f, 0.750362694f, 0.746060252f, 0.742611766f, 0.739434779f, 0.736354828f, 0.733443379f, - 0.730109870f, 0.726028502f, 0.722365141f, 0.720153689f, 0.718220115f, 0.714793265f, 0.710619092f, 0.708084404f, 0.707218647f, - 0.705624878f, 0.702472746f, 0.700073540f, 0.699947894f, 0.700519860f, 0.699934483f, 0.699344158f, 0.700895131f, 0.704551995f, - 0.708814025f, 0.713567019f, 0.719995975f, 0.728467822f, 0.738399088f, 0.749545693f, 0.761859894f, 0.774593413f, 0.787218869f, - 0.800481200f, 0.814727187f, 0.828367889f, 0.839860320f, 0.850490928f, 0.862034321f, 0.873037636f, 0.880097568f, 0.883217216f, - 0.885473788f, 0.887664974f, 0.886511028f, 0.880120754f, 0.871120989f, 0.862524390f, 0.853262126f, 0.840783834f, 0.825854301f, - 0.811407208f, 0.798167706f, 0.784307659f, 0.769172490f, 0.754072189f, 0.739893615f, 0.726129174f, 0.712544501f, 0.699519753f, - 0.686980069f, 0.674778104f, 0.663931608f, 0.655511260f, 0.648816824f, 0.642671287f, 0.638217211f, 0.637585819f, 0.640332758f, - 0.643755615f, 0.647433281f, 0.653589368f, 0.662824631f, 0.672268033f, 0.680022597f, 0.687623680f, 0.696763635f, 0.705829978f, - 0.712574661f, 0.717432320f, 0.721986175f, 0.725707173f, 0.727064371f, 0.726255059f, 0.724350274f, 0.720927835f, 0.715189219f, - 0.708206475f, 0.701428175f, 0.693923056f, 0.684313059f, 0.674107075f, 0.666009307f, 0.659245491f, 0.650998116f, 0.641600072f, - 0.634524226f, 0.630267978f, 0.625348687f, 0.618164837f, 0.611785769f, 0.608430445f, 0.605561733f, 0.600407422f, 0.594782710f, - 0.591767371f, 0.590365708f, 0.587845862f, 0.584915996f, 0.584355533f, 0.585834682f, 0.586913347f, 0.587935925f, 0.591403484f, - 0.596784472f, 0.601111054f, 0.604539037f, 0.610374093f, 0.618451059f, 0.624519289f, 0.627448440f, 0.631859899f, 0.639748096f, - 0.646256745f, 0.647378445f, 0.647664309f, 0.652599990f, 0.659044445f, 0.659743190f, 0.656243205f, 0.656651020f, 0.662200928f, - 0.664544880f, 0.660030127f, 0.656303048f, 0.659881413f, 0.664978266f, 0.662953973f, 0.657274961f, 0.658065319f, 0.665406108f, - 0.668446958f, 0.663809955f, 0.661349833f, 0.668595374f, 0.677367866f, 0.677208483f, 0.672289610f, 0.675831020f, 0.688208520f, - 0.695776582f, 0.691749871f, 0.687812865f, 0.696674168f, 0.711764693f, 0.716045380f, 0.706839681f, 0.701565385f, 0.711955190f, - 0.726487696f, 0.723370016f, 0.700417101f, 0.677427649f, 0.670733511f, 0.671355724f, 0.654210806f, 0.608316183f, 0.549225986f, - 0.504217446f, 0.484227657f, 0.475346446f, 0.452598959f, 0.399407327f, 0.319485664f, 0.229244962f, 0.146649837f, 0.083417825f, - 0.041744832f, 0.018142883f, 0.006854009f, 0.002511850f, 0.001177550f, 0.000840970f, 0.000701097f, 0.000571384f, 0.000458581f, - 0.000376965f, 0.000320562f, 0.000278847f, 0.000245546f, 0.000218281f, 0.000195632f, 0.000176647f, 0.000160827f, 0.000147978f, - 0.000137649f, 0.000129066f, 0.000121431f, 0.000114406f, 0.000108067f, 0.000102595f, 0.000097917f, 0.000093750f, 0.000089854f, - 0.000086255f, 0.000083183f, 0.000080804f, 0.000079026f, 0.000077552f, 0.000076117f, 0.000074693f, 0.000073431f, 0.000072456f, - 0.000071701f, 0.000071002f, 0.000070286f, 0.000069692f, 0.000069457f -}; - -/*----------------------------------------------------------------------------------* - * t-design and SN3D normalization table - *----------------------------------------------------------------------------------*/ - - /* SN3D norm */ -const float norm_sn3d_hoa3[16] = -{ - 1.f, 1.7320508f, 1.7320508f, 1.7320508f, 2.2360680f, 2.2360680f, 2.2360680f, 2.2360680f, - 2.2360680f, 2.6457512f, 2.6457512f, 2.6457512f, 2.6457512f, 2.6457512f, 2.6457512f, 2.6457512f -}; - -/* Order 11 t-design */ -const float t_design_11_azimuth[SBA_T_DESIGN_11_SIZE] = -{ - 1.329273e+02f, -8.393495e+01f, 8.474100e+00f, -1.133408e+02f, -1.032659e+02f, -3.323704e+01f, 2.185643e+01f, -1.565395e+02f, - -6.426475e+01f, 1.657795e+02f, -2.520283e+01f, -9.700380e+01f, 2.785464e+01f, 1.532142e+02f, -1.550616e+02f, -1.184214e+01f, - 8.053873e+01f, -4.205616e+01f, -3.122333e+01f, 3.883790e+01f, 9.376069e+01f, -8.475602e+01f, 7.755368e+00f, -1.222769e+02f, - 4.680127e+01f, -2.476863e+01f, 9.989047e+01f, -1.347840e+02f, -8.308802e+01f, 6.012817e+01f, 1.526447e+02f, 2.975767e+01f, - 4.077932e+01f, 1.101839e+02f, 1.656521e+02f, -1.299266e+01f, 7.973599e+01f, -5.052453e+01f, 1.189239e+02f, 4.722029e+01f, - 1.719253e+02f, -6.251458e+01f, -1.111567e+01f, 1.320180e+02f, -1.353555e+02f, 1.023709e+02f, 1.127393e+02f, -1.783050e+02f, - -1.223199e+02f, 5.907635e+01f, 1.517042e+02f, 2.137634e+01f, -1.690055e+02f, 1.189808e+02f, -1.160893e+02f, 9.647679e+00f, - 6.089332e+01f, -1.560215e+02f, -6.346030e+01f, 1.749298e+02f, -1.752888e+02f, -1.059519e+02f, -5.019283e+01f, 1.313583e+02f, - -1.362968e+02f, 9.356446e+01f, -9.708401e+01f, -1.691583e+02f, -4.413238e+01f, 8.147954e+01f -}; - -const float t_design_11_elevation[SBA_T_DESIGN_11_SIZE] = -{ - 7.692547e+00f, -2.373007e+01f, 2.351276e+01f, 7.042259e+01f, -9.896944e+00f, -7.075133e+01f, -2.646185e+01f, 4.777649e+01f, - -7.720470e+00f, 4.453436e+01f, 2.638979e+01f, -4.465789e+01f, 9.767035e+00f, -4.770533e+01f, 7.453029e+00f, -2.359012e+01f, - 2.371945e+01f, 7.043827e+01f, -9.835416e+00f, -7.049808e+01f, -2.629492e+01f, 4.761480e+01f, -7.517185e+00f, 4.428623e+01f, - 2.664426e+01f, -4.456937e+01f, 9.912719e+00f, -4.795996e+01f, 7.296799e+00f, -2.334460e+01f, 2.364153e+01f, 7.068431e+01f, - -9.581404e+00f, -7.039345e+01f, -2.642582e+01f, 4.775107e+01f, -7.308536e+00f, 4.426328e+01f, 2.671406e+01f, -4.431497e+01f, - 9.758997e+00f, -4.803619e+01f, 7.439651e+00f, -2.333261e+01f, 2.338690e+01f, 7.082191e+01f, -9.485964e+00f, -7.058019e+01f, - -2.667403e+01f, 4.799784e+01f, -7.382762e+00f, 4.449706e+01f, 2.650250e+01f, -4.424619e+01f, 9.518451e+00f, -4.782814e+01f, - 7.684274e+00f, -2.357068e+01f, 2.330745e+01f, 7.065865e+01f, -9.680889e+00f, -7.080268e+01f, -2.669635e+01f, 4.801363e+01f, - -7.637348e+00f, 4.466512e+01f, 2.630235e+01f, -4.445764e+01f, 9.523415e+00f, -4.762422e+01f -}; - - -/*----------------------------------------------------------------------* -* Reverberator ROM tables -*-----------------------------------------------------------------------*/ - -const float ivas_reverb_default_fc[IVAS_REVERB_DEFAULT_N_BANDS] = -{ - 20.0f, 25.0f, 31.5f, 40.0f, - 50.0f, 63.0f, 80.0f, 100.0f, - 125.0f, 160.0f, 200.0f, 250.0f, - 315.0f, 400.0f, 500.0f, 630.0f, - 800.0f, 1000.0f, 1250.0f, 1600.0f, - 2000.0f, 2500.0f, 3150.0f, 4000.0f, - 5000.0f, 6300.0f, 8000.0f, 10000.0f, - 12500.0f, 16000.0f, 20000.0f -}; - -const float ivas_reverb_default_RT60[IVAS_REVERB_DEFAULT_N_BANDS] = -{ - 1.3622f, 1.4486f, 1.3168f, 1.5787f, - 1.4766f, 1.3954f, 1.2889f, 1.3462f, - 1.0759f, 1.0401f, 1.097f, 1.085f, - 1.091f, 1.0404f, 1.0499f, 1.0699f, - 1.1028f, 1.1714f, 1.1027f, 1.0666f, - 1.055f, 1.0553f, 1.0521f, 1.0569f, - 1.0421f, 0.97822f, 0.80487f, 0.75944f, - 0.71945f, 0.61682f, 0.60031f -}; - -const float ivas_reverb_default_DSR[IVAS_REVERB_DEFAULT_N_BANDS] = -{ - 1.8811e-08f, 2.1428e-08f, 1.3972e-08f, 1.51e-08f, - 1.287e-08f, 1.8747e-08f, 2.413e-08f, 3.9927e-08f, - 8.9719e-08f, 1.902e-07f, 3.702e-07f, 6.1341e-07f, - 7.1432e-07f, 6.5331e-07f, 4.6094e-07f, 5.4683e-07f, - 7.0134e-07f, 6.856e-07f, 7.114e-07f, 6.9604e-07f, - 5.2939e-07f, 5.699e-07f, 6.1773e-07f, 5.7488e-07f, - 4.7748e-07f, 2.7213e-07f, 1.3681e-07f, 1.0941e-07f, - 6.2001e-08f, 2.8483e-08f, 2.6267e-08f -}; - - /* clang-format on */ diff --git a/lib_dec/ivas_rom_dec.h b/lib_dec/ivas_rom_dec.h index d85f433935db4fc9c1aaa8d45175c00aaf9f93a9..febc60e2e57fe1c8a0c4a1f98d29e9229933ccdb 100644 --- a/lib_dec/ivas_rom_dec.h +++ b/lib_dec/ivas_rom_dec.h @@ -118,7 +118,6 @@ extern const int16_t sba_map_tc[8]; extern const float ls_conversion_cicpX_mono[12][1]; extern const float ls_conversion_cicpX_stereo[12][2]; extern const LS_CONVERSION_MATRIX ls_conversion_cicp12_cicp6[]; -extern const LS_CONVERSION_MATRIX ls_conversion_cicp12_cicp6[]; extern const LS_CONVERSION_MATRIX ls_conversion_cicp14_cicp6[]; extern const LS_CONVERSION_MATRIX ls_conversion_cicp14_cicp12[]; extern const LS_CONVERSION_MATRIX ls_conversion_cicp16_cicp6[]; @@ -143,13 +142,6 @@ extern const LS_CONVERSION_MAPPING ls_conversion_mapping[]; * FASTCONV and PARAMETRIC binaural renderer ROM tables *----------------------------------------------------------------------------------*/ -extern const float dmxmtx[BINAURAL_CHANNELS][11]; -extern const int16_t channelIndex_CICP6[5]; -extern const int16_t channelIndex_CICP12[7]; -extern const int16_t channelIndex_CICP14[9]; -extern const int16_t channelIndex_CICP16[9]; -extern const int16_t channelIndex_CICP19[11]; - /* These are equalization values for spread and surround coherent sounds, approximating the spectrum * for such sounds at anechoic multichannel listening. */ extern const float surCohEne[MASA_NUM_DEFINED_SUR_SPR_COH_ENE_BINS]; @@ -164,44 +156,4 @@ extern const float diffuseFieldCoherenceDifferenceX[BINAURAL_COHERENCE_DIFFERENC extern const float diffuseFieldCoherenceDifferenceY[BINAURAL_COHERENCE_DIFFERENCE_BINS]; extern const float diffuseFieldCoherenceDifferenceZ[BINAURAL_COHERENCE_DIFFERENCE_BINS]; -/*----------------------------------------------------------------------------------* - * TD ISM Object renderer - *----------------------------------------------------------------------------------*/ - -extern const int16_t TDREND_SRC_REND_MaxTargetTimes[IVAS_NUM_SUPPORTED_FS]; -extern const int16_t TDREND_SRC_REND_MaxBlockLengths[IVAS_NUM_SUPPORTED_FS]; -extern const int16_t TDREND_MaxITD[IVAS_NUM_SUPPORTED_FS]; -extern const float TDREND_MaxITD_Incr[IVAS_NUM_SUPPORTED_FS]; - -extern const int16_t HRTF_MODEL_N_CPTS_VAR[HRTF_MODEL_N_SECTIONS]; - -extern const float SincTable[321]; - -extern const float orange53_left_avg_power[257]; -extern const float orange53_right_avg_power[257]; -extern const float orange53_coherence[257]; - - -/*----------------------------------------------------------------------------------* - * t-design and SN3D normalization table - *----------------------------------------------------------------------------------*/ - -/* SN3D norm */ -extern const float norm_sn3d_hoa3[16]; - -/* Order 11 t-design */ -extern const uint16_t t_design_11_size; -extern const float t_design_11_azimuth[SBA_T_DESIGN_11_SIZE]; -extern const float t_design_11_elevation[SBA_T_DESIGN_11_SIZE]; - - -/*----------------------------------------------------------------------* - * Reverberator ROM tables - *-----------------------------------------------------------------------*/ - -extern const float ivas_reverb_default_fc[]; -extern const float ivas_reverb_default_RT60[]; -extern const float ivas_reverb_default_DSR[]; - - #endif diff --git a/lib_dec/ivas_sba_dec.c b/lib_dec/ivas_sba_dec.c index 2681493f08ceb5cb88a03ac578d89095a4657406..fe043c746bb767909a9e5e6e62a295fb0935076c 100644 --- a/lib_dec/ivas_sba_dec.c +++ b/lib_dec/ivas_sba_dec.c @@ -38,301 +38,13 @@ #include "prot.h" #include "ivas_prot.h" #include "ivas_rom_dec.h" +#include #ifdef DEBUGGING #include "debug.h" #endif #include "wmops.h" -/*-----------------------------------------------------------------------* - * Local function prototypes - *-----------------------------------------------------------------------*/ - -#ifdef DEBUG_MODE_DIRAC -static void debug_mode_dirac( float output[MAX_OUTPUT_CHANNELS][L_FRAME48k], const int16_t nchan_transport, const int16_t output_frame ); -#endif - - -/*-------------------------------------------------------------------------* - * ivas_mc2sba() - * - * MC signals transformed into SBA in TD domain - *-------------------------------------------------------------------------*/ - -void ivas_mc2sba( - IVAS_OUTPUT_SETUP hIntSetup, /* i : Format of decoder output */ - float buffer_td[][L_FRAME48k], /* i/o: MC signals (on input) and the HOA3 (on output) */ - const int16_t output_frame, /* i : output frame length per channel */ - const int16_t sba_order, /* i : Ambisonic (SBA) order */ - const float gain_lfe /* i : gain for LFE, 0 = ignore LFE */ -) -{ - int16_t i, j, k; - int16_t idx_lfe, idx_in; - float buffer_tmp[16][L_FRAME48k]; - float gains[16]; - int16_t azimuth, elevation; - int16_t sba_num_chans; - - assert( ( sba_order <= 3 ) && "Only order up to 3 is supported!" ); - - /* Init*/ - sba_num_chans = ( sba_order + 1 ) * ( sba_order + 1 ); - for ( j = 0; j < sba_num_chans; j++ ) - { - set_zero( buffer_tmp[j], output_frame ); - } - - /* HOA encoding*/ - idx_lfe = 0; - idx_in = 0; - for ( i = 0; i < hIntSetup.nchan_out_woLFE + hIntSetup.num_lfe; i++ ) - { - if ( ( hIntSetup.num_lfe > 0 ) && ( i == hIntSetup.index_lfe[idx_lfe] ) ) - { - if ( gain_lfe > 0.f ) - { - /* Add LFE to omni W with gain*/ - for ( k = 0; k < output_frame; k++ ) - { - buffer_tmp[0][k] += gain_lfe * buffer_td[i][k]; - } - } - - if ( idx_lfe < ( hIntSetup.num_lfe - 1 ) ) - { - idx_lfe++; - } - } - else - { - azimuth = (int16_t) ( hIntSetup.ls_azimuth[idx_in] ); - elevation = (int16_t) ( hIntSetup.ls_elevation[idx_in] ); - idx_in++; - - /* get HOA response for direction (ACN/SN3D)*/ - ivas_dirac_dec_get_response( - azimuth, - elevation, - gains, - sba_order ); - - for ( j = 0; j < sba_num_chans; j++ ) - { - for ( k = 0; k < output_frame; k++ ) - { - buffer_tmp[j][k] += gains[j] * buffer_td[i][k]; - } - } - } - } - - for ( j = 0; j < sba_num_chans; j++ ) - { - mvr2r( buffer_tmp[j], buffer_td[j], output_frame ); - } - - return; -} - - -/*-------------------------------------------------------------------------* - * ivas_sba2MC_cldfb() - * - * SBA signals transformed into MC in CLDFB domain - *-------------------------------------------------------------------------*/ - -void ivas_sba2mc_cldfb( - IVAS_OUTPUT_SETUP hInSetup, /* i : Format of input layout */ - float RealBuffer[][MAX_PARAM_SPATIAL_SUBFRAMES][CLDFB_NO_CHANNELS_MAX], /* i/o: cldfb real part */ - float ImagBuffer[][MAX_PARAM_SPATIAL_SUBFRAMES][CLDFB_NO_CHANNELS_MAX], /* i/o: cldfb imag part */ - const int16_t nb_channels_out, /* i : nb of output channels */ - const int16_t nb_bands, /* i : nb of CLDFB bands to process */ - const float *hoa_dec_mtx /* i : HOA decoding mtx */ -) -{ - int16_t iBlock, iBand, n, m; - float realOut[16][MAX_PARAM_SPATIAL_SUBFRAMES * CLDFB_NO_CHANNELS_MAX], imagOut[16][MAX_PARAM_SPATIAL_SUBFRAMES * CLDFB_NO_CHANNELS_MAX]; - float g; - float *p_real, *p_imag, *p_realOut, *p_imagOut; - int16_t nb_channels_in; - - wmops_sub_start( "ivas_sba2mc_cldfb" ); - - nb_channels_in = hInSetup.nchan_out_woLFE; - assert( ( nb_channels_in == 16 ) && ( nb_channels_out == 11 ) && "ivas_sba2mc_cldfb; only HOA3 to CICP19 is for now supported!" ); - - for ( n = 0; n < nb_channels_out; n++ ) - { - set_zero( realOut[n], MAX_PARAM_SPATIAL_SUBFRAMES * nb_bands ); - set_zero( imagOut[n], MAX_PARAM_SPATIAL_SUBFRAMES * nb_bands ); - - for ( m = 0; m < nb_channels_in; m++ ) - { - g = hoa_dec_mtx[SBA_NHARM_HOA3 * n + m]; - p_realOut = realOut[n]; - p_imagOut = imagOut[n]; - for ( iBlock = 0; iBlock < MAX_PARAM_SPATIAL_SUBFRAMES; iBlock++ ) - { - p_real = RealBuffer[m][iBlock]; - p_imag = ImagBuffer[m][iBlock]; - for ( iBand = 0; iBand < nb_bands; iBand++ ) - { - *p_realOut = *p_realOut + g * *( p_real++ ); - *p_imagOut = *p_imagOut + g * *( p_imag++ ); - p_realOut++; - p_imagOut++; - } - } - } - } - - for ( n = 0; n < nb_channels_out; n++ ) - { - p_realOut = realOut[n]; - p_imagOut = imagOut[n]; - for ( iBlock = 0; iBlock < MAX_PARAM_SPATIAL_SUBFRAMES; iBlock++ ) - { - p_real = RealBuffer[n][iBlock]; - p_imag = ImagBuffer[n][iBlock]; - for ( iBand = 0; iBand < nb_bands; iBand++ ) - { - *( p_real++ ) = *p_realOut++; - *( p_imag++ ) = *p_imagOut++; - } - } - } - - wmops_sub_end(); - - return; -} - -/*-------------------------------------------------------------------* - * ivas_sba_remapTCs() - * - * Get TCs from Ambisonics signal in ACN - *-------------------------------------------------------------------*/ - -int16_t ivas_sba_remapTCs( - float sba_data[][L_FRAME48k], /* i/o: SBA signals */ - Decoder_Struct *st_ivas, /* i/o: decoder struct */ - const int16_t output_frame /* i : frame length */ -) -{ - int16_t nchan_remapped; - -#ifdef DEBUG_MODE_DIRAC - debug_mode_dirac( sba_data, st_ivas->nchan_transport, output_frame ); -#endif - - nchan_remapped = st_ivas->nchan_transport; - if ( ( st_ivas->sba_mode != SBA_MODE_SPAR && st_ivas->sba_planar && nchan_remapped >= 3 ) || - ( ( st_ivas->sba_mode == SBA_MODE_SPAR ) && nchan_remapped == 3 ) ) - { - - nchan_remapped++; - if ( st_ivas->sba_mode != SBA_MODE_SPAR ) - { - assert( ( ( st_ivas->nchan_transport == 3 ) || ( st_ivas->nchan_transport == 5 ) || ( st_ivas->nchan_transport == 7 ) ) && "Number of channels must be odd for SBA planar!" ); - } - - if ( nchan_remapped == 4 ) - { - /*For planar A-format channel 2 and 3 are identical -> Z=0*/ - mvr2r( sba_data[2], sba_data[3], output_frame ); - } - } - - if ( st_ivas->nchan_transport >= 3 ) - { - int16_t i = 0; - float temp; - - /*convert WYXZ downmix to WYZX*/ - for ( i = 0; i < output_frame; i++ ) - { - temp = sba_data[2][i]; - sba_data[2][i] = sba_data[3][i]; - sba_data[3][i] = temp; - if ( st_ivas->nchan_transport == 3 ) - { - sba_data[2][i] = 0; - } - } - } - - if ( st_ivas->sba_mode != SBA_MODE_SPAR ) - { - ivas_sba_zero_vert_comp( sba_data, st_ivas->sba_analysis_order, st_ivas->sba_planar, output_frame ); - } - - return ( nchan_remapped ); -} - - -/*-------------------------------------------------------------------------* - * ivas_ism2sba() - * - * ISM transformed into SBA in TD domain. - *-------------------------------------------------------------------------*/ - -void ivas_ism2sba( - float buffer_td[][L_FRAME48k], /* i/o: TD signal buffers */ - ISM_RENDERER_HANDLE hIsmRendererData, /* i/o: renderer data */ - const ISM_METADATA_HANDLE hIsmMetaData[], /* i : object metadata */ - const int16_t num_objects, /* i : number of objects */ - const int16_t output_frame, /* i : output frame length per channel */ - const int16_t sba_order /* i : Ambisonic (SBA) order */ -) -{ - int16_t i, j, k; - float buffer_tmp[16][L_FRAME48k]; - float gains[16]; - float g1, g2; - int16_t azimuth, elevation; - int16_t sba_num_chans; - - assert( ( sba_order <= 3 ) && "Only order up to 3 is supported!" ); - assert( hIsmRendererData != NULL && "hIsmRendererData not allocated!" ); - - /* Init*/ - sba_num_chans = ( sba_order + 1 ) * ( sba_order + 1 ); - for ( j = 0; j < sba_num_chans; j++ ) - { - set_zero( buffer_tmp[j], output_frame ); - } - - for ( i = 0; i < num_objects; i++ ) - { - azimuth = (int16_t) ( hIsmMetaData[i]->azimuth + 0.5f ); - elevation = (int16_t) ( hIsmMetaData[i]->elevation + 0.5f ); - - /*get HOA gets for direction (ACN/SN3D)*/ - ivas_dirac_dec_get_response( azimuth, elevation, gains, sba_order ); - - for ( j = 0; j < sba_num_chans; j++ ) - { - g2 = 0.f; - for ( k = 0; k < output_frame; k++ ) - { - g2 += 1.f / output_frame; - g1 = 1.0f - g2; - buffer_tmp[j][k] += ( g2 * gains[j] + g1 * hIsmRendererData->prev_gains[i][j] ) * buffer_td[i][k]; - } - hIsmRendererData->prev_gains[i][j] = gains[j]; - } - } - - for ( j = 0; j < sba_num_chans; j++ ) - { - mvr2r( buffer_tmp[j], buffer_td[j], output_frame ); - } - - return; -} - - #ifdef SBA_BR_SWITCHING /*-------------------------------------------------------------------* * ivas_sba_dec_reinit() diff --git a/lib_dec/ivas_stat_dec.h b/lib_dec/ivas_stat_dec.h index edbb0bed624be78b93722945f4c81283948c3729..50369fab6df6656564be67c6b3eac0e0f6087133 100644 --- a/lib_dec/ivas_stat_dec.h +++ b/lib_dec/ivas_stat_dec.h @@ -39,6 +39,10 @@ #include "stat_dec.h" #include "ivas_cnst.h" #include "ivas_stat_com.h" +#include "ivas_stat_rend.h" +#ifdef EXT_RENDERER +#include "common_api_types.h" // VE2AT: don't we want to avoid this include in the library? I admit that the rules hefre are not 100% clear to me but introducing it just for IVAS_QUATERNION is not necessry I think +#endif /*----------------------------------------------------------------------------------* @@ -980,7 +984,7 @@ typedef struct mct_dec_data_structure /*----------------------------------------------------------------------------------* * EFAP structures *----------------------------------------------------------------------------------*/ - +// VE2AT: move to ivas_rom_rend.h ? typedef struct EFAP_VERTEX { float azi; /* azimuth of the loudspeaker */ @@ -1043,7 +1047,7 @@ typedef struct EFAP /*----------------------------------------------------------------------------------* * VBAP structures *----------------------------------------------------------------------------------*/ - +// VE2AT: move to ivas_rom_rend.h ? enum SpeakerNodeGroup { SPEAKER_NODE_BOTTOM_HALF, @@ -1101,7 +1105,7 @@ typedef struct vbap_data_structure /*----------------------------------------------------------------------------------* * renderer structures *----------------------------------------------------------------------------------*/ - +// VE2AT: move to ivas_rom_rend.h ? typedef struct renderer_struct { float prev_gains[MAX_CICP_CHANNELS - 1][MAX_OUTPUT_CHANNELS]; @@ -1110,62 +1114,6 @@ typedef struct renderer_struct } ISM_RENDERER_DATA, *ISM_RENDERER_HANDLE; -/*----------------------------------------------------------------------------------* - * Loudspeaker Configuration Conversion structure - *----------------------------------------------------------------------------------*/ - -typedef struct ivas_LS_setupconversion_struct -{ - float *dmxMtx[MAX_OUTPUT_CHANNELS]; - float *targetEnergyPrev[MAX_OUTPUT_CHANNELS]; - float *dmxEnergyPrev[MAX_OUTPUT_CHANNELS]; - int16_t sfbOffset[MAX_SFB + 2]; - int16_t sfbCnt; - -} LSSETUP_CONVERSION_STRUCT, *LSSETUP_CONVERSION_HANDLE; - - -typedef struct ivas_LS_setupconversion_matrix -{ - int16_t index; - float value; - -} LS_CONVERSION_MATRIX; - -typedef struct ivas_LS_setupconversion_mapping -{ - AUDIO_CONFIG input_config; - AUDIO_CONFIG output_config; - const LS_CONVERSION_MATRIX *conversion_matrix; - -} LS_CONVERSION_MAPPING; - -typedef struct ivas_mono_downmix_renderer_struct -{ - float inputEnergy[CLDFB_NO_CHANNELS_MAX]; - float protoEnergy[CLDFB_NO_CHANNELS_MAX]; - -} MONO_DOWNMIX_RENDERER_STRUCT, *MONO_DOWNMIX_RENDERER_HANDLE; - - -/*----------------------------------------------------------------------------------* - * Custom Loudspeaker configuration structure - *----------------------------------------------------------------------------------*/ - -typedef struct ivas_LS_setup_custom -{ - int16_t is_planar_setup; /* flag to indicate if setup is planar or not */ - int16_t num_spk; /* number of custom loudspeakers */ - float ls_azimuth[MAX_OUTPUT_CHANNELS]; /* custom loudspeaker azimuths */ - float ls_elevation[MAX_OUTPUT_CHANNELS]; /* custom loudspeaker elevations */ - int16_t num_lfe; /* number of LFE channels */ - int16_t lfe_idx[MAX_OUTPUT_CHANNELS]; /* index for LFE channel insertion */ - int16_t separate_ch_found; /* flag to indicate if a center channel was found */ - float separate_ch_gains[MAX_OUTPUT_CHANNELS]; /* gains to pan McMASA separateChannel in case no center channel is present */ - -} LSSETUP_CUSTOM_STRUCT, *LSSETUP_CUSTOM_HANDLE; - - /*----------------------------------------------------------------------------------* * MASA decoder structures *----------------------------------------------------------------------------------*/ @@ -1224,7 +1172,7 @@ typedef struct ivas_masa_decoder_struct /*----------------------------------------------------------------------------------* * Binaural Rendering structure *----------------------------------------------------------------------------------*/ - +// VE2AT: move to ivas_rom_rend.h ? /* Binaural reverberator structure */ typedef struct ivas_binaural_reverb_struct { @@ -1343,18 +1291,24 @@ typedef struct ivas_binaural_rendering_struct /*----------------------------------------------------------------------------------* * Head tracking data structure *----------------------------------------------------------------------------------*/ - +// VE2AT: move to ivas_rom_rend.h ? +#ifndef EXT_RENDERER /* Quaternion type for head orientation */ typedef struct Quaternion_struct { float w, x, y, z; } Quaternion; +#endif typedef struct ivas_binaural_head_track_struct { int16_t num_quaternions; +#ifdef EXT_RENDERER + IVAS_QUATERNION Quaternions[MAX_PARAM_SPATIAL_SUBFRAMES]; +#else Quaternion Quaternions[MAX_PARAM_SPATIAL_SUBFRAMES]; +#endif float Rmat[3][3]; float Rmat_prev[3][3]; @@ -1370,7 +1324,7 @@ typedef struct ivas_binaural_head_track_struct /*----------------------------------------------------------------------------------* * TD ISm Object Renderer structure *----------------------------------------------------------------------------------*/ - +// VE2AT: move to ivas_rom_rend.h ? typedef struct { SFX_OpMode_t OpMode; /* Operating mode. This effect can only be TRANSIENT or OFF. */ @@ -1678,7 +1632,7 @@ typedef struct ivas_binaural_td_rendering_struct /*------------------------------------------------------------------------------------------* * Crend structures *------------------------------------------------------------------------------------------*/ - +// VE2AT: move to ivas_rom_rend.h ? typedef struct ivas_hrtfs_structure { float *pOut_to_bin_re[MAX_INTERN_CHANNELS][BINAURAL_CHANNELS]; diff --git a/lib_dec/ivas_vbap.c b/lib_dec/ivas_vbap.c index 2534854e8ba6894d621819411363a4312b00d9d7..f0960bd1271a1472c20cb34721270416a359b7d7 100644 --- a/lib_dec/ivas_vbap.c +++ b/lib_dec/ivas_vbap.c @@ -29,7 +29,7 @@ the United Nations Convention on Contracts on the International Sales of Goods. *******************************************************************************************************/ - +// VE2AT: move to lib_rend ? #include #include "options.h" #include diff --git a/lib_enc/lib_enc.c b/lib_enc/lib_enc.c old mode 100644 new mode 100755 index e7957a47e5d26e43e9d8fccacf943c75e5f874eb..04e9ed4233af7927e5202ff7d921f81812263f29 --- a/lib_enc/lib_enc.c +++ b/lib_enc/lib_enc.c @@ -378,7 +378,11 @@ ivas_error IVAS_ENC_ConfigureForObjects( if ( numObjects > MAX_NUM_OBJECTS ) { +#ifdef EXT_RENDERER + return IVAS_ERR_TOO_MANY_INPUTS; +#else return IVAS_ERR_TOO_MANY_OBJECT_INPUTS; +#endif } st_ivas = hIvasEnc->st_ivas; @@ -1361,7 +1365,11 @@ const char *IVAS_ENC_GetErrorMessage( return "invalid bitrate"; case IVAS_ERR_INVALID_MASA_CONFIG: return "invalid MASA config"; +#ifdef EXT_RENDERER + case IVAS_ERR_TOO_MANY_INPUTS: +#else case IVAS_ERR_TOO_MANY_OBJECT_INPUTS: +#endif return "too many object inputs provided"; case IVAS_ERR_INDEX_OUT_OF_BOUNDS: return "index out of bounds"; diff --git a/lib_dec/ivas_allrad_dec.c b/lib_rend/ivas_allrad_dec.c similarity index 99% rename from lib_dec/ivas_allrad_dec.c rename to lib_rend/ivas_allrad_dec.c index ab1402a452d0583910381e4f1da3bb636fd848b3..a61f543cb197e72aea08175db9464106e45de617 100644 --- a/lib_dec/ivas_allrad_dec.c +++ b/lib_rend/ivas_allrad_dec.c @@ -39,7 +39,7 @@ #include "prot.h" #include "ivas_prot.h" #include "ivas_stat_dec.h" -#include "ivas_rom_dec.h" +#include "ivas_rom_rend.h" #ifdef DEBUGGING #include "debug.h" #endif diff --git a/lib_dec/ivas_binauralRenderer.c b/lib_rend/ivas_binauralRenderer.c similarity index 99% rename from lib_dec/ivas_binauralRenderer.c rename to lib_rend/ivas_binauralRenderer.c index 45108a92faf37767b05a8bf481c28771e33ba406..53f8dbcfa308c0048a1a43da134bdf0c12f61f7a 100644 --- a/lib_dec/ivas_binauralRenderer.c +++ b/lib_rend/ivas_binauralRenderer.c @@ -37,7 +37,7 @@ #include "prot.h" #include "cnst.h" #include "ivas_cnst.h" -#include "ivas_rom_dec.h" +#include "ivas_rom_rend.h" #include "ivas_rom_com.h" #include "ivas_rom_binauralRenderer.h" #ifdef DEBUGGING diff --git a/lib_dec/ivas_binaural_reverb.c b/lib_rend/ivas_binaural_reverb.c similarity index 100% rename from lib_dec/ivas_binaural_reverb.c rename to lib_rend/ivas_binaural_reverb.c diff --git a/lib_dec/ivas_crend.c b/lib_rend/ivas_crend.c similarity index 58% rename from lib_dec/ivas_crend.c rename to lib_rend/ivas_crend.c index 796c4a9ed478c0b1da457db5de687fa733a0cc54..3782f5789e941b380ec0865fefa84875868003f2 100644 --- a/lib_dec/ivas_crend.c +++ b/lib_rend/ivas_crend.c @@ -35,10 +35,14 @@ #include "prot.h" #include "ivas_prot.h" #include "ivas_cnst.h" -#include "ivas_rom_dec.h" +#include "ivas_rom_rend.h" #include "ivas_stat_dec.h" #include #include "ivas_rom_binaural_crend_head.h" +#ifdef EXT_RENDERER +#include "lib_rend.h" +#include "ivas_lib_rend_internal.h" +#endif #ifdef DEBUGGING #include "debug.h" #endif @@ -1033,7 +1037,11 @@ ivas_error ivas_crend_process( { int16_t i, nchan_out, output_frame; int16_t subframe_len, subframe_idx; +#ifdef EXT_RENDERER + float pcm_tmp[BINAURAL_CHANNELS][L_FRAME48k]; +#else float pcm_tmp[MAX_TRANSPORT_CHANNELS][L_FRAME48k]; +#endif AUDIO_CONFIG intern_config; ivas_error error; @@ -1073,12 +1081,20 @@ ivas_error ivas_crend_process( */ if ( intern_config == AUDIO_CONFIG_FOA || intern_config == AUDIO_CONFIG_HOA2 || intern_config == AUDIO_CONFIG_HOA3 ) { +#ifdef EXT_RENDERER + rotateFrame_shd( st_ivas->hHeadTrackData, output, subframe_len, st_ivas->hIntSetup, subframe_idx ); +#else rotateFrame_shd( st_ivas->hHeadTrackData, output, st_ivas->hDecoderConfig->output_Fs, subframe_len, st_ivas->hIntSetup, subframe_idx ); +#endif } /* Rotation in SD for MC -> BINAURAL_ROOM */ else if ( st_ivas->ivas_format != ISM_FORMAT && st_ivas->hIntSetup.is_loudspeaker_setup ) { +#ifdef EXT_RENDERER + rotateFrame_sd( st_ivas->hHeadTrackData, output, subframe_len, st_ivas->hIntSetup, st_ivas->hEFAPdata, subframe_idx ); +#else rotateFrame_sd( st_ivas->hHeadTrackData, output, st_ivas->hDecoderConfig->output_Fs, subframe_len, st_ivas->hIntSetup, st_ivas->hEFAPdata, subframe_idx ); +#endif } } @@ -1119,3 +1135,845 @@ ivas_error ivas_crend_process( return IVAS_ERR_OK; } + +#ifdef EXT_RENDERER + +/*------------------------------------------------------------------------- + * ivas_rend_openCrend() + * + * Allocate and initialize crend renderer handle + *------------------------------------------------------------------------*/ + +ivas_error ivas_rend_openCrend( + CREND_WRAPPER *pCrend, + const IVAS_REND_AudioConfig inConfig, + const IVAS_REND_AudioConfig outConfig, + RENDER_CONFIG_DATA *hRendCfg, + const int32_t output_Fs ) +{ + /* TODO tmu : Based on ivas_crend_open() - could be harmonized / refactored */ + int16_t i, subframe_length; + int16_t max_total_ir_len; + HRTFS_HANDLE hHrtf; + CREND_HANDLE hCrend; + ivas_error error; + + error = IVAS_ERR_OK; + subframe_length = (int16_t) ( output_Fs / FRAMES_PER_SEC ) / MAX_PARAM_SPATIAL_SUBFRAMES; + + if ( pCrend->hHrtfCrend == NULL ) + { + if ( ( error = ivas_rend_initCrend( pCrend, inConfig, outConfig, hRendCfg, output_Fs ) ) != IVAS_ERR_OK ) + { + return error; + } + } + + if ( ( hCrend = (CREND_HANDLE) count_malloc( sizeof( CREND_DATA ) ) ) == NULL ) + { + return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for renderer handle" ); + } + + hCrend->lfe_delay_line = NULL; + + for ( i = 0; i < MAX_INTERN_CHANNELS; i++ ) + { + hCrend->freq_buffer_re[i] = NULL; + hCrend->freq_buffer_im[i] = NULL; + } + + for ( i = 0; i < BINAURAL_CHANNELS; i++ ) + { + hCrend->prev_out_buffer[i] = NULL; + } + + hCrend->freq_buffer_re_diffuse = NULL; + hCrend->freq_buffer_im_diffuse = NULL; + hCrend->hReverb = NULL; + hCrend->delay_line_rw_index = 0; + hCrend->diffuse_delay_line_rw_index = 0; + hCrend->hTrack = NULL; + hCrend->m_fYaw = 0; + hCrend->m_fPitch = 0; + hCrend->m_fRoll = 0; + + hHrtf = pCrend->hHrtfCrend; + + if ( hHrtf != NULL ) + { + max_total_ir_len = hHrtf->max_num_iterations * subframe_length; + + for ( i = 0; i < hHrtf->max_num_ir; i++ ) + { + if ( ( hCrend->freq_buffer_re[i] = (float *) count_malloc( sizeof( float ) * max_total_ir_len ) ) == NULL ) + { + return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for Crend" ); + } + set_zero( hCrend->freq_buffer_re[i], max_total_ir_len ); + + if ( ( hCrend->freq_buffer_im[i] = (float *) count_malloc( sizeof( float ) * max_total_ir_len ) ) == NULL ) + { + return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for Crend" ); + } + set_zero( hCrend->freq_buffer_im[i], max_total_ir_len ); + } + + for ( i = 0; i < BINAURAL_CHANNELS; i++ ) + { + if ( ( hCrend->prev_out_buffer[i] = (float *) count_malloc( sizeof( float ) * subframe_length ) ) == NULL ) + { + return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for Crend" ); + } + set_zero( hCrend->prev_out_buffer[i], subframe_length ); + } + + max_total_ir_len = hHrtf->num_iterations_diffuse[0] * subframe_length; + + if ( max_total_ir_len > 0 ) + { + if ( ( hCrend->freq_buffer_re_diffuse = (float *) count_malloc( sizeof( float ) * max_total_ir_len ) ) == NULL ) + { + return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for Crend" ); + } + set_zero( hCrend->freq_buffer_re_diffuse, max_total_ir_len ); + + if ( ( hCrend->freq_buffer_im_diffuse = (float *) count_malloc( sizeof( float ) * max_total_ir_len ) ) == NULL ) + { + return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for Crend" ); + } + set_zero( hCrend->freq_buffer_im_diffuse, max_total_ir_len ); + } + else + { + hCrend->freq_buffer_re_diffuse = NULL; + hCrend->freq_buffer_im_diffuse = NULL; + } + + max_total_ir_len = (int16_t) ( hHrtf->latency_s * output_Fs + 0.5f ) + subframe_length; + if ( max_total_ir_len > 0 ) + { + if ( ( hCrend->lfe_delay_line = (float *) count_malloc( sizeof( float ) * max_total_ir_len ) ) == NULL ) + { + return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for Crend" ); + } + set_zero( hCrend->lfe_delay_line, max_total_ir_len ); + } + else + { + hCrend->lfe_delay_line = NULL; + } + + if ( false ) /* TODO tmu : check renderer headrotation flag */ + { + if ( ( hCrend->hTrack = (ivas_orient_trk_state_t *) count_malloc( sizeof( ivas_orient_trk_state_t ) ) ) == NULL ) + { + return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for Orientation tracking" ); + } + + ivas_orient_trk_Init( hCrend->hTrack ); + } + else + { + hCrend->hTrack = NULL; + } + + if ( ( hRendCfg != NULL ) && ( hRendCfg->roomAcoustics.late_reverb_on ) ) + { + if ( ( error = ivas_reverb_open( &( hCrend->hReverb ), + getIvasAudioConfigFromRendAudioConfig( inConfig ), + pCrend->hHrtfCrend, + hRendCfg, + output_Fs ) ) != IVAS_ERR_OK ) + { + return error; + } + } + else + { + hCrend->hReverb = NULL; + } + + pCrend->binaural_latency_ns = (int32_t) ( pCrend->hHrtfCrend->latency_s * 1000000000.f ); + } + + pCrend->hCrend = hCrend; + return IVAS_ERR_OK; +} + +/*------------------------------------------------------------------------- + * initCrend_from_rom() + * + * Allocate and initialize crend renderer handle + *------------------------------------------------------------------------*/ + +ivas_error ivas_rend_initCrend( + CREND_WRAPPER *pCrend, + const IVAS_REND_AudioConfig inConfig, + const IVAS_REND_AudioConfig outConfig, + RENDER_CONFIG_DATA *hRendCfg, + const int32_t output_Fs ) +{ + int16_t i, j, tmp; + int16_t nchan_in; + bool use_brir; + IVAS_REND_AudioConfigType inConfigType; + HRTFS_HANDLE hHrtf; + ivas_error error; + + inConfigType = getAudioConfigType( inConfig ); + hHrtf = pCrend->hHrtfCrend; + + /* Do all error checks up front so that the nested if later is easier */ + if ( inConfigType != IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED && inConfigType != IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS ) + { + return IVAS_ERROR( IVAS_ERR_INTERNAL_FATAL, "Encountered unsupported input config in Crend" ); + } + if ( outConfig != IVAS_REND_AUDIO_CONFIG_BINAURAL && outConfig != IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM ) + { + return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Encountered unsupported output type in Crend" ); + } + if ( hHrtf == NULL ) + { + if ( ivas_hrtf_open( &hHrtf ) != IVAS_ERR_OK ) + { + return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Could not allocate memory for HRTF handle" ); + } + } + + /* set BRIR flag */ + use_brir = false; + if ( ( hRendCfg != NULL && hRendCfg->roomAcoustics.use_brir ) || outConfig == IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM ) + { + use_brir = true; + } + + + if ( ( error = getAudioConfigNumChannels( inConfig, &nchan_in ) ) != IVAS_ERR_OK ) + { + return error; + } + hHrtf->max_num_ir = nchan_in; + + if ( hHrtf->max_num_ir <= 0 ) + { + return IVAS_ERR_INTERNAL_FATAL; + } + + + if ( inConfigType == IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED ) + { + hHrtf->max_num_ir -= 1; /* subtract LFE */ + hHrtf->gain_lfe = GAIN_LFE; + + if ( output_Fs == 48000 ) + { + if ( use_brir ) + { + hHrtf->latency_s = CRendBin_Combined_BRIR_latency_s_48kHz; + hHrtf->max_num_iterations = CRendBin_Combined_BRIR_max_num_iterations_48kHz; + hHrtf->index_frequency_max_diffuse = CRendBin_Combined_BRIR_index_frequency_max_diffuse_48kHz; + } + else + { + hHrtf->latency_s = CRendBin_Combined_HRIR_latency_s_48kHz; + hHrtf->max_num_iterations = CRendBin_Combined_HRIR_max_num_iterations_48kHz; + hHrtf->index_frequency_max_diffuse = CRendBin_Combined_HRIR_index_frequency_max_diffuse_48kHz; + } + + for ( j = 0; j < BINAURAL_CHANNELS; j++ ) + { + if ( use_brir ) + { + hHrtf->num_iterations_diffuse[j] = CRendBin_Combined_BRIR_num_iterations_diffuse_48kHz[j]; + hHrtf->pIndex_frequency_max_diffuse[j] = CRendBin_Combined_BRIR_pIndex_frequency_max_diffuse_48kHz[j]; + hHrtf->pOut_to_bin_diffuse_re[j] = CRendBin_Combined_BRIR_coeff_diffuse_re_48kHz[j]; + hHrtf->pOut_to_bin_diffuse_im[j] = CRendBin_Combined_BRIR_coeff_diffuse_im_48kHz[j]; + } + else + { + hHrtf->num_iterations_diffuse[j] = CRendBin_Combined_HRIR_num_iterations_diffuse_48kHz[j]; + hHrtf->pIndex_frequency_max_diffuse[j] = CRendBin_Combined_HRIR_pIndex_frequency_max_diffuse_48kHz[j]; + hHrtf->pOut_to_bin_diffuse_re[j] = CRendBin_Combined_HRIR_coeff_diffuse_re_48kHz[j]; + hHrtf->pOut_to_bin_diffuse_im[j] = CRendBin_Combined_HRIR_coeff_diffuse_im_48kHz[j]; + } + } + } + else if ( output_Fs == 32000 ) + { + if ( use_brir ) + { + hHrtf->latency_s = CRendBin_Combined_BRIR_latency_s_32kHz; + hHrtf->max_num_iterations = CRendBin_Combined_BRIR_max_num_iterations_32kHz; + hHrtf->index_frequency_max_diffuse = CRendBin_Combined_BRIR_index_frequency_max_diffuse_32kHz; + } + else + { + hHrtf->latency_s = CRendBin_Combined_HRIR_latency_s_32kHz; + hHrtf->max_num_iterations = CRendBin_Combined_HRIR_max_num_iterations_32kHz; + hHrtf->index_frequency_max_diffuse = CRendBin_Combined_HRIR_index_frequency_max_diffuse_32kHz; + } + + for ( j = 0; j < BINAURAL_CHANNELS; j++ ) + { + if ( use_brir ) + { + hHrtf->num_iterations_diffuse[j] = CRendBin_Combined_BRIR_num_iterations_diffuse_32kHz[j]; + hHrtf->pIndex_frequency_max_diffuse[j] = CRendBin_Combined_BRIR_pIndex_frequency_max_diffuse_32kHz[j]; + hHrtf->pOut_to_bin_diffuse_re[j] = CRendBin_Combined_BRIR_coeff_diffuse_re_32kHz[j]; + hHrtf->pOut_to_bin_diffuse_im[j] = CRendBin_Combined_BRIR_coeff_diffuse_im_32kHz[j]; + } + else + { + hHrtf->num_iterations_diffuse[j] = CRendBin_Combined_HRIR_num_iterations_diffuse_32kHz[j]; + hHrtf->pIndex_frequency_max_diffuse[j] = CRendBin_Combined_HRIR_pIndex_frequency_max_diffuse_32kHz[j]; + hHrtf->pOut_to_bin_diffuse_re[j] = CRendBin_Combined_HRIR_coeff_diffuse_re_32kHz[j]; + hHrtf->pOut_to_bin_diffuse_im[j] = CRendBin_Combined_HRIR_coeff_diffuse_im_32kHz[j]; + } + } + } + else if ( output_Fs == 16000 ) + { + if ( use_brir ) + { + hHrtf->latency_s = CRendBin_Combined_BRIR_latency_s_16kHz; + hHrtf->max_num_iterations = CRendBin_Combined_BRIR_max_num_iterations_16kHz; + hHrtf->index_frequency_max_diffuse = CRendBin_Combined_BRIR_index_frequency_max_diffuse_16kHz; + } + else + { + hHrtf->latency_s = CRendBin_Combined_HRIR_latency_s_16kHz; + hHrtf->max_num_iterations = CRendBin_Combined_HRIR_max_num_iterations_16kHz; + hHrtf->index_frequency_max_diffuse = CRendBin_Combined_HRIR_index_frequency_max_diffuse_16kHz; + } + + for ( j = 0; j < BINAURAL_CHANNELS; j++ ) + { + if ( use_brir ) + { + hHrtf->num_iterations_diffuse[j] = CRendBin_Combined_BRIR_num_iterations_diffuse_16kHz[j]; + hHrtf->pIndex_frequency_max_diffuse[j] = CRendBin_Combined_BRIR_pIndex_frequency_max_diffuse_16kHz[j]; + hHrtf->pOut_to_bin_diffuse_re[j] = CRendBin_Combined_BRIR_coeff_diffuse_re_16kHz[j]; + hHrtf->pOut_to_bin_diffuse_im[j] = CRendBin_Combined_BRIR_coeff_diffuse_im_16kHz[j]; + } + else + { + hHrtf->num_iterations_diffuse[j] = CRendBin_Combined_HRIR_num_iterations_diffuse_16kHz[j]; + hHrtf->pIndex_frequency_max_diffuse[j] = CRendBin_Combined_HRIR_pIndex_frequency_max_diffuse_16kHz[j]; + hHrtf->pOut_to_bin_diffuse_re[j] = CRendBin_Combined_HRIR_coeff_diffuse_re_16kHz[j]; + hHrtf->pOut_to_bin_diffuse_im[j] = CRendBin_Combined_HRIR_coeff_diffuse_im_16kHz[j]; + } + } + } + else + { + return IVAS_ERROR( IVAS_ERR_INVALID_SAMPLING_RATE, "Encountered Unsupported sampling rate in Crend" ); + } + + for ( i = 0; i < hHrtf->max_num_ir; i++ ) + { + if ( inConfig == IVAS_REND_AUDIO_CONFIG_5_1 ) + { + tmp = channelIndex_CICP6[i]; + } + else if ( inConfig == IVAS_REND_AUDIO_CONFIG_7_1 ) + { + tmp = channelIndex_CICP12[i]; + } + else if ( inConfig == IVAS_REND_AUDIO_CONFIG_5_1_2 ) + { + tmp = channelIndex_CICP14[i]; + } + else if ( inConfig == IVAS_REND_AUDIO_CONFIG_5_1_4 ) + { + tmp = channelIndex_CICP16[i]; + } + else if ( inConfig == IVAS_REND_AUDIO_CONFIG_7_1_4 ) + { + tmp = channelIndex_CICP19[i]; + } + else + { + return IVAS_ERROR( IVAS_ERR_INTERNAL_FATAL, "Error: Channel configuration not specified!\n\n" ); + } + + if ( output_Fs == 48000 ) + { + if ( use_brir ) + { + hHrtf->inv_diffuse_weight[i] = CRendBin_Combined_BRIR_inv_diffuse_weight_48kHz[tmp]; + } + else + { + hHrtf->inv_diffuse_weight[i] = CRendBin_Combined_HRIR_inv_diffuse_weight_48kHz[tmp]; + } + + for ( j = 0; j < BINAURAL_CHANNELS; j++ ) + { + if ( use_brir ) + { + hHrtf->num_iterations[i][j] = CRendBin_Combined_BRIR_num_iterations_48kHz[tmp][j]; + hHrtf->pIndex_frequency_max[i][j] = CRendBin_Combined_BRIR_pIndex_frequency_max_48kHz[tmp][j]; + hHrtf->pOut_to_bin_re[i][j] = CRendBin_Combined_BRIR_coeff_re_48kHz[tmp][j]; + hHrtf->pOut_to_bin_im[i][j] = CRendBin_Combined_BRIR_coeff_im_48kHz[tmp][j]; + } + else + { + hHrtf->num_iterations[i][j] = CRendBin_Combined_HRIR_num_iterations_48kHz[tmp][j]; + hHrtf->pIndex_frequency_max[i][j] = CRendBin_Combined_HRIR_pIndex_frequency_max_48kHz[tmp][j]; + hHrtf->pOut_to_bin_re[i][j] = CRendBin_Combined_HRIR_coeff_re_48kHz[tmp][j]; + hHrtf->pOut_to_bin_im[i][j] = CRendBin_Combined_HRIR_coeff_im_48kHz[tmp][j]; + } + } + } + else if ( output_Fs == 32000 ) + { + if ( use_brir ) + { + hHrtf->inv_diffuse_weight[i] = CRendBin_Combined_BRIR_inv_diffuse_weight_32kHz[tmp]; + } + else + { + hHrtf->inv_diffuse_weight[i] = CRendBin_Combined_HRIR_inv_diffuse_weight_32kHz[tmp]; + } + + for ( j = 0; j < BINAURAL_CHANNELS; j++ ) + { + if ( use_brir ) + { + hHrtf->num_iterations[i][j] = CRendBin_Combined_BRIR_num_iterations_32kHz[tmp][j]; + hHrtf->pIndex_frequency_max[i][j] = CRendBin_Combined_BRIR_pIndex_frequency_max_32kHz[tmp][j]; + hHrtf->pOut_to_bin_re[i][j] = CRendBin_Combined_BRIR_coeff_re_32kHz[tmp][j]; + hHrtf->pOut_to_bin_im[i][j] = CRendBin_Combined_BRIR_coeff_im_32kHz[tmp][j]; + } + else + { + hHrtf->num_iterations[i][j] = CRendBin_Combined_HRIR_num_iterations_32kHz[tmp][j]; + hHrtf->pIndex_frequency_max[i][j] = CRendBin_Combined_HRIR_pIndex_frequency_max_32kHz[tmp][j]; + hHrtf->pOut_to_bin_re[i][j] = CRendBin_Combined_HRIR_coeff_re_32kHz[tmp][j]; + hHrtf->pOut_to_bin_im[i][j] = CRendBin_Combined_HRIR_coeff_im_32kHz[tmp][j]; + } + } + } + else if ( output_Fs == 16000 ) + { + if ( use_brir ) + { + hHrtf->inv_diffuse_weight[i] = CRendBin_Combined_BRIR_inv_diffuse_weight_16kHz[tmp]; + } + else + { + hHrtf->inv_diffuse_weight[i] = CRendBin_Combined_HRIR_inv_diffuse_weight_16kHz[tmp]; + } + + for ( j = 0; j < BINAURAL_CHANNELS; j++ ) + { + if ( use_brir ) + { + hHrtf->num_iterations[i][j] = CRendBin_Combined_BRIR_num_iterations_16kHz[tmp][j]; + hHrtf->pIndex_frequency_max[i][j] = CRendBin_Combined_BRIR_pIndex_frequency_max_16kHz[tmp][j]; + hHrtf->pOut_to_bin_re[i][j] = CRendBin_Combined_BRIR_coeff_re_16kHz[tmp][j]; + hHrtf->pOut_to_bin_im[i][j] = CRendBin_Combined_BRIR_coeff_im_16kHz[tmp][j]; + } + else + { + hHrtf->num_iterations[i][j] = CRendBin_Combined_HRIR_num_iterations_16kHz[tmp][j]; + hHrtf->pIndex_frequency_max[i][j] = CRendBin_Combined_HRIR_pIndex_frequency_max_16kHz[tmp][j]; + hHrtf->pOut_to_bin_re[i][j] = CRendBin_Combined_HRIR_coeff_re_16kHz[tmp][j]; + hHrtf->pOut_to_bin_im[i][j] = CRendBin_Combined_HRIR_coeff_im_16kHz[tmp][j]; + } + } + } + else + { + return IVAS_ERROR( IVAS_ERR_INVALID_SAMPLING_RATE, "Encountered Unsupported sampling rate in Crend" ); + } + } + } + else if ( inConfigType == IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS ) + { + if ( output_Fs == 48000 ) + { + hHrtf->latency_s = CRendBin_HOA3_HRIR_latency_s_48kHz; + hHrtf->max_num_iterations = CRendBin_HOA3_HRIR_max_num_iterations_48kHz; + hHrtf->index_frequency_max_diffuse = CRendBin_HOA3_HRIR_index_frequency_max_diffuse_48kHz; + + for ( i = 0; i < hHrtf->max_num_ir; i++ ) + { + hHrtf->inv_diffuse_weight[i] = CRendBin_HOA3_HRIR_inv_diffuse_weight_48kHz[i]; + + for ( j = 0; j < BINAURAL_CHANNELS; j++ ) + { + hHrtf->num_iterations[i][j] = CRendBin_HOA3_HRIR_num_iterations_48kHz[i][j]; + hHrtf->pIndex_frequency_max[i][j] = CRendBin_HOA3_HRIR_pIndex_frequency_max_48kHz[i][j]; + hHrtf->pOut_to_bin_re[i][j] = CRendBin_HOA3_HRIR_coeff_re_48kHz[i][j]; + hHrtf->pOut_to_bin_im[i][j] = CRendBin_HOA3_HRIR_coeff_im_48kHz[i][j]; + } + } + for ( j = 0; j < BINAURAL_CHANNELS; j++ ) + { + hHrtf->num_iterations_diffuse[j] = CRendBin_HOA3_HRIR_num_iterations_diffuse_48kHz[j]; + hHrtf->pIndex_frequency_max_diffuse[j] = CRendBin_HOA3_HRIR_pIndex_frequency_max_diffuse_48kHz[j]; + hHrtf->pOut_to_bin_diffuse_re[j] = CRendBin_HOA3_HRIR_coeff_diffuse_re_48kHz[j]; + hHrtf->pOut_to_bin_diffuse_im[j] = CRendBin_HOA3_HRIR_coeff_diffuse_im_48kHz[j]; + } + } + else if ( output_Fs == 32000 ) + { + hHrtf->latency_s = CRendBin_HOA3_HRIR_latency_s_32kHz; + hHrtf->max_num_iterations = CRendBin_HOA3_HRIR_max_num_iterations_32kHz; + hHrtf->index_frequency_max_diffuse = CRendBin_HOA3_HRIR_index_frequency_max_diffuse_32kHz; + + for ( i = 0; i < hHrtf->max_num_ir; i++ ) + { + hHrtf->inv_diffuse_weight[i] = CRendBin_HOA3_HRIR_inv_diffuse_weight_32kHz[i]; + + for ( j = 0; j < BINAURAL_CHANNELS; j++ ) + { + hHrtf->num_iterations[i][j] = CRendBin_HOA3_HRIR_num_iterations_32kHz[i][j]; + hHrtf->pIndex_frequency_max[i][j] = CRendBin_HOA3_HRIR_pIndex_frequency_max_32kHz[i][j]; + hHrtf->pOut_to_bin_re[i][j] = CRendBin_HOA3_HRIR_coeff_re_32kHz[i][j]; + hHrtf->pOut_to_bin_im[i][j] = CRendBin_HOA3_HRIR_coeff_im_32kHz[i][j]; + } + } + + for ( j = 0; j < BINAURAL_CHANNELS; j++ ) + { + hHrtf->num_iterations_diffuse[j] = CRendBin_HOA3_HRIR_num_iterations_diffuse_32kHz[j]; + hHrtf->pIndex_frequency_max_diffuse[j] = CRendBin_HOA3_HRIR_pIndex_frequency_max_diffuse_32kHz[j]; + hHrtf->pOut_to_bin_diffuse_re[j] = CRendBin_HOA3_HRIR_coeff_diffuse_re_32kHz[j]; + hHrtf->pOut_to_bin_diffuse_im[j] = CRendBin_HOA3_HRIR_coeff_diffuse_im_32kHz[j]; + } + } + else if ( output_Fs == 16000 ) + { + hHrtf->latency_s = CRendBin_HOA3_HRIR_latency_s_16kHz; + hHrtf->max_num_iterations = CRendBin_HOA3_HRIR_max_num_iterations_16kHz; + hHrtf->index_frequency_max_diffuse = CRendBin_HOA3_HRIR_index_frequency_max_diffuse_16kHz; + + for ( i = 0; i < hHrtf->max_num_ir; i++ ) + { + hHrtf->inv_diffuse_weight[i] = CRendBin_HOA3_HRIR_inv_diffuse_weight_16kHz[i]; + + for ( j = 0; j < BINAURAL_CHANNELS; j++ ) + { + hHrtf->num_iterations[i][j] = CRendBin_HOA3_HRIR_num_iterations_16kHz[i][j]; + hHrtf->pIndex_frequency_max[i][j] = CRendBin_HOA3_HRIR_pIndex_frequency_max_16kHz[i][j]; + hHrtf->pOut_to_bin_re[i][j] = CRendBin_HOA3_HRIR_coeff_re_16kHz[i][j]; + hHrtf->pOut_to_bin_im[i][j] = CRendBin_HOA3_HRIR_coeff_im_16kHz[i][j]; + } + } + + for ( j = 0; j < BINAURAL_CHANNELS; j++ ) + { + hHrtf->num_iterations_diffuse[j] = CRendBin_HOA3_HRIR_num_iterations_diffuse_16kHz[j]; + hHrtf->pIndex_frequency_max_diffuse[j] = CRendBin_HOA3_HRIR_pIndex_frequency_max_diffuse_16kHz[j]; + hHrtf->pOut_to_bin_diffuse_re[j] = CRendBin_HOA3_HRIR_coeff_diffuse_re_16kHz[j]; + hHrtf->pOut_to_bin_diffuse_im[j] = CRendBin_HOA3_HRIR_coeff_diffuse_im_16kHz[j]; + } + } + else + { + return IVAS_ERROR( IVAS_ERR_INVALID_SAMPLING_RATE, "Encountered Unsupported sampling rate in Crend" ); + } + } + + else + { + return IVAS_ERROR( IVAS_ERR_INTERNAL, "Unsupported renderer type in Crend" ); + } + + pCrend->hHrtfCrend = hHrtf; + + return IVAS_ERR_OK; +} + +/*------------------------------------------------------------------------- + * ivas_crend_close() + * + * Deallocate Crend renderer handle + *------------------------------------------------------------------------*/ + +ivas_error ivas_rend_closeCrend( + CREND_WRAPPER *pCrend ) +{ + int16_t i; + + if ( pCrend->hHrtfCrend != NULL ) + { + ivas_hrtf_close( &pCrend->hHrtfCrend ); + } + + if ( pCrend->hCrend != NULL ) + { + + for ( i = 0; i < MAX_INTERN_CHANNELS; i++ ) + { + if ( pCrend->hCrend->freq_buffer_re[i] != NULL ) + { + count_free( pCrend->hCrend->freq_buffer_re[i] ); + pCrend->hCrend->freq_buffer_re[i] = NULL; + } + if ( pCrend->hCrend->freq_buffer_im[i] != NULL ) + { + count_free( pCrend->hCrend->freq_buffer_im[i] ); + pCrend->hCrend->freq_buffer_im[i] = NULL; + } + } + + for ( i = 0; i < BINAURAL_CHANNELS; i++ ) + { + if ( pCrend->hCrend->prev_out_buffer[i] != NULL ) + { + count_free( pCrend->hCrend->prev_out_buffer[i] ); + pCrend->hCrend->prev_out_buffer[i] = NULL; + } + } + + if ( pCrend->hCrend->lfe_delay_line != NULL ) + { + count_free( pCrend->hCrend->lfe_delay_line ); + pCrend->hCrend->lfe_delay_line = NULL; + } + + if ( pCrend->hCrend->freq_buffer_re_diffuse != NULL ) + { + count_free( pCrend->hCrend->freq_buffer_re_diffuse ); + pCrend->hCrend->freq_buffer_re_diffuse = NULL; + } + + if ( pCrend->hCrend->freq_buffer_im_diffuse != NULL ) + { + count_free( pCrend->hCrend->freq_buffer_im_diffuse ); + pCrend->hCrend->freq_buffer_im_diffuse = NULL; + } + + if ( pCrend->hCrend->hTrack != NULL ) + { + count_free( pCrend->hCrend->hTrack ); + pCrend->hCrend->hTrack = NULL; + } + + ivas_reverb_close( &pCrend->hCrend->hReverb ); + + count_free( pCrend->hCrend ); + pCrend->hCrend = NULL; + } + + return IVAS_ERR_OK; +} + +/*-----------------------------------------------------------------------------------------* + * Function ivas_rend_crend_Process() + * + * Process call for IVAS Crend renderer + *-----------------------------------------------------------------------------------------*/ + +ivas_error ivas_rend_crendProcess( + const CREND_WRAPPER *pCrend, + const IVAS_REND_AudioConfig inConfig, + const IVAS_REND_AudioConfig outConfig, + float output[][L_FRAME48k], /* i/o: input/output audio channels */ + const int32_t output_Fs ) +{ + int16_t i, subframe_idx, output_frame; + int16_t nchan_out; + float pcm_tmp[BINAURAL_CHANNELS][L_FRAME48k]; + AUDIO_CONFIG in_config; + IVAS_REND_AudioConfigType inConfigType; + ivas_error error; + + wmops_sub_start( "ivas_rend_crendProcess" ); + + in_config = getIvasAudioConfigFromRendAudioConfig( inConfig ); + inConfigType = getAudioConfigType( inConfig ); + getAudioConfigNumChannels( outConfig, &nchan_out ); + output_frame = (int16_t) ( output_Fs / FRAMES_PER_SEC ); + + for ( subframe_idx = 0; subframe_idx < MAX_PARAM_SPATIAL_SUBFRAMES; subframe_idx++ ) + { + if ( ( inConfigType == IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED ) || ( inConfigType == IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS ) ) + { + if ( ( error = ivas_rend_crendConvolver( pCrend, inConfig, outConfig, output, pcm_tmp, output_Fs, subframe_idx ) ) != IVAS_ERR_OK ) + { + return error; + } + + if ( pCrend->hCrend->hReverb != NULL ) + { + if ( ( error = ivas_reverb_process( pCrend->hCrend->hReverb, in_config, 1, output, pcm_tmp, subframe_idx ) ) != IVAS_ERR_OK ) + { + return error; + } + } + } + else + { + return IVAS_ERR_INVALID_INPUT_FORMAT; + } + } + + /* move to output */ + for ( i = 0; i < nchan_out; i++ ) + { + mvr2r( pcm_tmp[i], output[i], output_frame ); + } + + wmops_sub_end(); + + return IVAS_ERR_OK; +} + +/*-----------------------------------------------------------------------------------------* + * Function ivas_crend_convolver() + * + * Convolver block + *-----------------------------------------------------------------------------------------*/ + +ivas_error ivas_rend_crendConvolver( + const CREND_WRAPPER *pCrend, + IVAS_REND_AudioConfig inConfig, + IVAS_REND_AudioConfig outConfig, + float pcm_in[][L_FRAME48k], + float pcm_out[][L_FRAME48k], + const int32_t output_Fs, + const int16_t i_ts ) +{ + int16_t i, j, k, m; + int16_t subframe_length, idx_in; + int16_t lfe_idx_in; + int16_t offset, offset_in, offset_diffuse; + int16_t nchan_in, nchan_out; + float *pIn; + float *pFreq_buf_re, *pFreq_buf_im; + float *pFreq_filt_re, *pFreq_filt_im; + float pOut[L_FRAME48k * 2]; + float tmp_out_re[L_FRAME48k], tmp_out_im[L_FRAME48k]; + + getAudioConfigNumChannels( inConfig, &nchan_in ); + getAudioConfigNumChannels( outConfig, &nchan_out ); + + subframe_length = (int16_t) ( output_Fs / FRAMES_PER_SEC ) / MAX_PARAM_SPATIAL_SUBFRAMES; + + lfe_idx_in = -1; + if ( getAudioConfigType( inConfig ) == IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED ) + { + if ( inConfig != IVAS_REND_AUDIO_CONFIG_LS_CUSTOM ) + { + lfe_idx_in = LFE_CHANNEL; + } + else + { + assert( 0 && "Custom LS not supported in CRend" ); + } + } + + offset = pCrend->hCrend->delay_line_rw_index * subframe_length; /* subframe_length * ( pCrend->hHrtfCrend->max_num_iterations - 1 ); */ + offset_diffuse = pCrend->hCrend->diffuse_delay_line_rw_index * subframe_length; /* subframe_length *( pCrend->hHrtfCrend->num_iterations_diffuse[0] - 1 ); */ + + if ( pCrend->hHrtfCrend->num_iterations_diffuse[0] > 0 ) + { + set_zero( &pCrend->hCrend->freq_buffer_re_diffuse[offset_diffuse], subframe_length ); + set_zero( &pCrend->hCrend->freq_buffer_im_diffuse[offset_diffuse], subframe_length ); + } + + i = 0; + for ( idx_in = 0; idx_in < nchan_in; idx_in++ ) + { + pIn = &pcm_in[idx_in][i_ts * subframe_length]; + if ( idx_in != lfe_idx_in ) + { + if ( pCrend->hHrtfCrend->num_iterations_diffuse[0] > 0 ) + { + pFreq_buf_re = &pCrend->hCrend->freq_buffer_re_diffuse[offset_diffuse]; + pFreq_buf_im = &pCrend->hCrend->freq_buffer_im_diffuse[offset_diffuse]; + pFreq_filt_re = &pCrend->hCrend->freq_buffer_re[i][offset]; + pFreq_filt_im = &pCrend->hCrend->freq_buffer_im[i][offset]; + + for ( k = 0; k < pCrend->hHrtfCrend->index_frequency_max_diffuse; k++ ) + { + pFreq_buf_re[k] += pFreq_filt_re[k] * pCrend->hHrtfCrend->inv_diffuse_weight[i]; + pFreq_buf_im[k] += pFreq_filt_im[k] * pCrend->hHrtfCrend->inv_diffuse_weight[i]; + } + } + + pFreq_buf_re = &pCrend->hCrend->freq_buffer_re[i][offset]; + pFreq_buf_im = &pCrend->hCrend->freq_buffer_im[i][offset]; + + ivas_mdft( pIn, pFreq_buf_re, pFreq_buf_im, subframe_length, subframe_length ); + i++; + } + } + + for ( j = 0; j < nchan_out; j++ ) + { + set_zero( tmp_out_re, subframe_length ); + set_zero( tmp_out_im, subframe_length ); + + i = 0; + for ( idx_in = 0; idx_in < nchan_in; idx_in++ ) + { + if ( idx_in != lfe_idx_in ) + { + offset = 0; + for ( m = 0; m < pCrend->hHrtfCrend->num_iterations[i][j]; m++ ) + { + offset_in = ( pCrend->hCrend->delay_line_rw_index + pCrend->hHrtfCrend->max_num_iterations - pCrend->hHrtfCrend->num_iterations[i][j] + m + 1 ); + offset_in = offset_in % ( pCrend->hHrtfCrend->max_num_iterations ); + offset_in = offset_in * subframe_length; + pFreq_buf_re = &pCrend->hCrend->freq_buffer_re[i][offset_in]; + pFreq_buf_im = &pCrend->hCrend->freq_buffer_im[i][offset_in]; + pFreq_filt_re = &pCrend->hHrtfCrend->pOut_to_bin_re[i][j][offset]; + pFreq_filt_im = &pCrend->hHrtfCrend->pOut_to_bin_im[i][j][offset]; + + for ( k = 0; k < pCrend->hHrtfCrend->pIndex_frequency_max[i][j][m]; k++ ) + { + tmp_out_re[k] += pFreq_buf_re[k] * pFreq_filt_re[k] - pFreq_buf_im[k] * pFreq_filt_im[k]; + tmp_out_im[k] += pFreq_buf_re[k] * pFreq_filt_im[k] + pFreq_buf_im[k] * pFreq_filt_re[k]; + } + offset = offset + k; + } + i++; + } + } + + offset = 0; + for ( m = 0; m < pCrend->hHrtfCrend->num_iterations_diffuse[j]; m++ ) + { + offset_diffuse = ( pCrend->hCrend->diffuse_delay_line_rw_index + m + 1 ); + offset_diffuse = offset_diffuse % pCrend->hHrtfCrend->num_iterations_diffuse[0]; + offset_diffuse = offset_diffuse * subframe_length; + pFreq_buf_re = &pCrend->hCrend->freq_buffer_re_diffuse[offset_diffuse]; + pFreq_buf_im = &pCrend->hCrend->freq_buffer_im_diffuse[offset_diffuse]; + pFreq_filt_re = &pCrend->hHrtfCrend->pOut_to_bin_diffuse_re[j][offset]; + pFreq_filt_im = &pCrend->hHrtfCrend->pOut_to_bin_diffuse_im[j][offset]; + + for ( k = 0; k < pCrend->hHrtfCrend->pIndex_frequency_max_diffuse[j][m]; k++ ) + { + tmp_out_re[k] += pFreq_buf_re[k] * pFreq_filt_re[k] - pFreq_buf_im[k] * pFreq_filt_im[k]; + tmp_out_im[k] += pFreq_buf_re[k] * pFreq_filt_im[k] + pFreq_buf_im[k] * pFreq_filt_re[k]; + } + offset = offset + k; + } + + ivas_imdft( tmp_out_re, tmp_out_im, pOut, subframe_length ); + + pFreq_buf_re = &pcm_out[j][i_ts * subframe_length]; + for ( k = 0; k < subframe_length; k++ ) + { + pFreq_buf_re[k] = pOut[k] + pCrend->hCrend->prev_out_buffer[j][k]; + pCrend->hCrend->prev_out_buffer[j][k] = pOut[k + subframe_length]; + } + } + + pCrend->hCrend->delay_line_rw_index++; + pCrend->hCrend->delay_line_rw_index = pCrend->hCrend->delay_line_rw_index % ( pCrend->hHrtfCrend->max_num_iterations ); + if ( pCrend->hHrtfCrend->num_iterations_diffuse[0] > 0 ) + { + pCrend->hCrend->diffuse_delay_line_rw_index++; + pCrend->hCrend->diffuse_delay_line_rw_index = pCrend->hCrend->diffuse_delay_line_rw_index % ( pCrend->hHrtfCrend->num_iterations_diffuse[0] ); + } + + return IVAS_ERR_OK; +} + +#endif diff --git a/lib_dec/ivas_efap.c b/lib_rend/ivas_efap.c similarity index 99% rename from lib_dec/ivas_efap.c rename to lib_rend/ivas_efap.c index 788eb58f33766140d654ca22ff0786738009f87e..8314d3117f02f69ba8ce603490b0206bbaef5f0e 100644 --- a/lib_dec/ivas_efap.c +++ b/lib_rend/ivas_efap.c @@ -155,8 +155,12 @@ ivas_error efap_init_data( if ( !speaker_node_azi_deg || !speaker_node_ele_deg ) { hEFAPdata = NULL; +#ifdef EXT_RENDERER + return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "EFAP requires arrays of speaker azimuths and elevations" ); +#else /* TODO: is this path correct behaviour or and error ? */ return IVAS_ERR_OK; +#endif } /*-----------------------------------------------------------------* @@ -1325,17 +1329,21 @@ static void efap_panning( float tmpBuff[EFAP_MAX_CHAN_NUM]; float normTmpBuff; float P[2]; +#ifndef FIX_EFAP_MATH float P_tmp[2]; +#endif P[0] = azi; P[1] = ele; /* Finding in which polygon the point is */ +#ifndef FIX_EFAP_MATH P_tmp[0] = roundf( P[0] / PANNING_AZI_RESOLUTION ); P_tmp[1] = roundf( P[1] / PANNING_ELE_RESOLUTION ); P[0] = P_tmp[0] * PANNING_AZI_RESOLUTION; P[1] = P_tmp[1] * PANNING_ELE_RESOLUTION; +#endif polyIdx = get_poly_num( P, polyData ); @@ -2257,7 +2265,11 @@ static int16_t in_tri( /* Verification of the non-colinearity */ invFactor = tmpDot1[0] * tmpDot2[1] - tmpDot1[1] * tmpDot2[0]; +#ifdef FIX_EFAP_MATH + if ( fabsf( invFactor ) < thresh ) +#else if ( invFactor < thresh ) +#endif { return 0; } diff --git a/lib_dec/ivas_hrtf.c b/lib_rend/ivas_hrtf.c similarity index 99% rename from lib_dec/ivas_hrtf.c rename to lib_rend/ivas_hrtf.c index 2169267206422bc03a4131ead67d1b3a4de749b1..7c4183b0e8cb0bb0db50bdee7b46fd9558ed6b80 100644 --- a/lib_dec/ivas_hrtf.c +++ b/lib_rend/ivas_hrtf.c @@ -32,7 +32,6 @@ #include #include "options.h" -#include "hrtf_file_reader.h" #include "prot.h" #include "ivas_prot.h" #include "lib_dec.h" diff --git a/lib_rend/ivas_lib_rend_internal.h b/lib_rend/ivas_lib_rend_internal.h new file mode 100644 index 0000000000000000000000000000000000000000..94a762c315814b5f06a6ba2c4917213d173e6f73 --- /dev/null +++ b/lib_rend/ivas_lib_rend_internal.h @@ -0,0 +1,123 @@ +/****************************************************************************************************** + + (C) 2022 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository. All Rights Reserved. + + This software is protected by copyright law and by international treaties. + The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository retain full ownership rights in their respective contributions in + the software. This notice grants no license of any kind, including but not limited to patent + license, nor is any license granted by implication, estoppel or otherwise. + + Contributors are required to enter into the IVAS codec Public Collaboration agreement before making + contributions. + + This software is provided "AS IS", without any express or implied warranties. The software is in the + development stage. It is intended exclusively for experts who have experience with such software and + solely for the purpose of inspection. All implied warranties of non-infringement, merchantability + and fitness for a particular purpose are hereby disclaimed and excluded. + + Any dispute, controversy or claim arising under or in relation to providing this software shall be + submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in + accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and + the United Nations Convention on Contracts on the International Sales of Goods. + +*******************************************************************************************************/ + +#include "ivas_error.h" +#include "lib_rend.h" +#include "ivas_stat_dec.h" + +#ifndef IVAS_LIB_REND_INTERNALS_H +#define IVAS_LIB_REND_INTERNALS_H + +#ifdef EXT_RENDERER +typedef struct +{ + int8_t headRotEnabled; + IVAS_QUATERNION headPositions[RENDERER_HEAD_POSITIONS_PER_FRAME]; + float crossfade[L_FRAME48k / RENDERER_HEAD_POSITIONS_PER_FRAME]; +} IVAS_REND_HeadRotData; + +typedef struct +{ + int32_t binaural_latency_ns; + BINAURAL_TD_OBJECT_RENDERER_HANDLE hBinRendererTd; + TDREND_HRFILT_FiltSet_t *hHrtfTD; +} TDREND_WRAPPER; + +typedef struct +{ + int32_t binaural_latency_ns; + CREND_HANDLE hCrend; + HRTFS_HANDLE hHrtfCrend; +} CREND_WRAPPER; + +IVAS_REND_AudioConfigType getAudioConfigType( + const IVAS_REND_AudioConfig config ); + +ivas_error getAudioConfigNumChannels( + const IVAS_REND_AudioConfig config, + int16_t *numChannels ); + +AUDIO_CONFIG getIvasAudioConfigFromRendAudioConfig( + IVAS_REND_AudioConfig config ); + +ivas_error ivas_rend_openCrend( + CREND_WRAPPER *pCrend, + const IVAS_REND_AudioConfig inConfig, + const IVAS_REND_AudioConfig outConfig, + RENDER_CONFIG_DATA *hRendCfg, + const int32_t output_Fs ); + +ivas_error ivas_rend_initCrend( + CREND_WRAPPER *pCrend, + const IVAS_REND_AudioConfig inConfig, + const IVAS_REND_AudioConfig outConfig, + RENDER_CONFIG_DATA *hRendCfg, + const int32_t output_Fs ); + +ivas_error ivas_rend_closeCrend( + CREND_WRAPPER *pCrend ); + +ivas_error ivas_rend_crendProcess( + const CREND_WRAPPER *pCrend, + const IVAS_REND_AudioConfig inConfig, + const IVAS_REND_AudioConfig outConfig, + float output[][L_FRAME48k], /* i/o: input/output audio channels */ + const int32_t output_Fs ); + +ivas_error ivas_rend_crendConvolver( + const CREND_WRAPPER *pCrend, + const IVAS_REND_AudioConfig inConfig, + const IVAS_REND_AudioConfig outConfig, + float pcm_in[][L_FRAME48k], + float pcm_out[][L_FRAME48k], + const int32_t output_Fs, + const int16_t i_ts ); + +ivas_error ivas_rend_TDObjRenderFrame( + const TDREND_WRAPPER *pTDRend, /* i : TD Renderer wrapper structure */ + const IVAS_REND_AudioConfig inConfig, /* i : Input audio configuration */ + const LSSETUP_CUSTOM_STRUCT *customLsInput, /* i : Input custom loudspeaker layout */ + const IVAS_REND_HeadRotData *headRotData, /* i : Input head positions */ + const IVAS_REND_AudioObjectPosition *currentPos, /* i : Object position */ + const int16_t output_frame, /* i : output frame length */ + const int32_t output_Fs, /* i : output sampling rate */ + float output[][L_FRAME48k] /* i/o: SCE channels / Binaural synthesis */ +); + +ivas_error ivas_rend_TDObjRendOpen( + TDREND_WRAPPER *pTDRend, + const IVAS_REND_AudioConfig inConfig, + LSSETUP_CUSTOM_STRUCT *customLsInput, + const int32_t output_Fs ); + +#endif +#endif diff --git a/lib_dec/ivas_limiter.c b/lib_rend/ivas_limiter.c similarity index 96% rename from lib_dec/ivas_limiter.c rename to lib_rend/ivas_limiter.c index e483318be8c08350ee00ea008baeb48707d92da4..fd116223c5d42aaad6c84f4c34daa714c71104c5 100644 --- a/lib_dec/ivas_limiter.c +++ b/lib_rend/ivas_limiter.c @@ -29,7 +29,7 @@ the United Nations Convention on Contracts on the International Sales of Goods. *******************************************************************************************************/ - +// VE2AT: keep in lib_rend or move to lib_dec ? #include #include #include @@ -39,6 +39,7 @@ #include "wmops.h" #include +#ifndef EXT_RENDERER /*----------------------------------------------------------------------------------* * Local constants *----------------------------------------------------------------------------------*/ @@ -46,6 +47,7 @@ #define LIMITER_THRESHOLD 32729 /* -0.01 dBFS */ #define LIMITER_ATTACK_SECONDS 0.005f +#endif /*-------------------------------------------------------------------* * detect_strong_saturations() @@ -71,11 +73,19 @@ static int16_t detect_strong_saturations( *strong_saturation_cnt = 50; apply_strong_limiting = 1; } +#ifdef EXT_RENDERER + else if ( max_val > 3 * IVAS_LIMITER_THRESHOLD && *strong_saturation_cnt > 0 ) +#else else if ( max_val > 3 * LIMITER_THRESHOLD && *strong_saturation_cnt > 0 ) +#endif { apply_strong_limiting = 1; } +#ifdef EXT_RENDERER + else if ( max_val > 10 * IVAS_LIMITER_THRESHOLD ) +#else else if ( max_val > 10 * LIMITER_THRESHOLD ) +#endif { *strong_saturation_cnt += 20; *strong_saturation_cnt = min( *strong_saturation_cnt, 50 ); @@ -130,7 +140,11 @@ IVAS_LIMITER_HANDLE ivas_limiter_open( hLimiter->sampling_rate = sampling_rate; hLimiter->gain = 1.f; hLimiter->release_heuristic = 0.f; +#ifdef EXT_RENDERER + hLimiter->attack_constant = powf( 0.01f, 1.0f / ( IVAS_LIMITER_ATTACK_SECONDS * sampling_rate ) ); +#else hLimiter->attack_constant = powf( 0.01f, 1.0f / ( LIMITER_ATTACK_SECONDS * sampling_rate ) ); +#endif hLimiter->strong_saturation_count = 0; #ifdef DEBUGGING hLimiter->cnt_frames_limited = 0; @@ -202,7 +216,11 @@ void ivas_limiter_dec( channels[c] = output[c]; } +#ifdef EXT_RENDERER + limiter_process( hLimiter, output_frame, IVAS_LIMITER_THRESHOLD, BER_detect, &hLimiter->strong_saturation_count ); +#else limiter_process( hLimiter, output_frame, LIMITER_THRESHOLD, BER_detect, &hLimiter->strong_saturation_count ); +#endif return; } diff --git a/lib_dec/ivas_ls_custom_dec.c b/lib_rend/ivas_ls_custom_dec.c similarity index 96% rename from lib_dec/ivas_ls_custom_dec.c rename to lib_rend/ivas_ls_custom_dec.c index 1d61ba0641980957ae0ff9baef0d72f1f27bdbe9..4f1042036386e5f3bbe103dd576ed5c5e0d0574a 100644 --- a/lib_dec/ivas_ls_custom_dec.c +++ b/lib_rend/ivas_ls_custom_dec.c @@ -79,8 +79,13 @@ ivas_error ivas_ls_custom_open( *-------------------------------------------------------------------------*/ void ivas_ls_custom_setup( +#ifdef EXT_RENDERER + IVAS_OUTPUT_SETUP_HANDLE hOutSetup, /* o : IVAS output setup handle */ + const LSSETUP_CUSTOM_STRUCT *hLsSetupCustom /* i : Custom loudspeaker setup handle */ +#else IVAS_OUTPUT_SETUP_HANDLE hOutSetup, /* o : IVAS output setup handle */ const LSSETUP_CUSTOM_HANDLE hLsSetupCustom /* i : Custom loudspeaker setup handle */ +#endif ) { hOutSetup->output_config = AUDIO_CONFIG_LS_CUSTOM; diff --git a/lib_dec/ivas_objectRenderer.c b/lib_rend/ivas_objectRenderer.c similarity index 60% rename from lib_dec/ivas_objectRenderer.c rename to lib_rend/ivas_objectRenderer.c index 59e63bd9ff3dca15a945c8a0bf73c8c3215a7568..31bc3b5e0a9c705db9e97a5d03627888b362d5cf 100644 --- a/lib_dec/ivas_objectRenderer.c +++ b/lib_rend/ivas_objectRenderer.c @@ -37,6 +37,10 @@ #include #include "wmops.h" #include "ivas_rom_com.h" +#ifdef EXT_RENDERER +#include "lib_rend.h" +#include "ivas_lib_rend_internal.h" +#endif #ifdef DEBUGGING #include "debug.h" #endif @@ -47,9 +51,25 @@ *---------------------------------------------------------------------*/ static ivas_error TDREND_GetMix( BINAURAL_TD_OBJECT_RENDERER_HANDLE hBinRendererTd, float output[][L_FRAME48k], const int16_t subframe_length, const int32_t output_Fs, const int16_t subframe_idx ); + static void TDREND_Clear_Update_flags( BINAURAL_TD_OBJECT_RENDERER_HANDLE hBinRendererTd ); -static void TDREND_Update_listener_orientation( BINAURAL_TD_OBJECT_RENDERER_HANDLE hBinRendererTd, const int16_t headRotEnabled, const Quaternion *headPosition ); -static void TDREND_Update_object_positions( BINAURAL_TD_OBJECT_RENDERER_HANDLE hBinRendererTd, const int16_t numSources, const IVAS_FORMAT in_format, const ISM_METADATA_HANDLE *hIsmMetaData, float output[][L_FRAME48k] ); + +static void TDREND_Update_listener_orientation( BINAURAL_TD_OBJECT_RENDERER_HANDLE hBinRendererTd, + const int16_t headRotEnabled, +#ifdef EXT_RENDERER + const IVAS_QUATERNION *headPosition +#else + const Quaternion *headPosition +#endif +); +static void TDREND_Update_object_positions( BINAURAL_TD_OBJECT_RENDERER_HANDLE hBinRendererTd, + const int16_t numSources, +#ifdef EXT_RENDERER + const int16_t lfe_idx, +#endif + const IVAS_FORMAT in_format, + const ISM_METADATA_HANDLE *hIsmMetaData, + float output[][L_FRAME48k] ); /*---------------------------------------------------------------------* * ivas_td_binaural_open() @@ -144,6 +164,12 @@ ivas_error ivas_td_binaural_open( ls_azimuth = ls_azimuth_CICP19; ls_elevation = ls_elevation_CICP19; break; +#ifdef EXT_RENDERER + case AUDIO_CONFIG_LS_CUSTOM: + ls_azimuth = st_ivas->hTransSetup.ls_azimuth; + ls_elevation = st_ivas->hTransSetup.ls_elevation; + break; +#endif default: ls_azimuth = NULL; ls_elevation = NULL; @@ -236,7 +262,11 @@ void ObjRenderIVASFrame( } /* Update object position(s) */ +#ifdef EXT_RENDERER + TDREND_Update_object_positions( st_ivas->hBinRendererTd, st_ivas->nchan_transport, LFE_CHANNEL, st_ivas->ivas_format, st_ivas->hIsmMetaData, output ); +#else TDREND_Update_object_positions( st_ivas->hBinRendererTd, st_ivas->nchan_transport, st_ivas->ivas_format, st_ivas->hIsmMetaData, output ); +#endif for ( subframe_idx = 0; subframe_idx < MAX_PARAM_SPATIAL_SUBFRAMES; subframe_idx++ ) { @@ -274,11 +304,11 @@ void ObjRenderIVASFrame( * Render one 5 ms subframe from the mixer *---------------------------------------------------------------------*/ static ivas_error TDREND_GetMix( - BINAURAL_TD_OBJECT_RENDERER_HANDLE hBinRendererTd, /* i/o: TD renderer handle */ + BINAURAL_TD_OBJECT_RENDERER_HANDLE hBinRendererTd, /* i/o: TD renderer handle */ float output[][L_FRAME48k], /* i/o: ISm object synth / rendered output in 0,1 */ - const int16_t subframe_length, /* i/o: subframe length */ + const int16_t subframe_length, /* i/o: subframe length */ const int32_t output_Fs, /* i : Output sampling rate */ - const int16_t subframe_idx /* i : Subframe index to 5 ms subframe */ + const int16_t subframe_idx /* i : Subframe index to 5 ms subframe */ ) { int16_t i; @@ -360,9 +390,12 @@ static void TDREND_Clear_Update_flags( static void TDREND_Update_object_positions( BINAURAL_TD_OBJECT_RENDERER_HANDLE hBinRendererTd, /* i/o : TD Renderer handle */ const int16_t numSources, /* i : Number of sources to render */ - const IVAS_FORMAT in_format, /* i : Format of input sources */ - const ISM_METADATA_HANDLE *hIsmMetaData, /* i : Input metadata for ISM objects */ - float output[][L_FRAME48k] /* i/o: SCE/MC channels */ +#ifdef EXT_RENDERER + const int16_t lfe_idx, /* i : Input LFE index */ +#endif + const IVAS_FORMAT in_format, /* i : Format of input sources */ + const ISM_METADATA_HANDLE *hIsmMetaData, /* i : Input metadata for ISM objects */ + float output[][L_FRAME48k] /* i/o: SCE/MC channels */ ) { TDREND_DirAtten_t *DirAtten_p; @@ -377,7 +410,11 @@ static void TDREND_Update_object_positions( c_indx = 0; for ( nS = 0; nS < numSources; nS++ ) { +#ifdef EXT_RENDERER + if ( !( in_format == MC_FORMAT && nS == lfe_idx ) ) /* Skip LFE for MC */ +#else if ( !( in_format == MC_FORMAT && nS == LFE_CHANNEL ) ) /* Skip LFE for MC */ +#endif { hBinRendererTd->Sources[c_indx]->InputFrame_p = output[nS]; hBinRendererTd->Sources[c_indx]->SrcRend_p->InputAvailable = TRUE; @@ -421,7 +458,11 @@ static void TDREND_Update_object_positions( static void TDREND_Update_listener_orientation( BINAURAL_TD_OBJECT_RENDERER_HANDLE hBinRendererTd, /* i/o: TD Renderer handle */ const int16_t headRotEnabled, /* i : Headrotation flag */ - const Quaternion *headPosition /* i : Head Position */ +#ifdef EXT_RENDERER + const IVAS_QUATERNION *headPosition /* i : Head Position */ +#else + const Quaternion *headPosition /* i : Head Position */ +#endif ) { float Pos[3]; @@ -466,3 +507,252 @@ static void TDREND_Update_listener_orientation( return; } + +#ifdef EXT_RENDERER + +ivas_error ivas_rend_TDObjRendOpen( + TDREND_WRAPPER *pTDRend, + IVAS_REND_AudioConfig inConfig, + LSSETUP_CUSTOM_STRUCT *customLsInput, + const int32_t outFs ) +{ + /* TODO tmu : Based on ivas_td_binaural_open() - could be harmonized / refactored + - review error handling + - review hHrtfTD init + */ + BINAURAL_TD_OBJECT_RENDERER_HANDLE hBinRendererTd; + TDREND_PosType_t PosType; + int16_t nS; + int16_t SrcInd[MAX_NUM_TDREND_CHANNELS]; + const float *ls_azimuth, *ls_elevation; + float Pos[3]; + float Dir[3]; + TDREND_DirAtten_t *DirAtten_p; + int16_t nchan_rend; + ivas_error error; + + error = IVAS_ERR_OK; + + if ( ( hBinRendererTd = count_malloc( sizeof( BINAURAL_TD_OBJECT_RENDERER ) ) ) == NULL ) + { + return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for TD renderer\n" ) ); + } + if ( ( hBinRendererTd->TdRend_MixSpatSpec_p = count_malloc( sizeof( TDREND_MixSpatSpec_t ) ) ) == NULL ) + { + return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for TD renderer\n" ) ); + } + if ( ( hBinRendererTd->DirAtten_p = count_malloc( sizeof( TDREND_DirAtten_t ) ) ) == NULL ) + { + return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for TD renderer\n" ) ); + } + if ( ( hBinRendererTd->Listener_p = count_malloc( sizeof( TDREND_MIX_Listener_t ) ) ) == NULL ) + { + return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for TD renderer\n" ) ); + } + + if ( inConfig != IVAS_REND_AUDIO_CONFIG_LS_CUSTOM ) + { + getAudioConfigNumChannels( inConfig, &nchan_rend ); + if ( getAudioConfigType( inConfig ) == IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED ) + { + nchan_rend--; /* Skip LFE channel -- added to the others */ + } + } + else + { + nchan_rend = customLsInput->num_spk; + } + + hBinRendererTd->NumOfSrcs = 0; + hBinRendererTd->MaxSrcInd = -1; + + /* Mixer spatial setup */ + hBinRendererTd->TdRend_MixSpatSpec_p->UseCommonDistAttenModel = TRUE; + hBinRendererTd->TdRend_MixSpatSpec_p->DistAttenModel = 0; /* 0=Turned off, else use TDREND_DIST_ATTEN_MODEL_INV_DIST_CLAMPED */ + + TDREND_MIX_Init( hBinRendererTd, &pTDRend->hHrtfTD, hBinRendererTd->TdRend_MixSpatSpec_p, outFs ); + + /* Set the attenuation (or can set MixSpatSpec.DistAttenModel above) */ + TDREND_MIX_SetDistAttenModel( hBinRendererTd, TDREND_DIST_ATTEN_MODEL_INV_DIST_CLAMPED ); + + /* Add sources to module and mixer, headphones */ + PosType = TDREND_POSTYPE_ABSOLUTE; /* or TDREND_POSTYPE_RELATIVE_TO_LISTENER */ + + for ( nS = 0; nS < nchan_rend; nS++ ) + { + if ( ( error = TDREND_MIX_AddSrc( hBinRendererTd, &SrcInd[nS], PosType, outFs ) ) != IVAS_ERR_OK ) + { + return error; + } + } + + if ( getAudioConfigType( inConfig ) == IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED ) + { + switch ( inConfig ) + { + case IVAS_REND_AUDIO_CONFIG_5_1: + ls_azimuth = ls_azimuth_CICP6; + ls_elevation = ls_elevation_CICP6; + break; + case IVAS_REND_AUDIO_CONFIG_7_1: + ls_azimuth = ls_azimuth_CICP12; + ls_elevation = ls_elevation_CICP12; + break; + case IVAS_REND_AUDIO_CONFIG_5_1_2: + ls_azimuth = ls_azimuth_CICP14; + ls_elevation = ls_elevation_CICP14; + break; + case IVAS_REND_AUDIO_CONFIG_5_1_4: + ls_azimuth = ls_azimuth_CICP16; + ls_elevation = ls_elevation_CICP16; + break; + case IVAS_REND_AUDIO_CONFIG_7_1_4: + ls_azimuth = ls_azimuth_CICP19; + ls_elevation = ls_elevation_CICP19; + break; + case IVAS_REND_AUDIO_CONFIG_LS_CUSTOM: + ls_azimuth = customLsInput->ls_azimuth; + ls_elevation = customLsInput->ls_elevation; + break; + default: + ls_azimuth = NULL; + ls_elevation = NULL; + } + + DirAtten_p = hBinRendererTd->DirAtten_p; + + for ( nS = 0; nS < nchan_rend; nS++ ) + { + /* Set source positions according to loudspeaker layout */ + Pos[0] = cosf( ls_elevation[nS] * PI_OVER_180 ) * cosf( ls_azimuth[nS] * PI_OVER_180 ); + Pos[1] = cosf( ls_elevation[nS] * PI_OVER_180 ) * sinf( ls_azimuth[nS] * PI_OVER_180 ); + Pos[2] = sinf( ls_elevation[nS] * PI_OVER_180 ); + Dir[0] = 1.0f; + Dir[1] = 0.0f; + Dir[2] = 0.0f; + + /* Source directivity info */ + DirAtten_p->ConeInnerAngle = 360.0f; + DirAtten_p->ConeOuterAngle = 360.0f; + DirAtten_p->ConeOuterGain = 1.0f; + + TDREND_MIX_SRC_SetPos( hBinRendererTd, nS, Pos ); + TDREND_MIX_SRC_SetDir( hBinRendererTd, nS, Dir ); + TDREND_MIX_SRC_SetPlayState( hBinRendererTd, nS, TDREND_PLAYSTATUS_PLAYING ); + TDREND_MIX_SRC_SetDirAtten( hBinRendererTd, nS, DirAtten_p ); + } + } + + pTDRend->hBinRendererTd = hBinRendererTd; + + pTDRend->binaural_latency_ns = (int32_t) ( BINAURAL_TD_LATENCY_S * 1000000000.f ); + + return IVAS_ERR_OK; +} + +/*---------------------------------------------------------------------* + * ObjRenderIVASFrame() + * + * Receives the current frames for the object streams, updates metadata + * and renders the current frame. + *---------------------------------------------------------------------*/ + +/*! r: TD Renderer result code. */ +ivas_error ivas_rend_TDObjRenderFrame( + const TDREND_WRAPPER *pTDRend, /* i : TD Renderer wrapper structure */ + const IVAS_REND_AudioConfig inConfig, /* i : Input audio configuration */ + const LSSETUP_CUSTOM_STRUCT *customLsInput, /* i : Input custom loudspeaker layout */ + const IVAS_REND_HeadRotData *headRotData, /* i : Input head positions */ + const IVAS_REND_AudioObjectPosition *currentPos, /* i : Object position */ + const int16_t output_frame, /* i : output frame length */ + const int32_t output_Fs, /* i : output sampling rate */ + float output[][L_FRAME48k] /* i/o: SCE channels / Binaural synthesis */ +) +{ + int16_t subframe_length; + int16_t subframe_idx; + ISM_METADATA_HANDLE hIsmMetaData[1]; + int16_t lfe_idx; + int16_t num_src; + /* TODO tmu : pass down renderer config struct */ + // float reverb_signal[BINAURAL_CHANNELS][L_FRAME48k]; + IVAS_FORMAT ivas_format; + IVAS_REND_AudioConfigType inConfigType; + + wmops_sub_start( "ivas_rend_TDObjRenderFrame" ); + + inConfigType = getAudioConfigType( inConfig ); + lfe_idx = LFE_CHANNEL; + if ( inConfigType == IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED ) + { + ivas_format = MC_FORMAT; + if ( inConfig != IVAS_REND_AUDIO_CONFIG_LS_CUSTOM ) + { + getAudioConfigNumChannels( inConfig, &num_src ); + } + else + { + lfe_idx = ( customLsInput->num_lfe > 0 ) ? customLsInput->lfe_idx[0] : -1; + num_src = customLsInput->num_spk + customLsInput->num_lfe; + } + } + else + { + ivas_format = ISM_FORMAT; + num_src = 1; + hIsmMetaData[0] = count_malloc( sizeof( ISM_METADATA_FRAME ) ); + hIsmMetaData[0]->azimuth = currentPos->azimuth; + hIsmMetaData[0]->elevation = currentPos->elevation; + } + + subframe_length = output_frame / MAX_PARAM_SPATIAL_SUBFRAMES; + + /* TODO tmu : pass down renderer config struct and decide what to do about ini_frame ? */ + // if ( hRenderConfig != NULL ) /* Renderer Configuration not enabled in TD standalone renderer */ + // { + // if ( hRenderConfig->roomAcoustics.late_reverb_on && ( st_ivas->ini_frame == 0 ) ) + // { + // ivas_reverb_open( &pTDRend->hCrend->hReverb, pTDRend->transport_config, NULL, pTDRend->hRenderConfig, pTDRend->hDecoderConfig->output_Fs ); + // } + // } + + /* Update object position(s) */ + TDREND_Update_object_positions( pTDRend->hBinRendererTd, num_src, lfe_idx, ivas_format, hIsmMetaData, output ); + + /* TODO tmu : needs a refactor / better approach */ + if ( ivas_format == ISM_FORMAT ) + { + count_free( hIsmMetaData[0] ); + } + + for ( subframe_idx = 0; subframe_idx < MAX_PARAM_SPATIAL_SUBFRAMES; subframe_idx++ ) + { + /* Update the listener's location/orientation */ + TDREND_Update_listener_orientation( pTDRend->hBinRendererTd, headRotData->headRotEnabled, ( headRotData != NULL ) ? &headRotData->headPositions[subframe_idx] : NULL ); + + /* TODO tmu : pass down renderer config struct */ + // if ( ( hRenderConfig != NULL ) && ( hRenderConfig->roomAcoustics.late_reverb_on ) ) + // { + // ivas_reverb_process( hCrend->hReverb, transport_config, 0, output, reverb_signal, subframe_idx ); + // } + + /* Render subframe */ + TDREND_GetMix( pTDRend->hBinRendererTd, output, subframe_length, output_Fs, subframe_idx ); + } + + /* TODO tmu : pass down renderer config struct */ + // if ( pTDRend->hRenderConfig != NULL ) /* Renderer Configuration not enabled in TD standalone renderer */ + // { + // if ( pTDRend->hRenderConfig->roomAcoustics.late_reverb_on ) + // { + // /* add reverb to rendered signals */ + // v_add( reverb_signal[0], output[0], output[0], output_frame ); + // v_add( reverb_signal[1], output[1], output[1], output_frame ); + // } + // } + + wmops_sub_end(); + + return IVAS_ERR_OK; +} +#endif diff --git a/lib_dec/ivas_objectRenderer_hrFilt.c b/lib_rend/ivas_objectRenderer_hrFilt.c similarity index 99% rename from lib_dec/ivas_objectRenderer_hrFilt.c rename to lib_rend/ivas_objectRenderer_hrFilt.c index d4acda02571e3a8a3e06b5d96fad2f8199717aeb..016d5a60f989e3ed52bfff769025b4762fdd55a1 100644 --- a/lib_dec/ivas_objectRenderer_hrFilt.c +++ b/lib_rend/ivas_objectRenderer_hrFilt.c @@ -36,7 +36,7 @@ #include "prot.h" #include #include "ivas_prot.h" -#include "ivas_rom_dec.h" +#include "ivas_rom_rend.h" #include "ivas_cnst.h" #include "ivas_rom_TdBinauralRenderer.h" #include "wmops.h" diff --git a/lib_dec/ivas_objectRenderer_mix.c b/lib_rend/ivas_objectRenderer_mix.c similarity index 100% rename from lib_dec/ivas_objectRenderer_mix.c rename to lib_rend/ivas_objectRenderer_mix.c diff --git a/lib_dec/ivas_objectRenderer_sfx.c b/lib_rend/ivas_objectRenderer_sfx.c similarity index 99% rename from lib_dec/ivas_objectRenderer_sfx.c rename to lib_rend/ivas_objectRenderer_sfx.c index 693180c48a7792d7939e7d7898383ea554a1b3ea..59c37eb6714b728e59137dd69c01fd40f56c9323 100644 --- a/lib_dec/ivas_objectRenderer_sfx.c +++ b/lib_rend/ivas_objectRenderer_sfx.c @@ -34,7 +34,7 @@ #include "options.h" #include #include "ivas_prot.h" -#include "ivas_rom_dec.h" +#include "ivas_rom_rend.h" #include "prot.h" #include "wmops.h" @@ -1205,6 +1205,12 @@ ivas_error TDREND_SFX_SpatBin_Initialize( SfxSpatBin_p->TurningOnEffect = FALSE; /* Init during next SetParams-call */ SfxSpatBin_p->InitializeParams = TRUE; +#ifdef FIX_I81 + SfxSpatBin_p->LeftFilter_p = NULL; + SfxSpatBin_p->LeftFilterIncr_p = NULL; + SfxSpatBin_p->RightFilter_p = NULL; + SfxSpatBin_p->RightFilterIncr_p = NULL; +#endif /* Init MaxTargetTime and MaxBlockLength */ switch ( output_Fs ) diff --git a/lib_dec/ivas_objectRenderer_sources.c b/lib_rend/ivas_objectRenderer_sources.c similarity index 100% rename from lib_dec/ivas_objectRenderer_sources.c rename to lib_rend/ivas_objectRenderer_sources.c diff --git a/lib_dec/ivas_objectRenderer_vec.c b/lib_rend/ivas_objectRenderer_vec.c similarity index 100% rename from lib_dec/ivas_objectRenderer_vec.c rename to lib_rend/ivas_objectRenderer_vec.c diff --git a/lib_dec/ivas_orient_trk.c b/lib_rend/ivas_orient_trk.c similarity index 100% rename from lib_dec/ivas_orient_trk.c rename to lib_rend/ivas_orient_trk.c diff --git a/lib_dec/ivas_output_init.c b/lib_rend/ivas_output_init.c similarity index 99% rename from lib_dec/ivas_output_init.c rename to lib_rend/ivas_output_init.c index 9486ee7d05f38790272b2f7a7d903335b312f040..e5407abff4320d6dab71106886fbf797b74f217a 100644 --- a/lib_dec/ivas_output_init.c +++ b/lib_rend/ivas_output_init.c @@ -29,7 +29,7 @@ the United Nations Convention on Contracts on the International Sales of Goods. *******************************************************************************************************/ - +// VE2AT: keep in lib_rend or move to lib_dec ? #include #include #include "options.h" diff --git a/lib_dec/ivas_render_config.c b/lib_rend/ivas_render_config.c similarity index 99% rename from lib_dec/ivas_render_config.c rename to lib_rend/ivas_render_config.c index d03ce0fa75c0af967be2e73477b83303032a20ba..7e8caec0923edf621f999fddf75cc29a954c049c 100644 --- a/lib_dec/ivas_render_config.c +++ b/lib_rend/ivas_render_config.c @@ -34,7 +34,7 @@ #include "options.h" #include "prot.h" #include "ivas_prot.h" -#include "ivas_rom_dec.h" +#include "ivas_rom_rend.h" #include "ivas_rom_TdBinauralRenderer.h" #ifdef DEBUGGING #include "debug.h" diff --git a/lib_dec/ivas_reverb.c b/lib_rend/ivas_reverb.c similarity index 99% rename from lib_dec/ivas_reverb.c rename to lib_rend/ivas_reverb.c index c18d3940e6cacfd52da140bc1fbdab18840bdd27..22265c03c13eff76eb90dc40f570670a0c10b174 100644 --- a/lib_dec/ivas_reverb.c +++ b/lib_rend/ivas_reverb.c @@ -39,7 +39,7 @@ #include "debug.h" #endif #include "math.h" -#include "ivas_rom_dec.h" +#include "ivas_rom_rend.h" #include #include "wmops.h" diff --git a/lib_dec/ivas_reverb_delay_line.c b/lib_rend/ivas_reverb_delay_line.c similarity index 100% rename from lib_dec/ivas_reverb_delay_line.c rename to lib_rend/ivas_reverb_delay_line.c diff --git a/lib_dec/ivas_reverb_fft_filter.c b/lib_rend/ivas_reverb_fft_filter.c similarity index 100% rename from lib_dec/ivas_reverb_fft_filter.c rename to lib_rend/ivas_reverb_fft_filter.c diff --git a/lib_dec/ivas_reverb_filter_design.c b/lib_rend/ivas_reverb_filter_design.c similarity index 100% rename from lib_dec/ivas_reverb_filter_design.c rename to lib_rend/ivas_reverb_filter_design.c diff --git a/lib_dec/ivas_reverb_iir_filter.c b/lib_rend/ivas_reverb_iir_filter.c similarity index 100% rename from lib_dec/ivas_reverb_iir_filter.c rename to lib_rend/ivas_reverb_iir_filter.c diff --git a/lib_dec/ivas_reverb_utils.c b/lib_rend/ivas_reverb_utils.c similarity index 99% rename from lib_dec/ivas_reverb_utils.c rename to lib_rend/ivas_reverb_utils.c index cbc62ab542ac1db6531f1d4aec25310e6ba78f50..31a559bd2c001cc138578b42fcbfdce842bd296d 100644 --- a/lib_dec/ivas_reverb_utils.c +++ b/lib_rend/ivas_reverb_utils.c @@ -35,7 +35,7 @@ #include "prot.h" #include "ivas_prot.h" #include "ivas_rom_binauralRenderer.h" -#include "ivas_rom_dec.h" +#include "ivas_rom_rend.h" #include #ifdef DEBUGGING #include "debug.h" diff --git a/lib_dec/ivas_rom_TdBinauralRenderer.c b/lib_rend/ivas_rom_TdBinauralRenderer.c similarity index 100% rename from lib_dec/ivas_rom_TdBinauralRenderer.c rename to lib_rend/ivas_rom_TdBinauralRenderer.c diff --git a/lib_dec/ivas_rom_TdBinauralRenderer.h b/lib_rend/ivas_rom_TdBinauralRenderer.h similarity index 100% rename from lib_dec/ivas_rom_TdBinauralRenderer.h rename to lib_rend/ivas_rom_TdBinauralRenderer.h diff --git a/lib_dec/ivas_rom_binauralRenderer.c b/lib_rend/ivas_rom_binauralRenderer.c similarity index 100% rename from lib_dec/ivas_rom_binauralRenderer.c rename to lib_rend/ivas_rom_binauralRenderer.c diff --git a/lib_dec/ivas_rom_binauralRenderer.h b/lib_rend/ivas_rom_binauralRenderer.h similarity index 100% rename from lib_dec/ivas_rom_binauralRenderer.h rename to lib_rend/ivas_rom_binauralRenderer.h diff --git a/lib_dec/ivas_rom_binaural_crend_head.c b/lib_rend/ivas_rom_binaural_crend_head.c similarity index 100% rename from lib_dec/ivas_rom_binaural_crend_head.c rename to lib_rend/ivas_rom_binaural_crend_head.c diff --git a/lib_dec/ivas_rom_binaural_crend_head.h b/lib_rend/ivas_rom_binaural_crend_head.h similarity index 100% rename from lib_dec/ivas_rom_binaural_crend_head.h rename to lib_rend/ivas_rom_binaural_crend_head.h diff --git a/lib_rend/ivas_rom_rend.c b/lib_rend/ivas_rom_rend.c new file mode 100644 index 0000000000000000000000000000000000000000..100dded45933f19b30aba85b6a3c908887c2df52 --- /dev/null +++ b/lib_rend/ivas_rom_rend.c @@ -0,0 +1,799 @@ +/****************************************************************************************************** + + (C) 2022 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository. All Rights Reserved. + + This software is protected by copyright law and by international treaties. + The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository retain full ownership rights in their respective contributions in + the software. This notice grants no license of any kind, including but not limited to patent + license, nor is any license granted by implication, estoppel or otherwise. + + Contributors are required to enter into the IVAS codec Public Collaboration agreement before making + contributions. + + This software is provided "AS IS", without any express or implied warranties. The software is in the + development stage. It is intended exclusively for experts who have experience with such software and + solely for the purpose of inspection. All implied warranties of non-infringement, merchantability + and fitness for a particular purpose are hereby disclaimed and excluded. + + Any dispute, controversy or claim arising under or in relation to providing this software shall be + submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in + accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and + the United Nations Convention on Contracts on the International Sales of Goods. + +*******************************************************************************************************/ + +#include "options.h" +#include +#ifdef DEBUGGING +#include "debug.h" +#endif +#include "ivas_rom_rend.h" + +/* clang-format off */ + +/*----------------------------------------------------------------------------------* + * FASTCONV and PARAMETRIC binaural renderer ROM tables + *----------------------------------------------------------------------------------*/ + +const float dmxmtx[BINAURAL_CHANNELS][11] = +{ + { 1.0f, 0.0f, 0.70709997f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f }, + { 0.0f, 1.0f, 0.70709997f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f }, +}; + +/* +* 0 = 30,0 +* 1 = -30,0 +* 2 = 0,0 +* 3 = 135,0 +* 4 = -135,0 +* 5 = 110,0 +* 6 = -110,0 +* 7 = 90,0 +* 8 = -90,0 +* 9 = 30,35 +* 10 = -30,35 +* 11 = 110,35 +* 12 = -110,35 +* 13 = 135, 35 +* 14 = -135, 35 +*/ +const int16_t channelIndex_CICP6[5] = { 0, 1, 2, 5, 6 }; +const int16_t channelIndex_CICP12[7] = { 0, 1, 2, 5, 6, 3, 4 }; +const int16_t channelIndex_CICP14[7] = { 0, 1, 2, 5, 6, 9, 10 }; +const int16_t channelIndex_CICP16[9] = { 0, 1, 2, 5, 6, 9, 10, 11, 12 }; +const int16_t channelIndex_CICP19[11] = { 0, 1, 2, 3, 4, 7, 8, 9, 10, 13, 14 }; + +/*----------------------------------------------------------------------------------* + * TD ISm binaural renderer ROM tables + *----------------------------------------------------------------------------------*/ + + /* The maximum target times set to 100 msec. */ +const int16_t TDREND_SRC_REND_MaxTargetTimes[IVAS_NUM_SUPPORTED_FS] = +{ + 1600, 3200, 4800 /* Corresponds to 16kHz, 32kHz, 48kHz */ +}; + +/* The maximum lengths of the blocks internally in the effect. Corresponds to 6 msec. This means also that */ +/* if the length of the input block is just above 6 msec, the block will be divided into two 3 msec blocks. */ +const int16_t TDREND_SRC_REND_MaxBlockLengths[IVAS_NUM_SUPPORTED_FS] = +{ + 96, 192, 288 /* Corresponds to 16kHz, 32kHz, 48kHz */ +}; + +const int16_t TDREND_MaxITD[IVAS_NUM_SUPPORTED_FS] = +{ + 111, 222, 333 /* Corresponds to 16kHz, 32kHz, 48kHz */ +}; + +const float TDREND_MaxITD_Incr[IVAS_NUM_SUPPORTED_FS] = +{ + 0.0925f, 0.1850f, 0.2775f /* Corresponds to 16kHz, 32kHz, 48kHz, e.g. ( ( 2 * MaxITD ) / ( 0.05 * 48000 ) ) */ +}; + +const int16_t HRTF_MODEL_N_CPTS_VAR[HRTF_MODEL_N_SECTIONS] = +{ + 13, 12, 11 +}; + +const float SincTable[321] = +{ + 1.00000000f, 0.99957629f, 0.99830587f, 0.99619078f, 0.99323448f, 0.98944177f, 0.98481881f, 0.97937311f, + 0.97311350f, 0.96605012f, 0.95819441f, 0.94955907f, 0.94015803f, 0.93000645f, 0.91912066f, 0.90751815f, + 0.89521750f, 0.88223838f, 0.86860150f, 0.85432856f, 0.83944219f, 0.82396595f, 0.80792425f, 0.79134231f, + 0.77424608f, 0.75666226f, 0.73861817f, 0.72014174f, 0.70126144f, 0.68200624f, 0.66240553f, 0.64248906f, + 0.62228691f, 0.60182943f, 0.58114713f, 0.56027070f, 0.53923087f, 0.51805843f, 0.49678411f, 0.47543856f, + 0.45405225f, 0.43265547f, 0.41127824f, 0.38995024f, 0.36870081f, 0.34755883f, 0.32655271f, 0.30571035f, + 0.28505905f, 0.26462549f, 0.24443569f, 0.22451493f, 0.20488776f, 0.18557791f, 0.16660829f, 0.14800093f, + 0.12977695f, 0.11195656f, 0.09455895f, 0.07760236f, 0.06110400f, 0.04508003f, 0.02954554f, 0.01451456f, + 0.00000000f, -0.01398631f, -0.02743368f, -0.04033255f, -0.05267447f, -0.06445214f, -0.07565940f, -0.08629121f, + -0.09634367f, -0.10581400f, -0.11470052f, -0.12300268f, -0.13072098f, -0.13785702f, -0.14441345f, -0.15039394f, + -0.15580318f, -0.16064685f, -0.16493160f, -0.16866498f, -0.17185547f, -0.17451243f, -0.17664604f, -0.17826729f, + -0.17938796f, -0.18002054f, -0.18017822f, -0.17987486f, -0.17912493f, -0.17794347f, -0.17634608f, -0.17434883f, + -0.17196824f, -0.16922125f, -0.16612516f, -0.16269761f, -0.15895648f, -0.15491992f, -0.15060625f, -0.14603396f, + -0.14122162f, -0.13618787f, -0.13095139f, -0.12553081f, -0.11994473f, -0.11421163f, -0.10834984f, -0.10237755f, + -0.09631271f, -0.09017300f, -0.08397586f, -0.07773838f, -0.07147731f, -0.06520902f, -0.05894946f, -0.05271415f, + -0.04651815f, -0.04037601f, -0.03430179f, -0.02830902f, -0.02241063f, -0.01661904f, -0.01094605f, -0.00540284f, + -0.00000000f, 0.00525251f, 0.01034538f, 0.01526993f, 0.02001814f, 0.02458266f, 0.02895676f, 0.03313441f, + 0.03711021f, 0.04087943f, 0.04443799f, 0.04778246f, 0.05091003f, 0.05381856f, 0.05650650f, 0.05897292f, + 0.06121749f, 0.06324047f, 0.06504268f, 0.06662549f, 0.06799083f, 0.06914112f, 0.07007930f, 0.07080878f, + 0.07133343f, 0.07165755f, 0.07178588f, 0.07172352f, 0.07147595f, 0.07104902f, 0.07044886f, 0.06968193f, + 0.06875494f, 0.06767485f, 0.06644886f, 0.06508435f, 0.06358888f, 0.06197015f, 0.06023599f, 0.05839432f, + 0.05645314f, 0.05442051f, 0.05230450f, 0.05011320f, 0.04785466f, 0.04553692f, 0.04316793f, 0.04075558f, + 0.03830765f, 0.03583181f, 0.03333557f, 0.03082630f, 0.02831121f, 0.02579730f, 0.02329137f, 0.02080003f, + 0.01832963f, 0.01588629f, 0.01347589f, 0.01110403f, 0.00877607f, 0.00649705f, 0.00427175f, 0.00210467f, + 0.00000000f, -0.00203837f, -0.00400686f, -0.00590216f, -0.00772131f, -0.00946162f, -0.01112072f, -0.01269654f, + -0.01418731f, -0.01559156f, -0.01690810f, -0.01813605f, -0.01927478f, -0.02032396f, -0.02128352f, -0.02215366f, + -0.02293482f, -0.02362769f, -0.02423318f, -0.02475245f, -0.02518686f, -0.02553797f, -0.02580754f, -0.02599752f, + -0.02611000f, -0.02614728f, -0.02611175f, -0.02600597f, -0.02583262f, -0.02559449f, -0.02529446f, -0.02493550f, + -0.02452066f, -0.02405306f, -0.02353586f, -0.02297226f, -0.02236549f, -0.02171881f, -0.02103547f, -0.02031874f, + -0.01957185f, -0.01879802f, -0.01800043f, -0.01718225f, -0.01634655f, -0.01549638f, -0.01463471f, -0.01376443f, + -0.01288838f, -0.01200928f, -0.01112977f, -0.01025241f, -0.00937962f, -0.00851376f, -0.00765705f, -0.00681160f, + -0.00597942f, -0.00516238f, -0.00436225f, -0.00358068f, -0.00281917f, -0.00207914f, -0.00136185f, -0.00066846f, + -0.00000000f, 0.00064260f, 0.00125856f, 0.00184718f, 0.00240790f, 0.00294026f, 0.00344390f, 0.00391857f, + 0.00436413f, 0.00478051f, 0.00516776f, 0.00552600f, 0.00585544f, 0.00615637f, 0.00642915f, 0.00667420f, + 0.00689203f, 0.00708318f, 0.00724827f, 0.00738795f, 0.00750293f, 0.00759395f, 0.00766178f, 0.00770723f, + 0.00773114f, 0.00773435f, 0.00771774f, 0.00768218f, 0.00762857f, 0.00755779f, 0.00747075f, 0.00736831f, + 0.00725138f, 0.00712082f, 0.00697748f, 0.00682221f, 0.00665584f, 0.00647916f, 0.00629295f, 0.00609797f, + 0.00589494f, 0.00568458f, 0.00546754f, 0.00524448f, 0.00501600f, 0.00478270f, 0.00454511f, 0.00430377f, + 0.00405916f, 0.00381176f, 0.00356198f, 0.00331023f, 0.00305690f, 0.00280234f, 0.00254687f, 0.00229079f, + 0.00203440f, 0.00177795f, 0.00152168f, 0.00126584f, 0.00101062f, 0.00075625f, 0.00050289f, 0.00025075f, + 0.00000000f +}; + +const float orange53_left_avg_power[257] = { + 0.999231100f, 0.992580175f, 0.969233215f, 0.925614893f, 0.871408045f, 0.826101780f, 0.803222895f, 0.800087631f, 0.802672029f, + 0.801490188f, 0.796555817f, 0.790879488f, 0.784882724f, 0.777585745f, 0.769326210f, 0.761789441f, 0.756145239f, 0.752754092f, + 0.751703024f, 0.752594173f, 0.754317880f, 0.755515277f, 0.754378498f, 0.748860359f, 0.738919020f, 0.727488697f, 0.718792558f, + 0.714865267f, 0.713446736f, 0.711076498f, 0.706021905f, 0.697553098f, 0.684623063f, 0.667031527f, 0.647006035f, 0.627680719f, + 0.609939933f, 0.592472672f, 0.574803054f, 0.558499217f, 0.544599831f, 0.532128096f, 0.520152628f, 0.509682238f, 0.501904130f, + 0.496162385f, 0.491121918f, 0.486813396f, 0.483951330f, 0.482198298f, 0.480713189f, 0.479654074f, 0.479590476f, 0.479965866f, + 0.479589254f, 0.478181243f, 0.476334095f, 0.474199444f, 0.471616089f, 0.469089746f, 0.467486322f, 0.466943622f, 0.467153549f, + 0.468381166f, 0.470996737f, 0.474416614f, 0.477639019f, 0.480612457f, 0.483910263f, 0.487287015f, 0.489909321f, 0.491668850f, + 0.493155539f, 0.494319856f, 0.494512051f, 0.493615031f, 0.492155492f, 0.490116775f, 0.486886710f, 0.482303619f, 0.476902038f, + 0.470775038f, 0.463377595f, 0.454571068f, 0.445130944f, 0.435581058f, 0.425568998f, 0.414717495f, 0.403531373f, 0.392556936f, + 0.381436378f, 0.369506508f, 0.357099295f, 0.345049500f, 0.333368897f, 0.321326375f, 0.308959186f, 0.297232091f, 0.286592871f, + 0.276453108f, 0.266589880f, 0.257950366f, 0.251341701f, 0.246435612f, 0.242861211f, 0.241405189f, 0.242839754f, 0.246688128f, + 0.252115428f, 0.259297341f, 0.268399984f, 0.278481483f, 0.288520366f, 0.298599035f, 0.308846802f, 0.318350822f, 0.326248646f, + 0.332813978f, 0.338464528f, 0.342543274f, 0.344278336f, 0.344031811f, 0.342641503f, 0.339995682f, 0.335437506f, 0.329174429f, + 0.322237372f, 0.315035462f, 0.306967229f, 0.297821850f, 0.288482070f, 0.279766560f, 0.271234214f, 0.262228251f, 0.253214896f, + 0.245183259f, 0.237939596f, 0.230546176f, 0.223051578f, 0.216552779f, 0.211263061f, 0.206180066f, 0.200917527f, 0.196485907f, + 0.193453044f, 0.190857053f, 0.187853232f, 0.185171053f, 0.183685005f, 0.182665780f, 0.180928215f, 0.178784713f, 0.177342966f, + 0.176323384f, 0.174430951f, 0.171496049f, 0.168740034f, 0.166518897f, 0.163711995f, 0.159658119f, 0.155442193f, 0.152056932f, + 0.148795277f, 0.144545168f, 0.139905334f, 0.136263832f, 0.133493021f, 0.130194828f, 0.126240104f, 0.123071767f, 0.121281922f, + 0.119557180f, 0.117016964f, 0.114773229f, 0.114072219f, 0.114103459f, 0.113414355f, 0.112460621f, 0.112842396f, 0.114564091f, + 0.115944758f, 0.116569765f, 0.117913686f, 0.120910525f, 0.124211200f, 0.126575813f, 0.128826424f, 0.132578567f, 0.137430578f, + 0.141675219f, 0.144987956f, 0.148879051f, 0.154273912f, 0.159992099f, 0.164641231f, 0.168560207f, 0.173201621f, 0.178906262f, + 0.184429348f, 0.188756809f, 0.192309171f, 0.196154252f, 0.200732291f, 0.205381230f, 0.209404662f, 0.212832779f, 0.216197237f, + 0.220162451f, 0.225029215f, 0.230637416f, 0.236752108f, 0.243243530f, 0.249900997f, 0.256293535f, 0.261716694f, 0.265186161f, + 0.265652657f, 0.262010813f, 0.253508776f, 0.243198514f, 0.244490802f, 0.255167097f, 0.258825988f, 0.257396817f, 0.256197631f, + 0.256865948f, 0.258354962f, 0.259370565f, 0.259730458f, 0.259894609f, 0.260285556f, 0.260970831f, 0.261650831f, 0.262020200f, + 0.262095064f, 0.262225062f, 0.262741268f, 0.263585031f, 0.264350951f, 0.264654577f, 0.264539272f, 0.264409125f, 0.264633715f, + 0.265172601f, 0.265621960f, 0.265678704f, 0.265469313f, 0.265454412f, 0.265907466f, 0.266625792f, 0.267101586f, 0.266997635f, + 0.266522497f, 0.266185820f, 0.266298562f, 0.266692907f, 0.266907692f +}; + +const float orange53_right_avg_power[257] = { + 0.999231100f, 0.992580175f, 0.969233215f, 0.925614893f, 0.871408045f, 0.826101780f, 0.803222895f, 0.800087631f, 0.802672029f, + 0.801490188f, 0.796555817f, 0.790879488f, 0.784882724f, 0.777585745f, 0.769326210f, 0.761789441f, 0.756145239f, 0.752754092f, + 0.751703024f, 0.752594173f, 0.754317880f, 0.755515277f, 0.754378498f, 0.748860359f, 0.738919020f, 0.727488697f, 0.718792558f, + 0.714865267f, 0.713446736f, 0.711076498f, 0.706021905f, 0.697553098f, 0.684623063f, 0.667031527f, 0.647006035f, 0.627680719f, + 0.609939933f, 0.592472672f, 0.574803054f, 0.558499217f, 0.544599831f, 0.532128096f, 0.520152628f, 0.509682238f, 0.501904130f, + 0.496162385f, 0.491121918f, 0.486813396f, 0.483951330f, 0.482198298f, 0.480713189f, 0.479654074f, 0.479590476f, 0.479965866f, + 0.479589254f, 0.478181243f, 0.476334095f, 0.474199444f, 0.471616089f, 0.469089746f, 0.467486322f, 0.466943622f, 0.467153549f, + 0.468381166f, 0.470996737f, 0.474416614f, 0.477639019f, 0.480612457f, 0.483910263f, 0.487287015f, 0.489909321f, 0.491668850f, + 0.493155539f, 0.494319856f, 0.494512051f, 0.493615031f, 0.492155492f, 0.490116775f, 0.486886710f, 0.482303619f, 0.476902038f, + 0.470775038f, 0.463377595f, 0.454571068f, 0.445130944f, 0.435581058f, 0.425568998f, 0.414717495f, 0.403531373f, 0.392556936f, + 0.381436378f, 0.369506508f, 0.357099295f, 0.345049500f, 0.333368897f, 0.321326375f, 0.308959186f, 0.297232091f, 0.286592871f, + 0.276453108f, 0.266589880f, 0.257950366f, 0.251341701f, 0.246435612f, 0.242861211f, 0.241405189f, 0.242839754f, 0.246688128f, + 0.252115428f, 0.259297341f, 0.268399984f, 0.278481483f, 0.288520366f, 0.298599035f, 0.308846802f, 0.318350822f, 0.326248646f, + 0.332813978f, 0.338464528f, 0.342543274f, 0.344278336f, 0.344031811f, 0.342641503f, 0.339995682f, 0.335437506f, 0.329174429f, + 0.322237372f, 0.315035462f, 0.306967229f, 0.297821850f, 0.288482070f, 0.279766560f, 0.271234214f, 0.262228251f, 0.253214896f, + 0.245183259f, 0.237939596f, 0.230546176f, 0.223051578f, 0.216552779f, 0.211263061f, 0.206180066f, 0.200917527f, 0.196485907f, + 0.193453044f, 0.190857053f, 0.187853232f, 0.185171053f, 0.183685005f, 0.182665780f, 0.180928215f, 0.178784713f, 0.177342966f, + 0.176323384f, 0.174430951f, 0.171496049f, 0.168740034f, 0.166518897f, 0.163711995f, 0.159658119f, 0.155442193f, 0.152056932f, + 0.148795277f, 0.144545168f, 0.139905334f, 0.136263832f, 0.133493021f, 0.130194828f, 0.126240104f, 0.123071767f, 0.121281922f, + 0.119557180f, 0.117016964f, 0.114773229f, 0.114072219f, 0.114103459f, 0.113414355f, 0.112460621f, 0.112842396f, 0.114564091f, + 0.115944758f, 0.116569765f, 0.117913686f, 0.120910525f, 0.124211200f, 0.126575813f, 0.128826424f, 0.132578567f, 0.137430578f, + 0.141675219f, 0.144987956f, 0.148879051f, 0.154273912f, 0.159992099f, 0.164641231f, 0.168560207f, 0.173201621f, 0.178906262f, + 0.184429348f, 0.188756809f, 0.192309171f, 0.196154252f, 0.200732291f, 0.205381230f, 0.209404662f, 0.212832779f, 0.216197237f, + 0.220162451f, 0.225029215f, 0.230637416f, 0.236752108f, 0.243243530f, 0.249900997f, 0.256293535f, 0.261716694f, 0.265186161f, + 0.265652657f, 0.262010813f, 0.253508776f, 0.243198514f, 0.244490802f, 0.255167097f, 0.258825988f, 0.257396817f, 0.256197631f, + 0.256865948f, 0.258354962f, 0.259370565f, 0.259730458f, 0.259894609f, 0.260285556f, 0.260970831f, 0.261650831f, 0.262020200f, + 0.262095064f, 0.262225062f, 0.262741268f, 0.263585031f, 0.264350951f, 0.264654577f, 0.264539272f, 0.264409125f, 0.264633715f, + 0.265172601f, 0.265621960f, 0.265678704f, 0.265469313f, 0.265454412f, 0.265907466f, 0.266625792f, 0.267101586f, 0.266997635f, + 0.266522497f, 0.266185820f, 0.266298562f, 0.266692907f, 0.266907692f +}; + +const float orange53_coherence[257] = { + 0.929530263f, 0.921171963f, 0.900268972f, 0.876067519f, 0.855227590f, 0.837884128f, 0.823401272f, 0.818804145f, 0.835025251f, + 0.871971071f, 0.911253273f, 0.929330528f, 0.921199203f, 0.900894165f, 0.882577479f, 0.867001534f, 0.849280477f, 0.832460761f, + 0.824062645f, 0.823441386f, 0.820908070f, 0.811902404f, 0.802339375f, 0.798648477f, 0.797345281f, 0.791158736f, 0.779512227f, + 0.768243194f, 0.760565042f, 0.754912853f, 0.751044095f, 0.752276063f, 0.759258866f, 0.766927004f, 0.769716740f, 0.767338514f, + 0.763358235f, 0.759508014f, 0.755201221f, 0.750362694f, 0.746060252f, 0.742611766f, 0.739434779f, 0.736354828f, 0.733443379f, + 0.730109870f, 0.726028502f, 0.722365141f, 0.720153689f, 0.718220115f, 0.714793265f, 0.710619092f, 0.708084404f, 0.707218647f, + 0.705624878f, 0.702472746f, 0.700073540f, 0.699947894f, 0.700519860f, 0.699934483f, 0.699344158f, 0.700895131f, 0.704551995f, + 0.708814025f, 0.713567019f, 0.719995975f, 0.728467822f, 0.738399088f, 0.749545693f, 0.761859894f, 0.774593413f, 0.787218869f, + 0.800481200f, 0.814727187f, 0.828367889f, 0.839860320f, 0.850490928f, 0.862034321f, 0.873037636f, 0.880097568f, 0.883217216f, + 0.885473788f, 0.887664974f, 0.886511028f, 0.880120754f, 0.871120989f, 0.862524390f, 0.853262126f, 0.840783834f, 0.825854301f, + 0.811407208f, 0.798167706f, 0.784307659f, 0.769172490f, 0.754072189f, 0.739893615f, 0.726129174f, 0.712544501f, 0.699519753f, + 0.686980069f, 0.674778104f, 0.663931608f, 0.655511260f, 0.648816824f, 0.642671287f, 0.638217211f, 0.637585819f, 0.640332758f, + 0.643755615f, 0.647433281f, 0.653589368f, 0.662824631f, 0.672268033f, 0.680022597f, 0.687623680f, 0.696763635f, 0.705829978f, + 0.712574661f, 0.717432320f, 0.721986175f, 0.725707173f, 0.727064371f, 0.726255059f, 0.724350274f, 0.720927835f, 0.715189219f, + 0.708206475f, 0.701428175f, 0.693923056f, 0.684313059f, 0.674107075f, 0.666009307f, 0.659245491f, 0.650998116f, 0.641600072f, + 0.634524226f, 0.630267978f, 0.625348687f, 0.618164837f, 0.611785769f, 0.608430445f, 0.605561733f, 0.600407422f, 0.594782710f, + 0.591767371f, 0.590365708f, 0.587845862f, 0.584915996f, 0.584355533f, 0.585834682f, 0.586913347f, 0.587935925f, 0.591403484f, + 0.596784472f, 0.601111054f, 0.604539037f, 0.610374093f, 0.618451059f, 0.624519289f, 0.627448440f, 0.631859899f, 0.639748096f, + 0.646256745f, 0.647378445f, 0.647664309f, 0.652599990f, 0.659044445f, 0.659743190f, 0.656243205f, 0.656651020f, 0.662200928f, + 0.664544880f, 0.660030127f, 0.656303048f, 0.659881413f, 0.664978266f, 0.662953973f, 0.657274961f, 0.658065319f, 0.665406108f, + 0.668446958f, 0.663809955f, 0.661349833f, 0.668595374f, 0.677367866f, 0.677208483f, 0.672289610f, 0.675831020f, 0.688208520f, + 0.695776582f, 0.691749871f, 0.687812865f, 0.696674168f, 0.711764693f, 0.716045380f, 0.706839681f, 0.701565385f, 0.711955190f, + 0.726487696f, 0.723370016f, 0.700417101f, 0.677427649f, 0.670733511f, 0.671355724f, 0.654210806f, 0.608316183f, 0.549225986f, + 0.504217446f, 0.484227657f, 0.475346446f, 0.452598959f, 0.399407327f, 0.319485664f, 0.229244962f, 0.146649837f, 0.083417825f, + 0.041744832f, 0.018142883f, 0.006854009f, 0.002511850f, 0.001177550f, 0.000840970f, 0.000701097f, 0.000571384f, 0.000458581f, + 0.000376965f, 0.000320562f, 0.000278847f, 0.000245546f, 0.000218281f, 0.000195632f, 0.000176647f, 0.000160827f, 0.000147978f, + 0.000137649f, 0.000129066f, 0.000121431f, 0.000114406f, 0.000108067f, 0.000102595f, 0.000097917f, 0.000093750f, 0.000089854f, + 0.000086255f, 0.000083183f, 0.000080804f, 0.000079026f, 0.000077552f, 0.000076117f, 0.000074693f, 0.000073431f, 0.000072456f, + 0.000071701f, 0.000071002f, 0.000070286f, 0.000069692f, 0.000069457f +}; + +/*----------------------------------------------------------------------------------* + * t-design and SN3D normalization table + *----------------------------------------------------------------------------------*/ + + /* SN3D norm */ +const float norm_sn3d_hoa3[16] = +{ + 1.f, 1.7320508f, 1.7320508f, 1.7320508f, 2.2360680f, 2.2360680f, 2.2360680f, 2.2360680f, + 2.2360680f, 2.6457512f, 2.6457512f, 2.6457512f, 2.6457512f, 2.6457512f, 2.6457512f, 2.6457512f +}; + +/* Order 11 t-design */ +const uint16_t t_design_11_size = 70; + +const float t_design_11_azimuth[70] = +{ + 1.329273e+02f, -8.393495e+01f, 8.474100e+00f, -1.133408e+02f, -1.032659e+02f, -3.323704e+01f, 2.185643e+01f, -1.565395e+02f, + -6.426475e+01f, 1.657795e+02f, -2.520283e+01f, -9.700380e+01f, 2.785464e+01f, 1.532142e+02f, -1.550616e+02f, -1.184214e+01f, + 8.053873e+01f, -4.205616e+01f, -3.122333e+01f, 3.883790e+01f, 9.376069e+01f, -8.475602e+01f, 7.755368e+00f, -1.222769e+02f, + 4.680127e+01f, -2.476863e+01f, 9.989047e+01f, -1.347840e+02f, -8.308802e+01f, 6.012817e+01f, 1.526447e+02f, 2.975767e+01f, + 4.077932e+01f, 1.101839e+02f, 1.656521e+02f, -1.299266e+01f, 7.973599e+01f, -5.052453e+01f, 1.189239e+02f, 4.722029e+01f, + 1.719253e+02f, -6.251458e+01f, -1.111567e+01f, 1.320180e+02f, -1.353555e+02f, 1.023709e+02f, 1.127393e+02f, -1.783050e+02f, + -1.223199e+02f, 5.907635e+01f, 1.517042e+02f, 2.137634e+01f, -1.690055e+02f, 1.189808e+02f, -1.160893e+02f, 9.647679e+00f, + 6.089332e+01f, -1.560215e+02f, -6.346030e+01f, 1.749298e+02f, -1.752888e+02f, -1.059519e+02f, -5.019283e+01f, 1.313583e+02f, + -1.362968e+02f, 9.356446e+01f, -9.708401e+01f, -1.691583e+02f, -4.413238e+01f, 8.147954e+01f +}; + +const float t_design_11_elevation[70] = +{ + 7.692547e+00f, -2.373007e+01f, 2.351276e+01f, 7.042259e+01f, -9.896944e+00f, -7.075133e+01f, -2.646185e+01f, 4.777649e+01f, + -7.720470e+00f, 4.453436e+01f, 2.638979e+01f, -4.465789e+01f, 9.767035e+00f, -4.770533e+01f, 7.453029e+00f, -2.359012e+01f, + 2.371945e+01f, 7.043827e+01f, -9.835416e+00f, -7.049808e+01f, -2.629492e+01f, 4.761480e+01f, -7.517185e+00f, 4.428623e+01f, + 2.664426e+01f, -4.456937e+01f, 9.912719e+00f, -4.795996e+01f, 7.296799e+00f, -2.334460e+01f, 2.364153e+01f, 7.068431e+01f, + -9.581404e+00f, -7.039345e+01f, -2.642582e+01f, 4.775107e+01f, -7.308536e+00f, 4.426328e+01f, 2.671406e+01f, -4.431497e+01f, + 9.758997e+00f, -4.803619e+01f, 7.439651e+00f, -2.333261e+01f, 2.338690e+01f, 7.082191e+01f, -9.485964e+00f, -7.058019e+01f, + -2.667403e+01f, 4.799784e+01f, -7.382762e+00f, 4.449706e+01f, 2.650250e+01f, -4.424619e+01f, 9.518451e+00f, -4.782814e+01f, + 7.684274e+00f, -2.357068e+01f, 2.330745e+01f, 7.065865e+01f, -9.680889e+00f, -7.080268e+01f, -2.669635e+01f, 4.801363e+01f, + -7.637348e+00f, 4.466512e+01f, 2.630235e+01f, -4.445764e+01f, 9.523415e+00f, -4.762422e+01f +}; + + +/*----------------------------------------------------------------------* +* Reverberator ROM tables +*-----------------------------------------------------------------------*/ + +const float ivas_reverb_default_fc[IVAS_REVERB_DEFAULT_N_BANDS] = +{ + 20.0f, 25.0f, 31.5f, 40.0f, + 50.0f, 63.0f, 80.0f, 100.0f, + 125.0f, 160.0f, 200.0f, 250.0f, + 315.0f, 400.0f, 500.0f, 630.0f, + 800.0f, 1000.0f, 1250.0f, 1600.0f, + 2000.0f, 2500.0f, 3150.0f, 4000.0f, + 5000.0f, 6300.0f, 8000.0f, 10000.0f, + 12500.0f, 16000.0f, 20000.0f +}; + +const float ivas_reverb_default_RT60[IVAS_REVERB_DEFAULT_N_BANDS] = +{ + 1.3622f, 1.4486f, 1.3168f, 1.5787f, + 1.4766f, 1.3954f, 1.2889f, 1.3462f, + 1.0759f, 1.0401f, 1.097f, 1.085f, + 1.091f, 1.0404f, 1.0499f, 1.0699f, + 1.1028f, 1.1714f, 1.1027f, 1.0666f, + 1.055f, 1.0553f, 1.0521f, 1.0569f, + 1.0421f, 0.97822f, 0.80487f, 0.75944f, + 0.71945f, 0.61682f, 0.60031f +}; + +const float ivas_reverb_default_DSR[IVAS_REVERB_DEFAULT_N_BANDS] = +{ + 1.8811e-08f, 2.1428e-08f, 1.3972e-08f, 1.51e-08f, + 1.287e-08f, 1.8747e-08f, 2.413e-08f, 3.9927e-08f, + 8.9719e-08f, 1.902e-07f, 3.702e-07f, 6.1341e-07f, + 7.1432e-07f, 6.5331e-07f, 4.6094e-07f, 5.4683e-07f, + 7.0134e-07f, 6.856e-07f, 7.114e-07f, 6.9604e-07f, + 5.2939e-07f, 5.699e-07f, 6.1773e-07f, 5.7488e-07f, + 4.7748e-07f, 2.7213e-07f, 1.3681e-07f, 1.0941e-07f, + 6.2001e-08f, 2.8483e-08f, 2.6267e-08f +}; + +/*----------------------------------------------------------------------------------* + * Renderer SBA & MC enc/dec matrices + *----------------------------------------------------------------------------------*/ + +/* CICP1 - Mono */ +const float ls_azimuth_CICP1[1] = { 0.0f }; +const float ls_elevation_CICP1[1] = { 0.0f }; +const uint32_t ls_LFE_last_idx_CICP1[1] = { 0 }; + +/* CICP2 - Stereo */ +const uint32_t ls_LFE_last_idx_CICP2[2] = { 0, 1 }; + +/* CICP6 - 5.1 */ +const uint32_t ls_LFE_last_idx_CICP6[6] = { 0, 1, 2, 4, 5, 3 }; + +/* CICP12 - 7.1 */ +const uint32_t ls_LFE_last_idx_CICP12[8] = { 0, 1, 2, 4, 5, 6, 7, 3 }; + +/* CICP14 - 5.1.2 */ +const uint32_t ls_LFE_last_idx_CICP14[8] = { 0, 1, 2, 4, 5, 6, 7, 3 }; + +/* CICP16 - 5.1.4 */ +const uint32_t ls_LFE_last_idx_CICP16[10] = { 0, 1, 2, 4, 5, 6, 7, 8, 9, 3 }; + +/* CICP19 - 7.1.4 */ +const uint32_t ls_LFE_last_idx_CICP19[12] = { 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 3 }; + +/*----------------------------------------------------------------------------------* + * LS Renderer ROM tables + *----------------------------------------------------------------------------------*/ + + /* All matrices are stored with dimensions nchan_in x nchan_out */ + /* Downmix matrices */ +const float ls_conversion_cicpX_mono[12][1] = +{ +#ifdef EXT_RENDERER + {1.00000000f}, + {1.00000000f}, + {1.00000000f}, + {1.00000000f}, + {1.00000000f}, + {1.00000000f}, + {1.00000000f}, + {1.00000000f}, + {1.00000000f}, + {1.00000000f}, + {1.00000000f}, + {1.00000000f}, +#else + {1.00000000f}, + {1.00000000f}, + {0.70710677f}, + {0.70710677f}, + {0.79999995f}, + {0.79999995f}, + {0.79999995f}, + {0.79999995f}, + {0.849999964f}, + {0.849999964f}, + {0.849999964f}, + {0.849999964f} +#endif +}; + +const float ls_conversion_cicpX_stereo[12][2] = +{ + {1.00000000f, 0.00000000f}, + {0.00000000f, 1.00000000f}, + {0.70710677f, 0.70710677f}, + {0.70710677f, 0.70710677f}, + {0.79999995f, 0.00000000f}, + {0.00000000f, 0.79999995f}, + {0.79999995f, 0.00000000f}, + {0.00000000f, 0.79999995f}, + {0.849999964f, 0.000000000f}, + {0.000000000f, 0.849999964f}, + {0.849999964f, 0.000000000f}, + {0.000000000f, 0.849999964f} +}; + +const LS_CONVERSION_MATRIX ls_conversion_cicp12_cicp6[] = +{ +#ifdef EXT_RENDERER + /* First row indicates the number of non-zero elements and the number of matrix columns */ + {8, 6.0f}, +#else + /* First row indicates the number of non-zero elements */ + {8, 0.0f}, +#endif + /* Index of non-zero element, value of non-zero element*/ + {0, 1.000000000f}, + {7, 1.000000000f}, + {14, 1.000000000f}, + {21, 1.000000000f}, + {28, 1.000000000f}, + {35, 1.000000000f}, + {40, 1.000000000f}, + {47, 1.000000000f} +}; + +const LS_CONVERSION_MATRIX ls_conversion_cicp14_cicp6[] = +{ +#ifdef EXT_RENDERER + /* First row indicates the number of non-zero elements and the number of matrix columns */ + {8, 6.0f}, +#else + /* First row indicates the number of non-zero elements */ + {8, 0.0f}, +#endif + /* Index of non-zero element, value of non-zero element*/ + {0, 1.000000000f}, + {7, 1.000000000f}, + {14, 1.000000000f}, + {21, 1.000000000f}, + {28, 1.000000000f}, + {35, 1.000000000f}, + {36, 0.849999964f}, + {43, 0.849999964f} +}; + +const LS_CONVERSION_MATRIX ls_conversion_cicp14_cicp12[] = +{ +#ifdef EXT_RENDERER + /* First row indicates the number of non-zero elements and the number of matrix columns */ + {8, 8.0f}, +#else + /* First row indicates the number of non-zero elements */ + {8, 0.0f}, +#endif + /* Index of non-zero element, value of non-zero element*/ + {0, 1.000000000f}, + {9, 1.000000000f}, + {18, 1.000000000f}, + {27, 1.000000000f}, + {36, 1.000000000f}, + {45, 1.000000000f}, + {48, 0.849999964f}, + {57, 0.849999964f} +}; + +const LS_CONVERSION_MATRIX ls_conversion_cicp16_cicp6[] = +{ +#ifdef EXT_RENDERER + /* First row indicates the number of non-zero elements and the number of matrix columns */ + {10, 6.0f}, +#else + /* First row indicates the number of non-zero elements */ + {10, 0.0f}, +#endif + /* Index of non-zero element, value of non-zero element*/ + {0, 1.000000000f}, + {7, 1.000000000f}, + {14, 1.000000000f}, + {21, 1.000000000f}, + {28, 1.000000000f}, + {35, 1.000000000f}, + {36, 0.849999964f}, + {43, 0.849999964f}, + {52, 0.849999964f}, + {59, 0.849999964f} +}; + +const LS_CONVERSION_MATRIX ls_conversion_cicp16_cicp12[] = +{ +#ifdef EXT_RENDERER + /* First row indicates the number of non-zero elements and the number of matrix columns */ + {10, 8.0f}, +#else + /* First row indicates the number of non-zero elements */ + {10, 0.0f}, +#endif + /* Index of non-zero element, value of non-zero element*/ + {0, 1.000000000f}, + {9, 1.000000000f}, + {18, 1.000000000f}, + {27, 1.000000000f}, + {36, 1.000000000f}, + {45, 1.000000000f}, + {48, 0.849999964f}, + {57, 0.849999964f}, + {68, 0.849999964f}, + {77, 0.849999964f} + +}; + +const LS_CONVERSION_MATRIX ls_conversion_cicp16_cicp14[] = +{ +#ifdef EXT_RENDERER + /* First row indicates the number of non-zero elements and the number of matrix columns */ + {10, 8.0f}, +#else + /* First row indicates the number of non-zero elements */ + {10, 0.0f}, +#endif + /* Index of non-zero element, value of non-zero element*/ + {0, 1.000000000f}, + {9, 1.000000000f}, + {18, 1.000000000f}, + {27, 1.000000000f}, + {36, 1.000000000f}, + {45, 1.000000000f}, + {54, 1.000000000f}, + {63, 1.000000000f}, + {68, 0.849999964f}, + {77, 0.849999964f}, +}; + +const LS_CONVERSION_MATRIX ls_conversion_cicp19_cicp6[] = +{ +#ifdef EXT_RENDERER + /* First row indicates the number of non-zero elements and the number of matrix columns */ + {14, 6.0f}, +#else + /* First row indicates the number of non-zero elements */ + {14, 0.0f}, +#endif + /* Index of non-zero element, value of non-zero element*/ + {0, 1.000000000f}, + {7, 1.000000000f}, + {14, 1.000000000f}, + {21, 1.000000000f}, + {28, 1.000000000f}, + {35, 1.000000000f}, + {36, 0.367322683f}, + {40, 0.930093586f}, + {43, 0.367322683f}, + {47, 0.930093586f}, + {48, 0.849999964f}, + {55, 0.849999964f}, + {64, 0.849999964f}, + {71, 0.849999964f} +}; + +const LS_CONVERSION_MATRIX ls_conversion_cicp19_cicp12[] = +{ +#ifdef EXT_RENDERER + /* First row indicates the number of non-zero elements and the number of matrix columns */ + {14, 8.0f}, +#else + /* First row indicates the number of non-zero elements */ + {14, 0.0f}, +#endif + /* Index of non-zero element, value of non-zero element*/ + {0, 1.000000000f}, + {9, 1.000000000f}, + {18, 1.000000000f}, + {27, 1.000000000f}, + {38, 1.000000000f}, + {47, 1.000000000f}, + {48, 0.367322683f}, + {52, 0.930093586f}, + {57, 0.367322683f}, + {61, 0.930093586f}, + {64, 0.849999964f}, + {73, 0.849999964f}, + {84, 0.849999964f}, + {93, 0.849999964f} +}; + +const LS_CONVERSION_MATRIX ls_conversion_cicp19_cicp14[] = +{ +#ifdef EXT_RENDERER + /* First row indicates the number of non-zero elements and the number of matrix columns */ + {14, 8.0f}, +#else + /* First row indicates the number of non-zero elements */ + {14, 0.0f}, +#endif + /* Index of non-zero element, value of non-zero element*/ + {0, 1.000000000f}, + {9, 1.000000000f}, + {18, 1.000000000f}, + {27, 1.000000000f}, + {36, 1.000000000f}, + {45, 1.000000000f}, + {48, 0.367322683f}, + {52, 0.930093586f}, + {57, 0.367322683f}, + {61, 0.930093586f}, + {70, 1.000000000f}, + {79, 1.000000000f}, + {84, 0.849999964f}, + {93, 0.849999964f} +}; + +const LS_CONVERSION_MATRIX ls_conversion_cicp19_cicp16[] = +{ +#ifdef EXT_RENDERER + /* First row indicates the number of non-zero elements and the number of matrix columns */ + {14, 10.0f}, +#else + /* First row indicates the number of non-zero elements */ + {14, 0.0f}, +#endif + /* Index of non-zero element, value of non-zero element*/ + {0, 1.000000000f}, + {11, 1.000000000f}, + {22, 1.000000000f}, + {33, 1.000000000f}, + {44, 1.000000000f}, + {55, 1.000000000f}, + {60, 0.367322683f}, + {64, 0.930093586f}, + {71, 0.367322683f}, + {75, 0.930093586f}, + {86, 1.000000000f}, + {97, 1.000000000f}, + {108, 1.000000000f}, + {119, 1.000000000f} +}; + +/* Upmix matrices */ +const LS_CONVERSION_MATRIX ls_conversion_cicp12_cicp14[] = +{ +#ifdef EXT_RENDERER + /* First row indicates the number of non-zero elements and the number of matrix columns */ + {8, 8.0f}, +#else + /* First row indicates the number of non-zero elements */ + {8, 0.0f}, +#endif + /* Index of non-zero element, value of non-zero element*/ + {0, 1.0f}, + {9, 1.0f}, + {18, 1.0f}, + {27, 1.0f}, + {36, 1.0f}, + {45, 1.0f}, + {52, 1.0f}, + {61, 1.0f} +}; + +const LS_CONVERSION_MATRIX ls_conversion_cicp12_cicp16[] = +{ +#ifdef EXT_RENDERER + /* First row indicates the number of non-zero elements and the number of matrix columns */ + {8, 10.0f}, +#else + /* First row indicates the number of non-zero elements */ + {8, 0.0f}, +#endif + /* Index of non-zero element, value of non-zero element*/ + {0, 1.0f}, + {11, 1.0f}, + {22, 1.0f}, + {33, 1.0f}, + {44, 1.0f}, + {55, 1.0f}, + {64, 1.0f}, + {75, 1.0f} +}; + +const LS_CONVERSION_MATRIX ls_conversion_cicp12_cicp19[] = +{ +#ifdef EXT_RENDERER + /* First row indicates the number of non-zero elements and the number of matrix columns */ + {8, 12.0f}, +#else + /* First row indicates the number of non-zero elements */ + {8, 0.0f}, +#endif + /* Index of non-zero element, value of non-zero element*/ + {0, 1.0f}, + {13, 1.0f}, + {26, 1.0f}, + {39, 1.0f}, + {54, 1.0f}, + {67, 1.0f}, + {76, 1.0f}, + {89, 1.0f} +}; + +const LS_CONVERSION_MATRIX ls_conversion_cicp14_cicp19[] = +{ +#ifdef EXT_RENDERER + /* First row indicates the number of non-zero elements and the number of matrix columns */ + {8, 12.0f}, +#else + /* First row indicates the number of non-zero elements */ + {8, 0.0f}, +#endif + /* Index of non-zero element, value of non-zero element*/ + {0, 1.0f}, + {13, 1.0f}, + {26, 1.0f}, + {39, 1.0f}, + {52, 1.0f}, + {65, 1.0f}, + {80, 1.0f}, + {93, 1.0f} +}; + +const LS_CONVERSION_MATRIX ls_conversion_cicp16_cicp19[] = +{ +#ifdef EXT_RENDERER + /* First row indicates the number of non-zero elements and the number of matrix columns */ + {10, 12.0f}, +#else + /* First row indicates the number of non-zero elements */ + {10, 0.0f}, +#endif + /* Index of non-zero element, value of non-zero element*/ + {0, 1.0f}, + {13, 1.0f}, + {26, 1.0f}, + {39, 1.0f}, + {52, 1.0f}, + {65, 1.0f}, + {80, 1.0f}, + {93, 1.0f}, + {106, 1.0f}, + {119, 1.0f} +}; + +/* + * Mapping table of input config : output config with corresponding matrix + * NULL indicates a 1:1 mapping of existing input channels to output channels ( used for upmix ) + */ + +const LS_CONVERSION_MAPPING ls_conversion_mapping[LS_SETUP_CONVERSION_NUM_MAPPINGS] = +{ + /* Dowmix mappings - NULL is a special case for MONO / STEREO downmix */ + {AUDIO_CONFIG_5_1, AUDIO_CONFIG_MONO, NULL}, + {AUDIO_CONFIG_7_1, AUDIO_CONFIG_MONO, NULL}, + {AUDIO_CONFIG_5_1_2, AUDIO_CONFIG_MONO, NULL}, + {AUDIO_CONFIG_5_1_4, AUDIO_CONFIG_MONO, NULL}, + {AUDIO_CONFIG_7_1_4, AUDIO_CONFIG_MONO, NULL}, + + {AUDIO_CONFIG_5_1, AUDIO_CONFIG_STEREO, NULL}, + {AUDIO_CONFIG_7_1, AUDIO_CONFIG_STEREO, NULL}, + {AUDIO_CONFIG_5_1_2, AUDIO_CONFIG_STEREO, NULL}, + {AUDIO_CONFIG_5_1_4, AUDIO_CONFIG_STEREO, NULL}, + {AUDIO_CONFIG_7_1_4, AUDIO_CONFIG_STEREO, NULL}, + + {AUDIO_CONFIG_7_1, AUDIO_CONFIG_5_1, ls_conversion_cicp12_cicp6}, + + {AUDIO_CONFIG_5_1_2, AUDIO_CONFIG_5_1, ls_conversion_cicp14_cicp6}, + {AUDIO_CONFIG_5_1_2, AUDIO_CONFIG_7_1, ls_conversion_cicp14_cicp12}, + + {AUDIO_CONFIG_5_1_4, AUDIO_CONFIG_5_1, ls_conversion_cicp16_cicp6}, + {AUDIO_CONFIG_5_1_4, AUDIO_CONFIG_7_1, ls_conversion_cicp16_cicp12}, + {AUDIO_CONFIG_5_1_4, AUDIO_CONFIG_5_1_2, ls_conversion_cicp16_cicp14}, + + {AUDIO_CONFIG_7_1_4, AUDIO_CONFIG_5_1, ls_conversion_cicp19_cicp6}, + {AUDIO_CONFIG_7_1_4, AUDIO_CONFIG_7_1, ls_conversion_cicp19_cicp12}, + {AUDIO_CONFIG_7_1_4, AUDIO_CONFIG_5_1_2, ls_conversion_cicp19_cicp14}, + {AUDIO_CONFIG_7_1_4, AUDIO_CONFIG_5_1_4, ls_conversion_cicp19_cicp16}, + + /* Upmix mappings - NULL implies a 1:1 upmix */ + {AUDIO_CONFIG_STEREO, AUDIO_CONFIG_5_1, NULL}, + {AUDIO_CONFIG_STEREO, AUDIO_CONFIG_7_1, NULL}, + {AUDIO_CONFIG_STEREO, AUDIO_CONFIG_5_1_2, NULL}, + {AUDIO_CONFIG_STEREO, AUDIO_CONFIG_5_1_4, NULL}, + {AUDIO_CONFIG_STEREO, AUDIO_CONFIG_7_1_4, NULL}, + + {AUDIO_CONFIG_5_1, AUDIO_CONFIG_7_1, NULL}, + {AUDIO_CONFIG_5_1, AUDIO_CONFIG_5_1_2, NULL}, + {AUDIO_CONFIG_5_1, AUDIO_CONFIG_5_1_4, NULL}, + {AUDIO_CONFIG_5_1, AUDIO_CONFIG_7_1_4, NULL}, + + {AUDIO_CONFIG_7_1, AUDIO_CONFIG_5_1_2, ls_conversion_cicp12_cicp14}, + {AUDIO_CONFIG_7_1, AUDIO_CONFIG_5_1_4, ls_conversion_cicp12_cicp16}, + {AUDIO_CONFIG_7_1, AUDIO_CONFIG_7_1_4, ls_conversion_cicp12_cicp19}, + + {AUDIO_CONFIG_5_1_2, AUDIO_CONFIG_5_1_4, NULL}, + {AUDIO_CONFIG_5_1_2, AUDIO_CONFIG_7_1_4, ls_conversion_cicp14_cicp19}, + + {AUDIO_CONFIG_5_1_4, AUDIO_CONFIG_7_1_4, ls_conversion_cicp16_cicp19}, +}; + +/* clang-format on */ diff --git a/lib_rend/ivas_rom_rend.h b/lib_rend/ivas_rom_rend.h new file mode 100644 index 0000000000000000000000000000000000000000..aaf7fa7113af789ac565ba75b15823386d0cac80 --- /dev/null +++ b/lib_rend/ivas_rom_rend.h @@ -0,0 +1,141 @@ +/****************************************************************************************************** + + (C) 2022 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository. All Rights Reserved. + + This software is protected by copyright law and by international treaties. + The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository retain full ownership rights in their respective contributions in + the software. This notice grants no license of any kind, including but not limited to patent + license, nor is any license granted by implication, estoppel or otherwise. + + Contributors are required to enter into the IVAS codec Public Collaboration agreement before making + contributions. + + This software is provided "AS IS", without any express or implied warranties. The software is in the + development stage. It is intended exclusively for experts who have experience with such software and + solely for the purpose of inspection. All implied warranties of non-infringement, merchantability + and fitness for a particular purpose are hereby disclaimed and excluded. + + Any dispute, controversy or claim arising under or in relation to providing this software shall be + submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in + accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and + the United Nations Convention on Contracts on the International Sales of Goods. + +*******************************************************************************************************/ + +#ifndef IVAS_ROM_REND_H +#define IVAS_ROM_REND_H + +#include "options.h" +#include +#ifdef DEBUGGING +#include "debug.h" +#endif +#include "ivas_cnst.h" +#include "ivas_stat_rend.h" + +/*----------------------------------------------------------------------------------* + * FASTCONV and PARAMETRIC binaural renderer ROM tables + *----------------------------------------------------------------------------------*/ + +extern const float dmxmtx[BINAURAL_CHANNELS][11]; + +extern const int16_t channelIndex_CICP6[5]; +extern const int16_t channelIndex_CICP12[7]; +extern const int16_t channelIndex_CICP14[7]; +extern const int16_t channelIndex_CICP16[9]; +extern const int16_t channelIndex_CICP19[11]; + +/*----------------------------------------------------------------------------------* + * TD ISM Object renderer + *----------------------------------------------------------------------------------*/ + +extern const int16_t TDREND_SRC_REND_MaxTargetTimes[IVAS_NUM_SUPPORTED_FS]; +extern const int16_t TDREND_SRC_REND_MaxBlockLengths[IVAS_NUM_SUPPORTED_FS]; +extern const int16_t TDREND_MaxITD[IVAS_NUM_SUPPORTED_FS]; +extern const float TDREND_MaxITD_Incr[IVAS_NUM_SUPPORTED_FS]; + +extern const int16_t HRTF_MODEL_N_CPTS_VAR[HRTF_MODEL_N_SECTIONS]; + +extern const float SincTable[321]; + +extern const float orange53_left_avg_power[257]; +extern const float orange53_right_avg_power[257]; +extern const float orange53_coherence[257]; + + +/*----------------------------------------------------------------------------------* + * t-design and SN3D normalization table + *----------------------------------------------------------------------------------*/ + +/* SN3D norm */ +extern const float norm_sn3d_hoa3[16]; + +/* Order 11 t-design */ +extern const uint16_t t_design_11_size; +extern const float t_design_11_azimuth[70]; +extern const float t_design_11_elevation[70]; + + +/*----------------------------------------------------------------------* + * Reverberator ROM tables + *-----------------------------------------------------------------------*/ + +extern const float ivas_reverb_default_fc[]; +extern const float ivas_reverb_default_RT60[]; +extern const float ivas_reverb_default_DSR[]; + +/*----------------------------------------------------------------------------------* + * Renderer SBA & MC enc/dec matrices + *----------------------------------------------------------------------------------*/ + +extern const float hoa_dec_mtx_CICP1[16]; +extern const float ls_azimuth_CICP1[1]; +extern const float ls_elevation_CICP1[1]; +extern const uint32_t ls_LFE_last_idx_CICP1[1]; +extern const uint32_t ls_LFE_last_idx_CICP2[2]; +extern const uint32_t ls_LFE_last_idx_CICP6[6]; +extern const uint32_t ls_LFE_last_idx_CICP12[8]; +extern const uint32_t ls_LFE_last_idx_CICP14[8]; +extern const uint32_t ls_LFE_last_idx_CICP16[10]; +extern const uint32_t ls_LFE_last_idx_CICP19[12]; + +/*----------------------------------------------------------------------------------* + * LS Configuration Converter ROM tables + *----------------------------------------------------------------------------------*/ + +/* Downmix matrices */ +extern const float ls_conversion_cicpX_mono[12][1]; +extern const float ls_conversion_cicpX_stereo[12][2]; +extern const LS_CONVERSION_MATRIX ls_conversion_cicp12_cicp6[]; +extern const LS_CONVERSION_MATRIX ls_conversion_cicp14_cicp6[]; +#ifdef FIX_I54_LS_CONVERSION +extern const LS_CONVERSION_MATRIX ls_conversion_cicp14_cicp12[]; +#endif +extern const LS_CONVERSION_MATRIX ls_conversion_cicp16_cicp6[]; +extern const LS_CONVERSION_MATRIX ls_conversion_cicp16_cicp14[]; +extern const LS_CONVERSION_MATRIX ls_conversion_cicp19_cicp6[]; +extern const LS_CONVERSION_MATRIX ls_conversion_cicp19_cicp12[]; +extern const LS_CONVERSION_MATRIX ls_conversion_cicp19_cicp14[]; +extern const LS_CONVERSION_MATRIX ls_conversion_cicp19_cicp16[]; + +/* Upmix matrices */ +extern const LS_CONVERSION_MATRIX ls_conversion_cicp12_cicp14[]; +extern const LS_CONVERSION_MATRIX ls_conversion_cicp12_cicp16[]; +#ifdef FIX_I54_LS_CONVERSION +extern const LS_CONVERSION_MATRIX ls_conversion_cicp12_cicp19[]; +#endif +extern const LS_CONVERSION_MATRIX ls_conversion_cicp14_cicp19[]; +extern const LS_CONVERSION_MATRIX ls_conversion_cicp16_cicp19[]; + +/* Mapping table of input config : output config with corresponding matrix */ +extern const LS_CONVERSION_MAPPING ls_conversion_mapping[]; + +#endif /* IVAS_ROM_REND_H */ diff --git a/lib_dec/ivas_rotation.c b/lib_rend/ivas_rotation.c similarity index 93% rename from lib_dec/ivas_rotation.c rename to lib_rend/ivas_rotation.c index 702dfd08a0a46685f40830045306227403302099..4f5ba72d3983d1725027722852c420cf3a838fe7 100644 --- a/lib_dec/ivas_rotation.c +++ b/lib_rend/ivas_rotation.c @@ -45,6 +45,7 @@ #endif #include "wmops.h" +#ifndef EXT_RENDERER /*-----------------------------------------------------------------------* * Local Constants @@ -61,6 +62,7 @@ static void SHrotmatgen( float SHrotmat[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM], float Rmat[3][3], const int16_t order ); +#endif /*-----------------------------------------------------------------------* * ivas_headTrack_open() @@ -104,8 +106,12 @@ ivas_error ivas_headTrack_open( *---------------------------------------------------------------------------------*/ void QuatToRotMat( +#ifdef EXT_RENDERER + const IVAS_QUATERNION quat, /* i : quaternion describing the rotation */ +#else const Quaternion quat, /* i : quaternion describing the rotation */ - float Rmat[3][3] /* o : real-space rotation matrix for this rotation */ +#endif + float Rmat[3][3] /* o : real-space rotation matrix for this rotation */ ) { float s1, s2, s3, c1, c2, c3; @@ -173,10 +179,14 @@ void QuatToRotMat( *------------------------------------------------------------------------*/ void Quat2Euler( +#ifdef EXT_RENDERER + const IVAS_QUATERNION quat, /* i : quaternion describing the rotation */ +#else const Quaternion quat, /* i : quaternion describing the rotation */ - float *yaw, /* o : yaw */ - float *pitch, /* o : pitch */ - float *roll /* o : roll */ +#endif + float *yaw, /* o : yaw */ + float *pitch, /* o : pitch */ + float *roll /* o : roll */ ) { if ( quat.w != -3.0 ) @@ -302,24 +312,41 @@ void rotateAziEle_DirAC( void rotateFrame_shd( HEAD_TRACK_DATA_HANDLE hHeadTrackData, /* i : head track handle */ float output[][L_FRAME48k], /* i/o: unrotated HOA3 signal buffer in TD */ - const int32_t output_Fs, /* i : output sampling frequency */ - const int16_t subframe_len, /* i : subframe length per channel */ - const IVAS_OUTPUT_SETUP hTransSetup, /* i : format for rotation */ - const int16_t subframe_idx /* i : subframe index */ +#ifndef EXT_RENDERER + const int32_t output_Fs, /* i : output sampling frequency */ +#endif + const int16_t subframe_len, /* i : subframe length per channel */ + const IVAS_OUTPUT_SETUP hTransSetup, /* i : format for rotation */ + const int16_t subframe_idx /* i : subframe index */ ) { int16_t i, l, n, m; int16_t m1, m2; +#ifdef EXT_RENDERER + int16_t shd_rot_max_order; +#else int16_t shd_rot_max_order, fade_len_smp; +#endif float tmp; float tmpRot[2 * HEADROT_ORDER + 1]; float SHrotmat_prev[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM]; float SHrotmat[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM]; +#ifdef EXT_RENDERER + float cross_fade[L_FRAME48k / MAX_PARAM_SPATIAL_SUBFRAMES]; +#else float cross_fade[IVAS_FB_1MS_48K_SAMP]; +#endif shd_rot_max_order = hTransSetup.ambisonics_order; +#ifdef EXT_RENDERER + tmp = 1.0f / ( subframe_len - 1 ); + for ( i = 0; i < subframe_len; i++ ) + { + cross_fade[i] = i * tmp; + } +#else /* 1ms linear crossfade */ fade_len_smp = NS2SA( output_Fs, 1000000 ); tmp = 1.0f / fade_len_smp; @@ -327,6 +354,7 @@ void rotateFrame_shd( { cross_fade[i] = ( i + 1 ) * tmp; } +#endif /* initialize rotation matrices with zeros */ for ( i = 0; i < HEADROT_SHMAT_DIM; i++ ) @@ -360,14 +388,18 @@ void rotateFrame_shd( for ( m = m1; m < m2; m++ ) { /* crossfade with previous rotation gains */ +#ifndef EXT_RENDERER if ( i < fade_len_smp ) { +#endif tmpRot[n - m1] += cross_fade[i] * SHrotmat[n][m] * output[m][subframe_idx * subframe_len + i] + ( 1 - cross_fade[i] ) * SHrotmat_prev[n][m] * output[m][subframe_idx * subframe_len + i]; +#ifndef EXT_RENDERER } else { tmpRot[n - m1] += SHrotmat[n][m] * output[m][subframe_idx * subframe_len + i]; } +#endif } } /* write back the result */ @@ -415,31 +447,46 @@ void rotateFrame_shd( void rotateFrame_sd( HEAD_TRACK_DATA_HANDLE hHeadTrackData, /* i : head track handle */ float output[][L_FRAME48k], /* i/o: unrotated SD signal buffer in TD */ - const int32_t output_Fs, /* i : output sampling frequency */ - const int16_t subframe_len, /* i : subframe length per channel */ - const IVAS_OUTPUT_SETUP hTransSetup, /* i : format for rotation */ - const EFAP_HANDLE hEFAPdata, /* i : EFAP structure */ - const int16_t subframe_idx /* i : subframe index */ +#ifndef EXT_RENDERER + const int32_t output_Fs, /* i : output sampling frequency */ +#endif + const int16_t subframe_len, /* i : subframe length per channel */ + const IVAS_OUTPUT_SETUP hTransSetup, /* i : format for rotation */ + const EFAP_HANDLE hEFAPdata, /* i : EFAP structure */ + const int16_t subframe_idx /* i : subframe index */ ) { int16_t i, j; int16_t nchan, index_lfe; int16_t ch_in, ch_in_woLFE, ch_out, ch_out_woLFE; int16_t azimuth, elevation; +#ifndef EXT_RENDERER int16_t fade_len_smp; +#endif float tmp; float tmp_gains[MAX_CICP_CHANNELS - 1]; float gains[MAX_CICP_CHANNELS][MAX_CICP_CHANNELS]; float gains_prev[MAX_CICP_CHANNELS][MAX_CICP_CHANNELS]; float output_tmp[MAX_CICP_CHANNELS][L_FRAME48k]; +#ifdef EXT_RENDERER + float cross_fade[L_FRAME48k / MAX_PARAM_SPATIAL_SUBFRAMES]; +#else float cross_fade[IVAS_FB_1MS_48K_SAMP]; +#endif wmops_sub_start( "rotateFrame_sd" ); nchan = hTransSetup.nchan_out_woLFE + hTransSetup.num_lfe; index_lfe = hTransSetup.index_lfe[0]; +#ifdef EXT_RENDERER + tmp = 1.0f / ( subframe_len - 1 ); + for ( i = 0; i < subframe_len; i++ ) + { + cross_fade[i] = i * tmp; + } +#else /* 1ms linear crossfade */ fade_len_smp = NS2SA( output_Fs, 1000000 ); tmp = 1.0f / fade_len_smp; @@ -447,6 +494,7 @@ void rotateFrame_sd( { cross_fade[i] = ( i + 1 ) * tmp; } +#endif /* Get next quaternion and calculate rotation matrix */ QuatToRotMat( hHeadTrackData->Quaternions[hHeadTrackData->num_quaternions++], hHeadTrackData->Rmat ); @@ -473,7 +521,11 @@ void rotateFrame_sd( /* gains for previous subframe rotation */ rotateAziEle( hTransSetup.ls_azimuth[ch_in_woLFE], hTransSetup.ls_elevation[ch_in_woLFE], &azimuth, &elevation, hHeadTrackData->Rmat_prev, hTransSetup.is_planar_setup ); +#ifdef EXT_RENDERER + if ( hEFAPdata != NULL && ( hTransSetup.ls_azimuth[ch_in_woLFE] != azimuth || hTransSetup.ls_elevation[ch_in_woLFE] != elevation ) ) +#else if ( hEFAPdata != NULL && ( hTransSetup.ls_azimuth[ch_in_woLFE] != azimuth && hTransSetup.ls_elevation[ch_in_woLFE] != elevation ) ) +#endif { efap_determine_gains( hEFAPdata, tmp_gains, azimuth, elevation, EFAP_MODE_EFAP ); for ( ch_out = 0; ch_out < nchan; ch_out++ ) @@ -519,15 +571,21 @@ void rotateFrame_sd( { for ( ch_in = 0; ch_in < nchan; ch_in++ ) { - /* crossfade with previous rotation gains */ +/* crossfade with previous rotation gains */ +#ifdef EXT_RENDERER + for ( i = subframe_idx * subframe_len, j = 0; j < subframe_len; i++, j++ ) +#else for ( i = subframe_idx * subframe_len, j = 0; j < fade_len_smp; i++, j++ ) +#endif { output_tmp[ch_out][i] += ( cross_fade[j] ) * gains[ch_in][ch_out] * output[ch_in][i] + ( 1 - cross_fade[j] ) * gains_prev[ch_in][ch_out] * output[ch_in][i]; } +#ifndef EXT_RENDERER for ( ; i < ( subframe_idx + 1 ) * subframe_len; i++ ) { output_tmp[ch_out][i] += gains[ch_in][ch_out] * output[ch_in][i]; } +#endif } } @@ -879,7 +937,11 @@ static float SHrot_w( } } +#ifdef EXT_RENDERER +void SHrotmatgen( +#else static void SHrotmatgen( +#endif float SHrotmat[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM], /* o : rotation matrix in SHD */ float Rmat[3][3], /* i : real-space rotation matrix */ const int16_t order /* i : ambisonics order */ diff --git a/lib_dec/ivas_sba_rendering.c b/lib_rend/ivas_sba_rendering.c old mode 100644 new mode 100755 similarity index 65% rename from lib_dec/ivas_sba_rendering.c rename to lib_rend/ivas_sba_rendering.c index 92e50de5359911616c7ecc66599f03666574f412..e2977a81e475f3cce3a409028883335650eabd2e --- a/lib_dec/ivas_sba_rendering.c +++ b/lib_rend/ivas_sba_rendering.c @@ -47,7 +47,293 @@ * Local function prototypes *-----------------------------------------------------------------------*/ +#ifndef EXT_RENDERER static void ivas_sba_mtx_mult( float output_f[][L_FRAME48k], const int16_t output_frame, const int16_t nchan_in, const IVAS_OUTPUT_SETUP output_setup, const float *mtx_hoa_decoder ); +#endif + +#ifdef DEBUG_MODE_DIRAC +static void debug_mode_dirac( float output[MAX_OUTPUT_CHANNELS][L_FRAME48k], const int16_t nchan_transport, const int16_t output_frame ); +#endif + + +/*-------------------------------------------------------------------------* + * ivas_mc2sba() + * + * MC signals transformed into SBA in TD domain + *-------------------------------------------------------------------------*/ + +void ivas_mc2sba( + IVAS_OUTPUT_SETUP hIntSetup, /* i : Format of decoder output */ + float buffer_td[][L_FRAME48k], /* i/o: MC signals (on input) and the HOA3 (on output) */ + const int16_t output_frame, /* i : output frame length per channel */ + const int16_t sba_order, /* i : Ambisonic (SBA) order */ + const float gain_lfe /* i : gain for LFE, 0 = ignore LFE */ +) +{ + int16_t i, j, k; + int16_t idx_lfe, idx_in; + float buffer_tmp[16][L_FRAME48k]; + float gains[16]; + int16_t azimuth, elevation; + int16_t sba_num_chans; + + assert( ( sba_order <= 3 ) && "Only order up to 3 is supported!" ); + + /* Init*/ + sba_num_chans = ( sba_order + 1 ) * ( sba_order + 1 ); + for ( j = 0; j < sba_num_chans; j++ ) + { + set_zero( buffer_tmp[j], output_frame ); + } + + /* HOA encoding*/ + idx_lfe = 0; + idx_in = 0; + for ( i = 0; i < hIntSetup.nchan_out_woLFE + hIntSetup.num_lfe; i++ ) + { + if ( ( hIntSetup.num_lfe > 0 ) && ( i == hIntSetup.index_lfe[idx_lfe] ) ) + { + if ( gain_lfe > 0.f ) + { + /* Add LFE to omni W with gain*/ + for ( k = 0; k < output_frame; k++ ) + { + buffer_tmp[0][k] += gain_lfe * buffer_td[i][k]; + } + } + + if ( idx_lfe < ( hIntSetup.num_lfe - 1 ) ) + { + idx_lfe++; + } + } + else + { + azimuth = (int16_t) ( hIntSetup.ls_azimuth[idx_in] ); + elevation = (int16_t) ( hIntSetup.ls_elevation[idx_in] ); + idx_in++; + + /* get HOA response for direction (ACN/SN3D)*/ + ivas_dirac_dec_get_response( + azimuth, + elevation, + gains, + sba_order ); + + for ( j = 0; j < sba_num_chans; j++ ) + { + for ( k = 0; k < output_frame; k++ ) + { + buffer_tmp[j][k] += gains[j] * buffer_td[i][k]; + } + } + } + } + + for ( j = 0; j < sba_num_chans; j++ ) + { + mvr2r( buffer_tmp[j], buffer_td[j], output_frame ); + } + + return; +} + + +/*-------------------------------------------------------------------------* + * ivas_sba2MC_cldfb() + * + * SBA signals transformed into MC in CLDFB domain + *-------------------------------------------------------------------------*/ + +void ivas_sba2mc_cldfb( + IVAS_OUTPUT_SETUP hInSetup, /* i : Format of input layout */ + float RealBuffer[][MAX_PARAM_SPATIAL_SUBFRAMES][CLDFB_NO_CHANNELS_MAX], /* i/o: cldfb real part */ + float ImagBuffer[][MAX_PARAM_SPATIAL_SUBFRAMES][CLDFB_NO_CHANNELS_MAX], /* i/o: cldfb imag part */ + const int16_t nb_channels_out, /* i : nb of output channels */ + const int16_t nb_bands, /* i : nb of CLDFB bands to process */ + const float *hoa_dec_mtx /* i : HOA decoding mtx */ +) +{ + int16_t iBlock, iBand, n, m; + float realOut[16][MAX_PARAM_SPATIAL_SUBFRAMES * CLDFB_NO_CHANNELS_MAX], imagOut[16][MAX_PARAM_SPATIAL_SUBFRAMES * CLDFB_NO_CHANNELS_MAX]; + float g; + float *p_real, *p_imag, *p_realOut, *p_imagOut; + int16_t nb_channels_in; + + wmops_sub_start( "ivas_sba2mc_cldfb" ); + + nb_channels_in = hInSetup.nchan_out_woLFE; + assert( ( nb_channels_in == 16 ) && ( nb_channels_out == 11 ) && "ivas_sba2mc_cldfb; only HOA3 to CICP19 is for now supported!" ); + + for ( n = 0; n < nb_channels_out; n++ ) + { + set_zero( realOut[n], MAX_PARAM_SPATIAL_SUBFRAMES * nb_bands ); + set_zero( imagOut[n], MAX_PARAM_SPATIAL_SUBFRAMES * nb_bands ); + + for ( m = 0; m < nb_channels_in; m++ ) + { + g = hoa_dec_mtx[SBA_NHARM_HOA3 * n + m]; + p_realOut = realOut[n]; + p_imagOut = imagOut[n]; + for ( iBlock = 0; iBlock < MAX_PARAM_SPATIAL_SUBFRAMES; iBlock++ ) + { + p_real = RealBuffer[m][iBlock]; + p_imag = ImagBuffer[m][iBlock]; + for ( iBand = 0; iBand < nb_bands; iBand++ ) + { + *p_realOut = *p_realOut + g * *( p_real++ ); + *p_imagOut = *p_imagOut + g * *( p_imag++ ); + p_realOut++; + p_imagOut++; + } + } + } + } + + for ( n = 0; n < nb_channels_out; n++ ) + { + p_realOut = realOut[n]; + p_imagOut = imagOut[n]; + for ( iBlock = 0; iBlock < MAX_PARAM_SPATIAL_SUBFRAMES; iBlock++ ) + { + p_real = RealBuffer[n][iBlock]; + p_imag = ImagBuffer[n][iBlock]; + for ( iBand = 0; iBand < nb_bands; iBand++ ) + { + *( p_real++ ) = *p_realOut++; + *( p_imag++ ) = *p_imagOut++; + } + } + } + + wmops_sub_end(); + + return; +} + +/*-------------------------------------------------------------------* + * ivas_sba_remapTCs() + * + * Get TCs from Ambisonics signal in ACN + *-------------------------------------------------------------------*/ + +int16_t ivas_sba_remapTCs( + float sba_data[][L_FRAME48k], /* i/o: SBA signals */ + Decoder_Struct *st_ivas, /* i/o: decoder struct */ + const int16_t output_frame /* i : frame length */ +) +{ + int16_t nchan_remapped; + +#ifdef DEBUG_MODE_DIRAC + debug_mode_dirac( sba_data, st_ivas->nchan_transport, output_frame ); +#endif + + nchan_remapped = st_ivas->nchan_transport; + if ( ( st_ivas->sba_mode != SBA_MODE_SPAR && st_ivas->sba_planar && nchan_remapped >= 3 ) || + ( ( st_ivas->sba_mode == SBA_MODE_SPAR ) && nchan_remapped == 3 ) ) + { + + nchan_remapped++; + if ( st_ivas->sba_mode != SBA_MODE_SPAR ) + { + assert( ( ( st_ivas->nchan_transport == 3 ) || ( st_ivas->nchan_transport == 5 ) || ( st_ivas->nchan_transport == 7 ) ) && "Number of channels must be odd for SBA planar!" ); + } + + if ( nchan_remapped == 4 ) + { + /*For planar A-format channel 2 and 3 are identical -> Z=0*/ + mvr2r( sba_data[2], sba_data[3], output_frame ); + } + } + + if ( st_ivas->nchan_transport >= 3 ) + { + int16_t i = 0; + float temp; + + /*convert WYXZ downmix to WYZX*/ + for ( i = 0; i < output_frame; i++ ) + { + temp = sba_data[2][i]; + sba_data[2][i] = sba_data[3][i]; + sba_data[3][i] = temp; + if ( st_ivas->nchan_transport == 3 ) + { + sba_data[2][i] = 0; + } + } + } + + if ( st_ivas->sba_mode != SBA_MODE_SPAR ) + { + ivas_sba_zero_vert_comp( sba_data, st_ivas->sba_analysis_order, st_ivas->sba_planar, output_frame ); + } + + return ( nchan_remapped ); +} + + +/*-------------------------------------------------------------------------* + * ivas_ism2sba() + * + * ISM transformed into SBA in TD domain. + *-------------------------------------------------------------------------*/ + +void ivas_ism2sba( + float buffer_td[][L_FRAME48k], /* i/o: TD signal buffers */ + ISM_RENDERER_HANDLE hIsmRendererData, /* i/o: renderer data */ + const ISM_METADATA_HANDLE hIsmMetaData[], /* i : object metadata */ + const int16_t num_objects, /* i : number of objects */ + const int16_t output_frame, /* i : output frame length per channel */ + const int16_t sba_order /* i : Ambisonic (SBA) order */ +) +{ + int16_t i, j, k; + float buffer_tmp[16][L_FRAME48k]; + float gains[16]; + float g1, g2; + int16_t azimuth, elevation; + int16_t sba_num_chans; + + assert( ( sba_order <= 3 ) && "Only order up to 3 is supported!" ); + assert( hIsmRendererData != NULL && "hIsmRendererData not allocated!" ); + + /* Init*/ + sba_num_chans = ( sba_order + 1 ) * ( sba_order + 1 ); + for ( j = 0; j < sba_num_chans; j++ ) + { + set_zero( buffer_tmp[j], output_frame ); + } + + for ( i = 0; i < num_objects; i++ ) + { + azimuth = (int16_t) ( hIsmMetaData[i]->azimuth + 0.5f ); + elevation = (int16_t) ( hIsmMetaData[i]->elevation + 0.5f ); + + /*get HOA gets for direction (ACN/SN3D)*/ + ivas_dirac_dec_get_response( azimuth, elevation, gains, sba_order ); + + for ( j = 0; j < sba_num_chans; j++ ) + { + g2 = 0.f; + for ( k = 0; k < output_frame; k++ ) + { + g2 += 1.f / output_frame; + g1 = 1.0f - g2; + buffer_tmp[j][k] += ( g2 * gains[j] + g1 * hIsmRendererData->prev_gains[i][j] ) * buffer_td[i][k]; + } + hIsmRendererData->prev_gains[i][j] = gains[j]; + } + } + + for ( j = 0; j < sba_num_chans; j++ ) + { + mvr2r( buffer_tmp[j], buffer_td[j], output_frame ); + } + + return; +} /*-------------------------------------------------------------------* @@ -142,7 +428,11 @@ ivas_error ivas_sba_linear_renderer( * HOA decoding with LFE insertion *-------------------------------------------------------------------*/ +#ifdef EXT_RENDERER +void ivas_sba_mtx_mult( +#else static void ivas_sba_mtx_mult( +#endif float output_f[][L_FRAME48k], /* i/o: synthesized core-coder transport channels/DirAC output */ const int16_t output_frame, /* i : output frame length per channel */ const int16_t nchan_in, /* i : Number of ambisonic channels */ diff --git a/lib_rend/ivas_stat_rend.h b/lib_rend/ivas_stat_rend.h new file mode 100644 index 0000000000000000000000000000000000000000..a38b8d96c1f428dcb1d4d858639fdda1de8d74b8 --- /dev/null +++ b/lib_rend/ivas_stat_rend.h @@ -0,0 +1,102 @@ +/****************************************************************************************************** + + (C) 2022 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository. All Rights Reserved. + + This software is protected by copyright law and by international treaties. + The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository retain full ownership rights in their respective contributions in + the software. This notice grants no license of any kind, including but not limited to patent + license, nor is any license granted by implication, estoppel or otherwise. + + Contributors are required to enter into the IVAS codec Public Collaboration agreement before making + contributions. + + This software is provided "AS IS", without any express or implied warranties. The software is in the + development stage. It is intended exclusively for experts who have experience with such software and + solely for the purpose of inspection. All implied warranties of non-infringement, merchantability + and fitness for a particular purpose are hereby disclaimed and excluded. + + Any dispute, controversy or claim arising under or in relation to providing this software shall be + submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in + accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and + the United Nations Convention on Contracts on the International Sales of Goods. + +*******************************************************************************************************/ + +#ifndef IVAS_STAT_REND_H +#define IVAS_STAT_REND_H + +#include +#include "ivas_cnst.h" + +#define MAX_SPEAKERS 12 /* Max number of speakers (including LFE) in a channel-based config */ + +/*----------------------------------------------------------------------------------* + * Loudspeaker Configuration Conversion structure + *----------------------------------------------------------------------------------*/ + +typedef struct ivas_LS_setupconversion_struct +{ + float *dmxMtx[MAX_OUTPUT_CHANNELS]; + float *targetEnergyPrev[MAX_OUTPUT_CHANNELS]; + float *dmxEnergyPrev[MAX_OUTPUT_CHANNELS]; + int16_t sfbOffset[MAX_SFB + 2]; + int16_t sfbCnt; + +} LSSETUP_CONVERSION_STRUCT, *LSSETUP_CONVERSION_HANDLE; + + +typedef struct ivas_LS_setupconversion_matrix +{ + int16_t index; + float value; +} LS_CONVERSION_MATRIX; + +typedef struct ivas_LS_setupconversion_mapping +{ + AUDIO_CONFIG input_config; + AUDIO_CONFIG output_config; + const LS_CONVERSION_MATRIX *conversion_matrix; +} LS_CONVERSION_MAPPING; + +typedef struct ivas_mono_downmix_renderer_struct +{ + float inputEnergy[CLDFB_NO_CHANNELS_MAX]; + float protoEnergy[CLDFB_NO_CHANNELS_MAX]; + +} MONO_DOWNMIX_RENDERER_STRUCT, *MONO_DOWNMIX_RENDERER_HANDLE; + + +/*----------------------------------------------------------------------------------* + * Custom Loudspeaker configuration structure + *----------------------------------------------------------------------------------*/ + +typedef struct ivas_LS_setup_custom +{ + int16_t is_planar_setup; /* flag to indicate if setup is planar or not */ + int16_t num_spk; /* number of custom loudspeakers */ + float ls_azimuth[MAX_OUTPUT_CHANNELS]; /* custom loudspeaker azimuths */ + float ls_elevation[MAX_OUTPUT_CHANNELS]; /* custom loudspeaker elevations */ + int16_t num_lfe; /* number of LFE channels */ + int16_t lfe_idx[MAX_OUTPUT_CHANNELS]; /* index for LFE channel insertion */ + int16_t separate_ch_found; /* flag to indicate if a center channel was found */ + float separate_ch_gains[MAX_OUTPUT_CHANNELS]; /* gains to pan McMASA separateChannel in case no center channel is present */ + +} LSSETUP_CUSTOM_STRUCT, *LSSETUP_CUSTOM_HANDLE; + +/* Channel types in a channel-based config */ +typedef enum +{ + CHANNEL_TYPE_UNUSED = 0, + CHANNEL_TYPE_SPEAKER, + CHANNEL_TYPE_LFE +} ChannelType; + +#endif /* IVAS_STAT_REND_H */ diff --git a/lib_rend/lib_rend.c b/lib_rend/lib_rend.c new file mode 100644 index 0000000000000000000000000000000000000000..ef38d00c19e3513c9d6a00e66740cfcc1647e68e --- /dev/null +++ b/lib_rend/lib_rend.c @@ -0,0 +1,4140 @@ +/****************************************************************************************************** + + (C) 2022 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository. All Rights Reserved. + + This software is protected by copyright law and by international treaties. + The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository retain full ownership rights in their respective contributions in + the software. This notice grants no license of any kind, including but not limited to patent + license, nor is any license granted by implication, estoppel or otherwise. + + Contributors are required to enter into the IVAS codec Public Collaboration agreement before making + contributions. + + This software is provided "AS IS", without any express or implied warranties. The software is in the + development stage. It is intended exclusively for experts who have experience with such software and + solely for the purpose of inspection. All implied warranties of non-infringement, merchantability + and fitness for a particular purpose are hereby disclaimed and excluded. + + Any dispute, controversy or claim arising under or in relation to providing this software shall be + submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in + accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and + the United Nations Convention on Contracts on the International Sales of Goods. + +*******************************************************************************************************/ + +#include "options.h" +#include "ivas_cnst.h" +#include "lib_rend.h" +#include "ivas_prot.h" +#include "ivas_rom_com.h" +#include "ivas_rom_dec.h" +#include "ivas_rom_rend.h" +#include "ivas_lib_rend_internal.h" +#include "prot.h" +#include "wmops.h" + +#include +#include +#include +#include +#include +#include + +#ifdef EXT_RENDERER +/* Maximum buffer length (per channel) in samples. + * Keep this separate from L_FRAME48k in case we want to support different size later */ +#define MAX_BUFFER_LENGTH_PER_CHANNEL ( L_FRAME48k ) + +/* Maximum buffer length (total) in samples. */ +#define MAX_BUFFER_LENGTH ( MAX_BUFFER_LENGTH_PER_CHANNEL * MAX_INPUT_CHANNELS ) + +/* Frame size required when rendering to binaural */ +#define BINAURAL_RENDERING_FRAME_SIZE_MS 20 + + +typedef float pan_vector[MAX_OUTPUT_CHANNELS]; +typedef float pan_matrix[MAX_INPUT_CHANNELS][MAX_OUTPUT_CHANNELS]; +typedef float rotation_gains[MAX_INPUT_CHANNELS][MAX_INPUT_CHANNELS]; +typedef float rotation_matrix[3][3]; + +/* EFAP wrapper to simplify writing panning gains to a vector that includes LFE channels */ +typedef struct +{ + EFAP_HANDLE hEfap; + IVAS_REND_AudioConfig speakerConfig; + const LSSETUP_CUSTOM_STRUCT *pCustomLsSetup; /* Pointer to main custom LS struct from renderer handle - doesn't need freeing */ +} EFAP_WRAPPER; + +/* Lightweight helper struct that gathers all information required for rendering + * any config to any other config. Used to simplify signatures of rendering functions. + * + * This struct should store ONLY CONST POINTERS to data existing elsewhere. + * Storing pointers instead of data itself ensures that no additional updates + * are required when any of these are changed in the renderer. Making the pointers + * const ensures that this data is only read, but not modified by the rendering functions. */ +typedef struct +{ + const int32_t *pOutSampleRate; + const IVAS_REND_AudioConfig *pOutConfig; + const LSSETUP_CUSTOM_STRUCT *pCustomLsOut; + const EFAP_WRAPPER *pEfapOutWrapper; + const IVAS_REND_HeadRotData *pHeadRotData; + /* TODO @Philips : would this be a better place to store the render config data? + * bearing in mind we could have multiple inputs to the renderer, we might neeed to accomodate + * multiple rendering configurations unless one global one can be used. If this is not relevant, + * feel free to remove this TODO. + */ +} rendering_context; + +/* Common base for input structs */ +typedef struct +{ + IVAS_REND_AudioConfig inConfig; + IVAS_REND_InputId id; + IVAS_REND_AudioBuffer inputBuffer; + float bufferData[MAX_BUFFER_LENGTH]; + float gain; /* Linear, not in dB */ + rendering_context ctx; + int32_t numNewSamplesPerChannel; /* Used to keep track how much new audio was fed before rendering current frame */ +} input_base; + +typedef struct +{ + input_base base; + IVAS_REND_AudioObjectPosition currentPos; + IVAS_REND_AudioObjectPosition previousPos; + TDREND_WRAPPER tdRendWrapper; + CREND_WRAPPER crendWrapper; + rotation_matrix rot_mat_prev; +} input_ism; + +typedef struct +{ + input_base base; + + /* Full panning matrix. 1st index is input channel, 2nd index is output channel. + All LFE channels should be included, both for inputs and outputs */ + pan_matrix panGains; + + LSSETUP_CUSTOM_STRUCT customLsInput; + EFAP_WRAPPER efapInWrapper; + TDREND_WRAPPER tdRendWrapper; + CREND_WRAPPER crendWrapper; + rotation_gains rot_gains_prev; + IVAS_REND_LfeRouting lfeRouting; +} input_mc; + +typedef struct +{ + input_base base; + pan_matrix hoaDecMtx; + CREND_WRAPPER crendWrapper; + rotation_gains rot_gains_prev; +} input_sba; + +struct IVAS_REND +{ + int32_t sampleRateOut; + + IVAS_LIMITER_HANDLE hLimiter; +#ifdef DEBUGGING + int32_t numClipping; /* Counter of clipped output samples */ +#endif + + input_ism inputsIsm[RENDERER_MAX_ISM_INPUTS]; + input_mc inputsMc[RENDERER_MAX_MC_INPUTS]; + input_sba inputsSba[RENDERER_MAX_SBA_INPUTS]; + + /* TODO @Philips - inputConfig should not be stored here, but read from e.g. input_mc->input_base.inConfig, please remove this */ + IVAS_REND_AudioConfig inputConfig; + IVAS_REND_AudioConfig outputConfig; + EFAP_WRAPPER efapOutWrapper; + IVAS_LSSETUP_CUSTOM_STRUCT customLsOut; + + IVAS_REND_HeadRotData headRotData; + + int8_t rendererConfigEnabled; + RENDER_CONFIG_DATA *hRendererConfig; /* Renderer config pointer */ +}; + +static IVAS_QUATERNION quaternionInit( + void ) +{ + IVAS_QUATERNION q; + q.w = 1.0f; + q.x = q.y = q.z = 0.0f; + return q; +} + +static float *getSmplPtr( + IVAS_REND_AudioBuffer buffer, + uint32_t chnlIdx, + uint32_t smplIdx ) +{ + return buffer.data + chnlIdx * buffer.config.numSamplesPerChannel + smplIdx; +} + +static void copyBufferTo2dArray( + const IVAS_REND_AudioBuffer buffer, + float array[MAX_OUTPUT_CHANNELS][L_FRAME48k] ) +{ + uint32_t smplIdx; + uint32_t chnlIdx; + const float *readPtr; + + readPtr = buffer.data; + + for ( chnlIdx = 0; chnlIdx < (uint32_t) buffer.config.numChannels; ++chnlIdx ) + { + for ( smplIdx = 0; smplIdx < (uint32_t) buffer.config.numSamplesPerChannel; ++smplIdx ) + { + array[chnlIdx][smplIdx] = *readPtr++; + } + } + + return; +} + +static void accumulate2dArrayToBuffer( + float array[MAX_OUTPUT_CHANNELS][L_FRAME48k], + IVAS_REND_AudioBuffer *buffer ) +{ + int16_t smplIdx, chnlIdx; + float *writePtr; + + writePtr = buffer->data; + for ( chnlIdx = 0; chnlIdx < buffer->config.numChannels; ++chnlIdx ) + { + for ( smplIdx = 0; smplIdx < buffer->config.numSamplesPerChannel; ++smplIdx ) + { + *writePtr++ += array[chnlIdx][smplIdx]; + } + } + + return; +} + +/*-------------------------------------------------------------------* + * limitRendererOutput() + * + * In-place saturation control for multichannel buffers with adaptive release time + * + * + *-------------------------------------------------------------------*/ + +/*! r: number of clipped output samples */ +static int32_t limitRendererOutput( + IVAS_LIMITER_HANDLE hLimiter, /* i/o: limiter struct handle */ + float *output, /* i/o: I/O buffer */ + const int16_t output_frame, /* i : number of samples per channel in the buffer */ + const float threshold /* i : signal amplitude above which limiting starts to be applied */ +) +{ + int16_t i; + float **channels; + int16_t num_channels; + int32_t numClipping = 0; + + /* return early if given bad parameters */ + if ( hLimiter == NULL || output == NULL || output_frame <= 0 ) + { + return 0; + } + + channels = hLimiter->channel_ptrs; + num_channels = hLimiter->num_channels; + + for ( i = 0; i < num_channels; ++i ) + { + channels[i] = output + i * output_frame; + } + + limiter_process( hLimiter, output_frame, threshold, 0, NULL ); + + /* Apply clipping to buffer in case the limiter let through some samples > 1.0f */ + for ( i = 0; i < output_frame * num_channels; ++i ) + { +#ifdef DEBUGGING + if ( output[i] < INT16_MIN || output[i] > INT16_MAX ) + { + ++numClipping; + } +#endif + + output[i] = min( max( INT16_MIN, output[i] ), INT16_MAX ); + } + + return numClipping; +} + +static AUDIO_CONFIG rendAudioConfigToIvasAudioConfig( // VE2AT: similar is defined again at line 397, why? + IVAS_REND_AudioConfig rendConfig ) +{ + switch ( rendConfig ) + { + case IVAS_REND_AUDIO_CONFIG_MONO: + return AUDIO_CONFIG_MONO; + case IVAS_REND_AUDIO_CONFIG_STEREO: + return AUDIO_CONFIG_STEREO; + case IVAS_REND_AUDIO_CONFIG_5_1: + return AUDIO_CONFIG_5_1; + case IVAS_REND_AUDIO_CONFIG_7_1: + return AUDIO_CONFIG_7_1; + case IVAS_REND_AUDIO_CONFIG_5_1_2: + return AUDIO_CONFIG_5_1_2; + case IVAS_REND_AUDIO_CONFIG_5_1_4: + return AUDIO_CONFIG_5_1_4; + case IVAS_REND_AUDIO_CONFIG_7_1_4: + return AUDIO_CONFIG_7_1_4; + case IVAS_REND_AUDIO_CONFIG_LS_CUSTOM: + return AUDIO_CONFIG_LS_CUSTOM; + case IVAS_REND_AUDIO_CONFIG_FOA: + return AUDIO_CONFIG_FOA; + case IVAS_REND_AUDIO_CONFIG_HOA2: + return AUDIO_CONFIG_HOA2; + case IVAS_REND_AUDIO_CONFIG_HOA3: + return AUDIO_CONFIG_HOA3; + case IVAS_REND_AUDIO_CONFIG_OBJECT: + return AUDIO_CONFIG_OBA; + case IVAS_REND_AUDIO_CONFIG_BINAURAL: + return AUDIO_CONFIG_BINAURAL; + case IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM: + return AUDIO_CONFIG_BINAURAL_ROOM; + case IVAS_REND_AUDIO_CONFIG_MASA1: + return AUDIO_CONFIG_MASA1; + case IVAS_REND_AUDIO_CONFIG_MASA2: + return AUDIO_CONFIG_MASA2; + default: + break; + } + + return AUDIO_CONFIG_INVALID; +} + +static ivas_error validateOutputAudioConfig( + IVAS_REND_AudioConfig outConfig ) +{ + switch ( outConfig ) + { + case IVAS_REND_AUDIO_CONFIG_MONO: + case IVAS_REND_AUDIO_CONFIG_STEREO: + case IVAS_REND_AUDIO_CONFIG_5_1: + case IVAS_REND_AUDIO_CONFIG_7_1: + case IVAS_REND_AUDIO_CONFIG_5_1_2: + case IVAS_REND_AUDIO_CONFIG_5_1_4: + case IVAS_REND_AUDIO_CONFIG_7_1_4: + case IVAS_REND_AUDIO_CONFIG_LS_CUSTOM: + case IVAS_REND_AUDIO_CONFIG_FOA: + case IVAS_REND_AUDIO_CONFIG_HOA2: + case IVAS_REND_AUDIO_CONFIG_HOA3: + case IVAS_REND_AUDIO_CONFIG_BINAURAL: + case IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM: + return IVAS_ERR_OK; + default: + break; + } + + return IVAS_ERR_INVALID_OUTPUT_FORMAT; +} + +IVAS_REND_AudioConfigType getAudioConfigType( + IVAS_REND_AudioConfig config ) +{ + /* By definition, config type is the second byte (from LSB) of IVAS_REND_AudioConfig enum. */ + return ( config & 0xFF00 ) >> 8; // VE2AT: MSVC returns warning C4244: 'return': conversion from 'int' to 'IVAS_REND_InputId', possible loss of data +} + +static ivas_error validateOutputSampleRate( + const int32_t sampleRate, + const IVAS_REND_AudioConfig outConfig ) +{ + if ( getAudioConfigType( outConfig ) != IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL ) + { + /* If no binaural rendering, any sampling rate is supported */ + return IVAS_ERR_OK; + } + + /* Otherwise rendering to binaural, support the same set as IVAS decoder */ + switch ( sampleRate ) + { + case 8000: + case 16000: + case 32000: + case 48000: + return IVAS_ERR_OK; + } + + return IVAS_ERR_INVALID_SAMPLING_RATE; +} + +ivas_error getAudioConfigNumChannels( + const IVAS_REND_AudioConfig config, + int16_t *numChannels ) +{ + switch ( config ) + { + case IVAS_REND_AUDIO_CONFIG_MONO: + case IVAS_REND_AUDIO_CONFIG_OBJECT: + *numChannels = 1; + break; + case IVAS_REND_AUDIO_CONFIG_STEREO: + case IVAS_REND_AUDIO_CONFIG_BINAURAL: + case IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM: + *numChannels = 2; + break; + case IVAS_REND_AUDIO_CONFIG_FOA: + *numChannels = 4; + break; + case IVAS_REND_AUDIO_CONFIG_5_1: + *numChannels = 6; + break; + case IVAS_REND_AUDIO_CONFIG_7_1: + case IVAS_REND_AUDIO_CONFIG_5_1_2: + *numChannels = 8; + break; + case IVAS_REND_AUDIO_CONFIG_HOA2: + *numChannels = 9; + break; + case IVAS_REND_AUDIO_CONFIG_5_1_4: + *numChannels = 10; + break; + case IVAS_REND_AUDIO_CONFIG_7_1_4: + *numChannels = 12; + break; + case IVAS_REND_AUDIO_CONFIG_HOA3: + *numChannels = 16; + break; + default: + return IVAS_ERR_NUM_CHANNELS_UNKNOWN; + } + + return IVAS_ERR_OK; +} + +AUDIO_CONFIG getIvasAudioConfigFromRendAudioConfig( + IVAS_REND_AudioConfig config ) +{ + switch ( config ) + { + case IVAS_REND_AUDIO_CONFIG_MONO: + return AUDIO_CONFIG_MONO; + case IVAS_REND_AUDIO_CONFIG_STEREO: + return AUDIO_CONFIG_STEREO; + case IVAS_REND_AUDIO_CONFIG_BINAURAL: + return AUDIO_CONFIG_BINAURAL; + case IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM: + return AUDIO_CONFIG_BINAURAL_ROOM; + case IVAS_REND_AUDIO_CONFIG_5_1: + return AUDIO_CONFIG_5_1; + case IVAS_REND_AUDIO_CONFIG_7_1: + return AUDIO_CONFIG_7_1; + case IVAS_REND_AUDIO_CONFIG_5_1_2: + return AUDIO_CONFIG_5_1_2; + case IVAS_REND_AUDIO_CONFIG_5_1_4: + return AUDIO_CONFIG_5_1_4; + case IVAS_REND_AUDIO_CONFIG_7_1_4: + return AUDIO_CONFIG_7_1_4; + case IVAS_REND_AUDIO_CONFIG_FOA: + return AUDIO_CONFIG_FOA; + case IVAS_REND_AUDIO_CONFIG_HOA2: + return AUDIO_CONFIG_HOA2; + case IVAS_REND_AUDIO_CONFIG_HOA3: + return AUDIO_CONFIG_HOA3; + default: + return AUDIO_CONFIG_INVALID; + } +} + +static ivas_error initLimiter( + IVAS_LIMITER_HANDLE *phLimiter, + const int16_t numChannels, + const int32_t sampleRate ) +{ + /* If re-initializing with unchanged values, return early */ + if ( *phLimiter != NULL && + ( *phLimiter )->num_channels == numChannels && + ( *phLimiter )->sampling_rate == sampleRate ) + { + return IVAS_ERR_OK; + } + + /* Support re-init: close if already allocated */ + if ( *phLimiter != NULL ) + { + ivas_limiter_close( phLimiter ); + } + + *phLimiter = ivas_limiter_open( (int16_t) numChannels, sampleRate ); + if ( *phLimiter == NULL ) + { + return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Failed to open limiter" ); + } + + return IVAS_ERR_OK; +} + +static LSSETUP_CUSTOM_STRUCT defaultCustomLs( + void ) +{ + LSSETUP_CUSTOM_STRUCT ls; + + /* Set mono by default. This simplifies initialization, + since output config is never in an undefined state. */ + ls.is_planar_setup = 1; + ls.num_spk = 1; + set_zero( ls.ls_azimuth, MAX_OUTPUT_CHANNELS ); + set_zero( ls.ls_elevation, MAX_OUTPUT_CHANNELS ); + ls.num_lfe = 0; + set_s( ls.lfe_idx, 0, MAX_OUTPUT_CHANNELS ); + ls.separate_ch_found = 0; + set_f( ls.separate_ch_gains, 0, MAX_OUTPUT_CHANNELS ); + + return ls; +} + +static ivas_error getSpeakerAzimuths( + IVAS_REND_AudioConfig config, + const float **azimuths ) +{ + switch ( config ) + { + case IVAS_REND_AUDIO_CONFIG_MONO: + *azimuths = ls_azimuth_CICP1; + break; + case IVAS_REND_AUDIO_CONFIG_STEREO: + *azimuths = ls_azimuth_CICP2; + break; + case IVAS_REND_AUDIO_CONFIG_5_1: + *azimuths = ls_azimuth_CICP6; + break; + case IVAS_REND_AUDIO_CONFIG_7_1: + *azimuths = ls_azimuth_CICP12; + break; + case IVAS_REND_AUDIO_CONFIG_5_1_2: + *azimuths = ls_azimuth_CICP14; + break; + case IVAS_REND_AUDIO_CONFIG_5_1_4: + *azimuths = ls_azimuth_CICP16; + break; + case IVAS_REND_AUDIO_CONFIG_7_1_4: + *azimuths = ls_azimuth_CICP19; + break; + default: + return IVAS_ERROR( IVAS_ERR_INTERNAL_FATAL, "Unexpected audio config" ); + } + + return IVAS_ERR_OK; +} + +static ivas_error getSpeakerElevations( + IVAS_REND_AudioConfig config, + const float **elevations ) +{ + switch ( config ) + { + case IVAS_REND_AUDIO_CONFIG_MONO: + *elevations = ls_elevation_CICP1; + break; + case IVAS_REND_AUDIO_CONFIG_STEREO: + *elevations = ls_elevation_CICP2; + break; + case IVAS_REND_AUDIO_CONFIG_5_1: + *elevations = ls_elevation_CICP6; + break; + case IVAS_REND_AUDIO_CONFIG_7_1: + *elevations = ls_elevation_CICP12; + break; + case IVAS_REND_AUDIO_CONFIG_5_1_2: + *elevations = ls_elevation_CICP14; + break; + case IVAS_REND_AUDIO_CONFIG_5_1_4: + *elevations = ls_elevation_CICP16; + break; + case IVAS_REND_AUDIO_CONFIG_7_1_4: + *elevations = ls_elevation_CICP19; + break; + default: + return IVAS_ERROR( IVAS_ERR_INTERNAL_FATAL, "Unexpected audio config" ); + } + + return IVAS_ERR_OK; +} + +static ivas_error getAmbisonicsOrder( + IVAS_REND_AudioConfig config, + int16_t *order ) +{ + switch ( config ) + { + case IVAS_REND_AUDIO_CONFIG_FOA: + *order = 1; + break; + case IVAS_REND_AUDIO_CONFIG_HOA2: + *order = 2; + break; + case IVAS_REND_AUDIO_CONFIG_HOA3: + *order = 3; + break; + default: + return IVAS_ERROR( IVAS_ERR_INTERNAL_FATAL, "Unsupported audio config" ); + } + + return IVAS_ERR_OK; +} + +static ivas_error getNumNonLfeChannelsInSpeakerLayout( + IVAS_REND_AudioConfig config, + int16_t *numNonLfeChannels ) +{ + switch ( config ) + { + case IVAS_REND_AUDIO_CONFIG_MONO: + *numNonLfeChannels = 1; + break; + case IVAS_REND_AUDIO_CONFIG_STEREO: + *numNonLfeChannels = 2; + break; + case IVAS_REND_AUDIO_CONFIG_5_1: + *numNonLfeChannels = 5; + break; + case IVAS_REND_AUDIO_CONFIG_5_1_2: + case IVAS_REND_AUDIO_CONFIG_7_1: + *numNonLfeChannels = 7; + break; + case IVAS_REND_AUDIO_CONFIG_5_1_4: + *numNonLfeChannels = 9; + break; + case IVAS_REND_AUDIO_CONFIG_7_1_4: + *numNonLfeChannels = 11; + break; + default: + return IVAS_ERROR( IVAS_ERR_INTERNAL_FATAL, "Unexpected audio config" ); + } + + return IVAS_ERR_OK; +} + +static ivas_error getMcConfigValues( + IVAS_REND_AudioConfig inConfig, + LSSETUP_CUSTOM_STRUCT inCustomLs, + const float **azimuth, + const float **elevation, + int16_t *lfe_idx, + int16_t *is_planar ) +{ + int16_t i; + + *lfe_idx = -1; + *is_planar = 1; + switch ( inConfig ) + { + case IVAS_REND_AUDIO_CONFIG_LS_CUSTOM: + *azimuth = (const float *) &inCustomLs.ls_azimuth; + *elevation = (const float *) &inCustomLs.ls_elevation; + if ( inCustomLs.num_lfe > 0 ) + { + *lfe_idx = inCustomLs.lfe_idx[0]; + } + for ( i = 0; i < inCustomLs.num_spk; i++ ) + { + if ( inCustomLs.ls_elevation[i] != 0 ) + { + *is_planar = 0; + break; + } + } + break; + case IVAS_REND_AUDIO_CONFIG_MONO: + case IVAS_REND_AUDIO_CONFIG_STEREO: + getSpeakerAzimuths( inConfig, azimuth ); + getSpeakerElevations( inConfig, elevation ); + break; + case IVAS_REND_AUDIO_CONFIG_5_1: + case IVAS_REND_AUDIO_CONFIG_7_1: + case IVAS_REND_AUDIO_CONFIG_5_1_2: + case IVAS_REND_AUDIO_CONFIG_5_1_4: + case IVAS_REND_AUDIO_CONFIG_7_1_4: + getSpeakerAzimuths( inConfig, azimuth ); + getSpeakerElevations( inConfig, elevation ); + *lfe_idx = LFE_CHANNEL; + *is_planar = ( inConfig == IVAS_REND_AUDIO_CONFIG_5_1 || inConfig == IVAS_REND_AUDIO_CONFIG_7_1 ) ? 1 : 0; + break; + default: + assert( !"Invalid speaker config" ); + return IVAS_ERR_WRONG_PARAMS; + } + + return IVAS_ERR_OK; +} + +static ivas_error initEfap( + EFAP_WRAPPER *pEfapWrapper, + IVAS_REND_AudioConfig outConfig, + const LSSETUP_CUSTOM_STRUCT *pCustomLsOut ) +{ + ivas_error error; + const float *azimuths; + const float *elevations; + int16_t numNonLfeChannels; + + if ( outConfig == IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM ) + { + pEfapWrapper->speakerConfig = IVAS_REND_AUDIO_CONFIG_7_1_4; + } + else + { + pEfapWrapper->speakerConfig = outConfig; + } + pEfapWrapper->pCustomLsSetup = pCustomLsOut; + + /* If re-initializing, free existing EFAP handle. */ + if ( pEfapWrapper->hEfap != NULL ) + { + efap_free_data( &pEfapWrapper->hEfap ); + } + + /* Only initialize EFAP handle if output config is channel-based */ + if ( getAudioConfigType( pEfapWrapper->speakerConfig ) != IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED ) + { + pEfapWrapper->hEfap = NULL; + return IVAS_ERR_OK; + } + + if ( outConfig == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM ) + { + if ( ( error = efap_init_data( &pEfapWrapper->hEfap, pCustomLsOut->ls_azimuth, pCustomLsOut->ls_elevation, pCustomLsOut->num_spk, EFAP_MODE_EFAP ) ) != IVAS_ERR_OK ) + { + return error; + } + } + else + { + if ( ( error = getSpeakerAzimuths( pEfapWrapper->speakerConfig, &azimuths ) ) != IVAS_ERR_OK ) + { + return error; + } + if ( ( error = getSpeakerElevations( pEfapWrapper->speakerConfig, &elevations ) ) != IVAS_ERR_OK ) + { + return error; + } + if ( ( error = getNumNonLfeChannelsInSpeakerLayout( pEfapWrapper->speakerConfig, &numNonLfeChannels ) ) != IVAS_ERR_OK ) + { + return error; + } + if ( ( error = efap_init_data( &pEfapWrapper->hEfap, azimuths, elevations, numNonLfeChannels, EFAP_MODE_EFAP ) ) != IVAS_ERR_OK ) + { + return error; + } + } + + return IVAS_ERR_OK; +} + +static ivas_error getEfapGains( + EFAP_WRAPPER efapWrapper, + const float azi, + const float ele, + pan_vector panGains ) +{ + pan_vector tmpPanGains; /* tmp pan gain buffer without LFE channels */ + float *readPtr; + int16_t i; + int16_t lfeCount; + int16_t numChannels; + ivas_error error; + + /* EFAP returns an array of gains only for non-LFE speakers */ + efap_determine_gains( efapWrapper.hEfap, tmpPanGains, azi, ele, EFAP_MODE_EFAP ); + + /* Now copy to buffer that includes LFE channels */ + if ( efapWrapper.speakerConfig == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM ) + { + numChannels = efapWrapper.pCustomLsSetup->num_spk + efapWrapper.pCustomLsSetup->num_lfe; + readPtr = tmpPanGains; + + for ( i = 0, lfeCount = 0; i < numChannels; ++i ) + { + if ( lfeCount < efapWrapper.pCustomLsSetup->num_lfe && i == efapWrapper.pCustomLsSetup->lfe_idx[lfeCount] ) + { + panGains[i] = 0.0f; + ++lfeCount; + } + else + { + panGains[i] = *readPtr; + ++readPtr; + } + } + } + else + { + if ( ( error = getAudioConfigNumChannels( efapWrapper.speakerConfig, &numChannels ) ) != IVAS_ERR_OK ) + { + return error; + } + + readPtr = tmpPanGains; + + for ( i = 0; i < numChannels; ++i ) + { + if ( i == LFE_CHANNEL ) + { + panGains[i] = 0.0f; + } + else + { + panGains[i] = *readPtr; + ++readPtr; + } + } + } + + return IVAS_ERR_OK; +} + +static void initHeadRotation( + IVAS_REND_HANDLE hIvasRend ) +{ + int16_t i, crossfade_len; + float tmp; + + /* Head rotation is enabled by default */ + hIvasRend->headRotData.headRotEnabled = 1; + + /* Initialize 5ms crossfade */ + crossfade_len = L_FRAME48k / RENDERER_HEAD_POSITIONS_PER_FRAME; + tmp = 1.f / ( crossfade_len - 1 ); + for ( i = 0; i < crossfade_len; i++ ) + { + hIvasRend->headRotData.crossfade[i] = i * tmp; + } + + /* Initialize with unit quaternions */ + for ( i = 0; i < RENDERER_HEAD_POSITIONS_PER_FRAME; ++i ) + { + hIvasRend->headRotData.headPositions[i] = quaternionInit(); + } + + return; +} + +static void initRotMatrix( + rotation_matrix rot_mat ) +{ + int16_t i; + + /* Initialize rotation matrices */ + for ( i = 0; i < 3; i++ ) + { + set_zero( rot_mat[i], 3 ); + rot_mat[i][i] = 1.f; + } + + return; +} + + +static void initRotGains( + rotation_gains rot_gains ) +{ + int16_t i; + /* Set gains to passthrough */ + for ( i = 0; i < MAX_INPUT_CHANNELS; i++ ) + { + set_zero( rot_gains[i], MAX_INPUT_CHANNELS ); + rot_gains[i][i] = 1.f; + } + + return; +} + +static void initRendInputBase( + input_base *inputBase, + const IVAS_REND_AudioConfig inConfig, + const IVAS_REND_InputId id, + const rendering_context rendCtx ) +{ + inputBase->inConfig = inConfig; + inputBase->id = id; + inputBase->gain = 1.0f; + inputBase->ctx = rendCtx; + inputBase->numNewSamplesPerChannel = 0; + + inputBase->inputBuffer.config.numSamplesPerChannel = 0; + inputBase->inputBuffer.config.numChannels = 0; + inputBase->inputBuffer.data = inputBase->bufferData; + + set_zero( inputBase->bufferData, MAX_BUFFER_LENGTH ); + + return; +} + +static IVAS_REND_AudioObjectPosition defaultObjectPosition( + void ) +{ + IVAS_REND_AudioObjectPosition pos; + + pos.azimuth = 0.0f; + pos.elevation = 0.0f; + + return pos; +} + +static rendering_context getRendCtx( + IVAS_REND_HANDLE hIvasRend ) +{ + rendering_context ctx; + + /* Note: when refactoring this, always take the ADDRESS of a member of the + * renderer struct, so that the context stores a POINTER to the member, even + * if the member is a pointer or handle itself. */ + ctx.pOutConfig = &hIvasRend->outputConfig; + ctx.pOutSampleRate = &hIvasRend->sampleRateOut; + ctx.pCustomLsOut = &hIvasRend->customLsOut; + ctx.pEfapOutWrapper = &hIvasRend->efapOutWrapper; + ctx.pHeadRotData = &hIvasRend->headRotData; + + return ctx; +} + +static TDREND_WRAPPER defaultTdRendWrapper( + void ) +{ + TDREND_WRAPPER w; + + w.binaural_latency_ns = 0; + w.hBinRendererTd = NULL; + w.hHrtfTD = NULL; + + return w; +} + +static CREND_WRAPPER defaultCrendWrapper( + void ) +{ + CREND_WRAPPER w; + + w.hCrend = NULL; + w.hHrtfCrend = NULL; + w.binaural_latency_ns = 0; + + return w; +} + +static ivas_error setRendInputActiveIsm( + void *input, + const IVAS_REND_AudioConfig inConfig, + const IVAS_REND_InputId id, + RENDER_CONFIG_DATA *hRendCfg ) +{ + ivas_error error; + rendering_context rendCtx; + IVAS_REND_AudioConfig outConfig; + input_ism *inputIsm; + + inputIsm = (input_ism *) input; + rendCtx = inputIsm->base.ctx; + outConfig = *rendCtx.pOutConfig; + + initRendInputBase( &inputIsm->base, inConfig, id, rendCtx ); + + inputIsm->currentPos = defaultObjectPosition(); + inputIsm->previousPos = defaultObjectPosition(); + inputIsm->crendWrapper = defaultCrendWrapper(); + inputIsm->tdRendWrapper = defaultTdRendWrapper(); + initRotMatrix( inputIsm->rot_mat_prev ); + + error = IVAS_ERR_OK; + if ( outConfig == IVAS_REND_AUDIO_CONFIG_BINAURAL ) + { + error = ivas_rend_TDObjRendOpen( &inputIsm->tdRendWrapper, inConfig, NULL, *rendCtx.pOutSampleRate ); + } + else if ( outConfig == IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM ) + { + error = ivas_rend_openCrend( &inputIsm->crendWrapper, + IVAS_REND_AUDIO_CONFIG_7_1_4, + outConfig, + hRendCfg, + *rendCtx.pOutSampleRate ); + } + if ( error != IVAS_ERR_OK ) + { + return error; + } + + return IVAS_ERR_OK; +} + +static void clearInputIsm( + input_ism *inputIsm ) +{ + rendering_context rendCtx; + + rendCtx = inputIsm->base.ctx; + + initRendInputBase( &inputIsm->base, IVAS_REND_AUDIO_CONFIG_UNKNOWN, 0, rendCtx ); + + /* Free input's internal handles */ + if ( inputIsm->crendWrapper.hCrend != NULL ) + { + ivas_rend_closeCrend( &inputIsm->crendWrapper ); + } + if ( inputIsm->tdRendWrapper.hBinRendererTd != NULL ) + { + ivas_td_binaural_close( &inputIsm->tdRendWrapper.hBinRendererTd ); + inputIsm->tdRendWrapper.hHrtfTD = NULL; + } +} + +static void copyLsConversionMatrixToPanMatrix( + const LS_CONVERSION_MATRIX *lsConvMatrix, + pan_matrix panMatrix ) +{ + int16_t i; + int16_t inCh, outCh; + int16_t numNonZeroGains; + int16_t numColumns; + + /* Index 0 is special and describes the following values */ + numNonZeroGains = lsConvMatrix[0].index; + numColumns = (int16_t) lsConvMatrix[0].value; + + for ( i = 1; i < numNonZeroGains + 1; ++i ) + { + inCh = lsConvMatrix[i].index / numColumns; + outCh = lsConvMatrix[i].index % numColumns; + + panMatrix[inCh][outCh] = lsConvMatrix[i].value; + } + + return; +} + +static void setZeroPanMatrix( + pan_matrix panMatrix ) +{ + int16_t i; + + for ( i = 0; i < MAX_INPUT_CHANNELS; ++i ) + { + set_zero( panMatrix[i], MAX_OUTPUT_CHANNELS ); + } + + return; +} + +/* Note: this only sets non-zero elements, call setZeroPanMatrix() to init first. */ +static void fillIdentityPanMatrix( + pan_matrix panMatrix ) +{ + int16_t i; + + for ( i = 0; i < min( MAX_INPUT_CHANNELS, MAX_OUTPUT_CHANNELS ); ++i ) + { + panMatrix[i][i] = 1.0f; + } +} + +static ivas_error initMcPanGainsWithIdentMatrix( + input_mc *inputMc ) +{ + fillIdentityPanMatrix( inputMc->panGains ); + + return IVAS_ERR_OK; +} + +static ivas_error initMcPanGainsWithConversionMapping( + input_mc *inputMc, + const IVAS_REND_AudioConfig outConfig ) +{ + AUDIO_CONFIG ivasConfigIn, ivasConfigOut; + int16_t i; + + ivasConfigIn = rendAudioConfigToIvasAudioConfig( inputMc->base.inConfig ); + ivasConfigOut = rendAudioConfigToIvasAudioConfig( outConfig ); + + /* Find conversion mapping for current I/O config pair. + * Stay with default panning matrix if conversion_matrix is NULL */ + for ( i = 0; i < LS_SETUP_CONVERSION_NUM_MAPPINGS; ++i ) + { + if ( ls_conversion_mapping[i].input_config == ivasConfigIn && + ls_conversion_mapping[i].output_config == ivasConfigOut ) + { + /* Mapping found with valid matrix - copy */ + if ( ls_conversion_mapping[i].conversion_matrix != NULL ) + { + copyLsConversionMatrixToPanMatrix( ls_conversion_mapping[i].conversion_matrix, inputMc->panGains ); + } + /* Mapping found with NULL matrix - use identity matrix */ + else + { + fillIdentityPanMatrix( inputMc->panGains ); + } + + return IVAS_ERR_OK; + } + } + + return IVAS_ERROR( IVAS_ERR_INTERNAL_FATAL, "Missing multichannel conversion mapping" ); +} + +static ivas_error initMcPanGainsWithEfap( input_mc *inputMc, IVAS_REND_AudioConfig outConfig ) +{ + int16_t i; + int16_t numNonLfeInChannels; + int16_t inLfeChIdx, outChIdx; + const float *spkAzi, *spkEle; + ivas_error error; + + if ( inputMc->base.inConfig != IVAS_REND_AUDIO_CONFIG_LS_CUSTOM ) + { + if ( ( error = getNumNonLfeChannelsInSpeakerLayout( inputMc->base.inConfig, &numNonLfeInChannels ) ) != IVAS_ERR_OK ) + { + return error; + } + if ( ( error = getSpeakerAzimuths( inputMc->base.inConfig, &spkAzi ) ) != IVAS_ERR_OK ) + { + return error; + } + if ( ( error = getSpeakerElevations( inputMc->base.inConfig, &spkEle ) ) != IVAS_ERR_OK ) + { + return error; + } + + inLfeChIdx = LFE_CHANNEL; + } + else + { + numNonLfeInChannels = inputMc->customLsInput.num_spk; + spkAzi = inputMc->customLsInput.ls_azimuth; + spkEle = inputMc->customLsInput.ls_elevation; + inLfeChIdx = -1; + if ( inputMc->customLsInput.num_lfe > 0 ) + { + inLfeChIdx = inputMc->customLsInput.lfe_idx[0]; + } + } + + for ( i = 0, outChIdx = 0; i < numNonLfeInChannels; ++i, ++outChIdx ) + { + if ( i == inLfeChIdx ) + { + ++outChIdx; + } + + if ( ( error = getEfapGains( *inputMc->base.ctx.pEfapOutWrapper, spkAzi[i], spkEle[i], inputMc->panGains[outChIdx] ) ) != IVAS_ERR_OK ) + { + return error; + } + } + + if ( outConfig != IVAS_REND_AUDIO_CONFIG_LS_CUSTOM && inLfeChIdx >= 0 ) + { + inputMc->panGains[inLfeChIdx][LFE_CHANNEL] = 1; + } + else if ( inputMc->base.ctx.pCustomLsOut->num_lfe > 0 && inLfeChIdx >= 0 ) + { + inputMc->panGains[inLfeChIdx][inputMc->base.ctx.pCustomLsOut->lfe_idx[0]] = 1; + } + + return IVAS_ERR_OK; +} + +static ivas_error getRendInputNumChannels( + const void *rendInput, + int16_t *numInChannels ) +{ + /* Using a void pointer for this function to be reusable for any input type (input_ism, input_mc, input_sba). + Assumptions: + - input_base is always the first member in the input struct + */ + + ivas_error error; + const input_base *pInputBase; + const input_mc *pInputMc; + + pInputBase = (const input_base *) rendInput; + + if ( pInputBase->inConfig == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM ) + { + pInputMc = (const input_mc *) rendInput; + *numInChannels = pInputMc->customLsInput.num_spk + pInputMc->customLsInput.num_lfe; + } + else + { + if ( ( error = getAudioConfigNumChannels( pInputBase->inConfig, numInChannels ) ) != IVAS_ERR_OK ) + { + return error; + } + } + + return IVAS_ERR_OK; +} + +static ivas_error initMcPanGainsWithMonoOut( + input_mc *inputMc ) +{ + int16_t i; + int16_t numInChannels; + ivas_error error; + + if ( ( error = getRendInputNumChannels( inputMc, &numInChannels ) ) != IVAS_ERR_OK ) + { + return error; + } + + for ( i = 0; i < numInChannels; ++i ) + { + /* It's OK to also set gain 1 for LFE input channels here. + * Correct LFE handling will be applied within updateMcPanGains() */ + inputMc->panGains[i][0] = 1.f; + } + + return IVAS_ERR_OK; +} + +static ivas_error initMcPanGainsWithStereoLookup( + input_mc *inputMc ) +{ + int16_t readIdx; + int16_t writeIdx; + bool skipSideSpeakers; + int16_t numInChannels; + ivas_error error; + + /* Special case - MONO input. + * Use gains for center CICP speaker and return early. */ + if ( inputMc->base.inConfig == IVAS_REND_AUDIO_CONFIG_MONO ) + { + inputMc->panGains[0][0] = ls_conversion_cicpX_stereo[2][0]; + inputMc->panGains[0][1] = ls_conversion_cicpX_stereo[2][1]; + return IVAS_ERR_OK; + } + + /* ls_conversion_cicpX_stereo contains gains for side speakers. + * These should be skipped with 5.1+X inputs. */ + skipSideSpeakers = false; + if ( inputMc->base.inConfig == IVAS_REND_AUDIO_CONFIG_5_1_2 || inputMc->base.inConfig == IVAS_REND_AUDIO_CONFIG_5_1_4 ) + { + skipSideSpeakers = true; + } + + if ( ( error = getRendInputNumChannels( inputMc, &numInChannels ) ) != IVAS_ERR_OK ) + { + return error; + } + for ( readIdx = 0, writeIdx = 0; writeIdx < numInChannels; ++readIdx, ++writeIdx ) + { + if ( skipSideSpeakers && readIdx == 4 ) + { + /* Skip gains for side speakers in lookup table */ + readIdx += 2; + } + + inputMc->panGains[writeIdx][0] = ls_conversion_cicpX_stereo[readIdx][0]; + inputMc->panGains[writeIdx][1] = ls_conversion_cicpX_stereo[readIdx][1]; + } + + return IVAS_ERR_OK; +} + +/* Returns 1 (true) if configs A and B are equal, otherwise returns 0 (false). + * If both configs are custom LS layouts, layout details are compared to determine equality. */ +static bool configsAreEqual( + IVAS_REND_AudioConfig configA, + LSSETUP_CUSTOM_STRUCT customLsA, + IVAS_REND_AudioConfig configB, + LSSETUP_CUSTOM_STRUCT customLsB ) +{ + int16_t i; + + /* Both input and output are custom LS - compare structs */ + if ( configA == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM && configB == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM ) + { + if ( customLsA.num_spk != customLsB.num_spk ) + { + return false; + } + if ( customLsA.num_lfe != customLsB.num_lfe ) + { + return false; + } + if ( customLsA.is_planar_setup != customLsB.is_planar_setup ) + { + return false; + } + for ( i = 0; i < customLsA.num_spk; ++i ) + { + /* Compare to nearest degree (hence the int cast) */ + if ( (int16_t) customLsA.ls_azimuth[i] != (int16_t) customLsB.ls_azimuth[i] || + (int16_t) customLsA.ls_elevation[i] != (int16_t) customLsB.ls_elevation[i] ) + { + return false; + } + } + for ( i = 0; i < customLsA.num_lfe; ++i ) + { + if ( customLsA.lfe_idx[i] != customLsB.lfe_idx[i] ) + { + return false; + } + } + + return true; + } + + /* Otherwise it's enough to compare config enums */ + return configA == configB; +} + +static ivas_error updateMcPanGainsForMcOut( + input_mc *inputMc, + const IVAS_REND_AudioConfig outConfig ) +{ + ivas_error error; + + /* "if" conditions below realize the following mapping: + + If in == out, use identity matrix, otherwise follow the table: + +-----------+----------+---------------+-----------+--------------------+ + | in\out | MONO | STEREO | custom LS | other | + +-----------+----------+---------------+-----------+--------------------+ + | MONO | mono out | EFAP | EFAP | EFAP | + | custom LS | mono out | EFAP | EFAP | EFAP | + | other | mono out | stereo lookup | EFAP | conversion mapping | + +-----------+----------+---------------+-----------+--------------------+ + */ + + if ( configsAreEqual( inputMc->base.inConfig, inputMc->customLsInput, outConfig, *inputMc->base.ctx.pCustomLsOut ) ) + { + error = initMcPanGainsWithIdentMatrix( inputMc ); + } + else if ( outConfig == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM || + inputMc->base.inConfig == IVAS_REND_AUDIO_CONFIG_MONO || + inputMc->base.inConfig == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM ) + { + error = initMcPanGainsWithEfap( inputMc, outConfig ); + } + else if ( outConfig == IVAS_REND_AUDIO_CONFIG_MONO ) + { + error = initMcPanGainsWithMonoOut( inputMc ); + } + else if ( outConfig == IVAS_REND_AUDIO_CONFIG_STEREO ) + { + error = initMcPanGainsWithStereoLookup( inputMc ); + } + else /* default */ + { + error = initMcPanGainsWithConversionMapping( inputMc, outConfig ); + } + + return error; +} + +static ivas_error updateMcPanGainsForAmbiOut( + input_mc *inputMc, + const IVAS_REND_AudioConfig outConfig ) +{ + int16_t ch_in, ch_out, lfeIdx; + int16_t numNonLfeInChannels, outAmbiOrder; + const float *spkAzi, *spkEle; + ivas_error error; + + if ( ( error = getAmbisonicsOrder( outConfig, &outAmbiOrder ) ) != IVAS_ERR_OK ) + { + return error; + } + + if ( inputMc->base.inConfig != IVAS_REND_AUDIO_CONFIG_LS_CUSTOM ) + { + if ( ( error = getNumNonLfeChannelsInSpeakerLayout( inputMc->base.inConfig, &numNonLfeInChannels ) ) != IVAS_ERR_OK ) + { + return error; + } + if ( ( error = getSpeakerAzimuths( inputMc->base.inConfig, &spkAzi ) ) != IVAS_ERR_OK ) + { + return error; + } + if ( ( error = getSpeakerElevations( inputMc->base.inConfig, &spkEle ) ) != IVAS_ERR_OK ) + { + return error; + } + + for ( ch_in = 0, ch_out = 0; ch_in < numNonLfeInChannels; ++ch_in, ++ch_out ) + { + if ( ch_in == LFE_CHANNEL ) + { + ++ch_out; + } + ivas_dirac_dec_get_response( (int16_t) spkAzi[ch_in], (int16_t) spkEle[ch_in], inputMc->panGains[ch_out], outAmbiOrder ); + } + } + else + { + numNonLfeInChannels = inputMc->customLsInput.num_spk; + spkAzi = inputMc->customLsInput.ls_azimuth; + spkEle = inputMc->customLsInput.ls_elevation; + + for ( ch_in = 0, ch_out = 0; ch_in < numNonLfeInChannels; ++ch_in, ++ch_out ) + { + for ( lfeIdx = 0; lfeIdx < inputMc->customLsInput.num_lfe; ++lfeIdx ) + { + if ( inputMc->customLsInput.lfe_idx[lfeIdx] == ch_in ) + { + ++ch_out; + break; + } + } + + ivas_dirac_dec_get_response( (int16_t) spkAzi[ch_in], (int16_t) spkEle[ch_in], inputMc->panGains[ch_out], outAmbiOrder ); + } + } + + return IVAS_ERR_OK; +} + +static ivas_error updateMcPanGains( + input_mc *inputMc, + const IVAS_REND_AudioConfig outConfig ) +{ + int16_t i; + ivas_error error; + + /* Reset to all zeros - some functions below only write non-zero elements. */ + setZeroPanMatrix( inputMc->panGains ); + + error = IVAS_ERR_OK; + switch ( getAudioConfigType( outConfig ) ) + { + case IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED: + error = updateMcPanGainsForMcOut( inputMc, outConfig ); + break; + case IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS: + error = updateMcPanGainsForAmbiOut( inputMc, outConfig ); + break; + case IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL: + switch ( outConfig ) + { + case IVAS_REND_AUDIO_CONFIG_BINAURAL: + break; /* Do nothing */ + case IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM: + /* Prepare rendering to intermediate format */ + error = updateMcPanGainsForMcOut( inputMc, IVAS_REND_AUDIO_CONFIG_7_1_4 ); + break; + default: + return IVAS_ERR_INVALID_OUTPUT_FORMAT; + } + break; + default: + return IVAS_ERR_INVALID_OUTPUT_FORMAT; + } + /* Check error here to keep switch statement more compact */ + if ( error != IVAS_ERR_OK ) + { + return error; + } + + /* Copy LFE routing to pan gains array */ + if ( inputMc->base.inConfig == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM ) + { + for ( i = 0; i < inputMc->customLsInput.num_lfe; ++i ) + { + mvr2r( inputMc->lfeRouting.lfeOutputGains[i], inputMc->panGains[inputMc->customLsInput.lfe_idx[i]], IVAS_MAX_OUTPUT_CHANNELS ); + } + } + else + { + /* For code simplicity, always copy LFE gains. If config has no LFE, gains will be zero anyway. */ + mvr2r( inputMc->lfeRouting.lfeOutputGains[0], inputMc->panGains[LFE_CHANNEL], IVAS_MAX_OUTPUT_CHANNELS ); + } + + return IVAS_ERR_OK; +} + +#ifndef FIX_I81 +/* Fixes initialization issues in TD renderer. Fix to be merged with branch. + See issue: https://forge.3gpp.org/rep/ivas-codec-pc/ivas-codec/-/issues/81 */ +static void tmpFixBuggyTdBinRendInit( BINAURAL_TD_OBJECT_RENDERER_HANDLE hBinRendererTd ) +{ + int16_t i, j; + + for ( i = 0; i < hBinRendererTd->NumOfSrcs; ++i ) + { + for ( j = 0; j < SPAT_BIN_MAX_INPUT_CHANNELS; ++j ) + { + hBinRendererTd->Sources[i]->SrcRend_p->SfxSpatBin_p[j].LeftFilter_p = NULL; + hBinRendererTd->Sources[i]->SrcRend_p->SfxSpatBin_p[j].LeftFilterIncr_p = NULL; + hBinRendererTd->Sources[i]->SrcRend_p->SfxSpatBin_p[j].RightFilter_p = NULL; + hBinRendererTd->Sources[i]->SrcRend_p->SfxSpatBin_p[j].RightFilterIncr_p = NULL; + } + } +} +#endif + +static ivas_error initMcBinauralRendering( + input_mc *inputMc, + const IVAS_REND_AudioConfig inConfig, + const IVAS_REND_AudioConfig outConfig, + RENDER_CONFIG_DATA *hRendCfg ) +{ + ivas_error error; + int32_t outSampleRate; + + /* check if re-initialization */ + if ( inputMc->tdRendWrapper.hBinRendererTd != NULL ) + { + ivas_td_binaural_close( &inputMc->tdRendWrapper.hBinRendererTd ); + inputMc->tdRendWrapper.hHrtfTD = NULL; + } + if ( inputMc->crendWrapper.hCrend != NULL ) + { + ivas_rend_closeCrend( &inputMc->crendWrapper ); + } + + outSampleRate = *inputMc->base.ctx.pOutSampleRate; + + /* TODO tmu : needs review allocate both renderers; needed if headrotation is toggled so the renderer can be switched */ + // bool initTDRend; + // initTDRend = false; + // if ( outConfig != IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM ) + // { + // if ( inConfig == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM ) + // { + // initTDRend = true; + // } + // else if ( ( inConfig == IVAS_REND_AUDIO_CONFIG_5_1 || inConfig == IVAS_REND_AUDIO_CONFIG_7_1 ) && + // ( inputMc->base.ctx.pHeadRotData->headRotEnabled ) ) + // { + // initTDRend = true; + // } + // } + + // if ( initTDRend ) + { + if ( ( error = ivas_rend_TDObjRendOpen( &inputMc->tdRendWrapper, inConfig, &inputMc->customLsInput, outSampleRate ) ) != IVAS_ERR_OK ) + { + return error; + } +#ifndef FIX_I81 + tmpFixBuggyTdBinRendInit( inputMc->tdRendWrapper.hBinRendererTd ); +#endif + } + + { + if ( ( error = ivas_rend_openCrend( &inputMc->crendWrapper, + ( inConfig == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM ) ? IVAS_REND_AUDIO_CONFIG_7_1_4 : inConfig, + outConfig, + hRendCfg, + outSampleRate ) ) != IVAS_ERR_OK ) + { + return error; + } + } + + return error; +} + +static IVAS_REND_LfeRouting defaultLfeRouting( + IVAS_REND_AudioConfig inConfig, + LSSETUP_CUSTOM_STRUCT customLsIn, + IVAS_REND_AudioConfig outConfig, + LSSETUP_CUSTOM_STRUCT customLsOut ) +{ + int32_t i; + IVAS_REND_LfeRouting routing; + + /* Set all output gains to zero, then route each input LFE consecutively to the next available output LFE. */ + + for ( i = 0; i < IVAS_MAX_INPUT_LFE_CHANNELS; ++i ) + { + set_zero( routing.lfeOutputGains[i], IVAS_MAX_OUTPUT_CHANNELS ); + } + + switch ( inConfig ) + { + case IVAS_REND_AUDIO_CONFIG_5_1: + case IVAS_REND_AUDIO_CONFIG_5_1_2: + case IVAS_REND_AUDIO_CONFIG_5_1_4: + case IVAS_REND_AUDIO_CONFIG_7_1: + case IVAS_REND_AUDIO_CONFIG_7_1_4: + routing.numLfeChannels = 1; + break; + case IVAS_REND_AUDIO_CONFIG_LS_CUSTOM: + routing.numLfeChannels = customLsIn.num_lfe; + break; + default: + routing.numLfeChannels = 0; + } + + switch ( outConfig ) + { + case IVAS_REND_AUDIO_CONFIG_5_1: + case IVAS_REND_AUDIO_CONFIG_5_1_2: + case IVAS_REND_AUDIO_CONFIG_5_1_4: + case IVAS_REND_AUDIO_CONFIG_7_1: + case IVAS_REND_AUDIO_CONFIG_7_1_4: + routing.lfeOutputGains[0][LFE_CHANNEL] = 1.0f; + break; + case IVAS_REND_AUDIO_CONFIG_LS_CUSTOM: + for ( i = 0; i < routing.numLfeChannels && i < customLsOut.num_lfe; ++i ) + { + routing.lfeOutputGains[i][customLsOut.lfe_idx[i]] = 1.0f; + } + break; + default: + /* Do nothing */ + break; + } + + return routing; +} + +static ivas_error setRendInputActiveMc( + void *input, + const IVAS_REND_AudioConfig inConfig, + const IVAS_REND_InputId id, + RENDER_CONFIG_DATA *hRendCfg ) +{ + ivas_error error; + rendering_context rendCtx; + IVAS_REND_AudioConfig outConfig; + input_mc *inputMc; + + inputMc = (input_mc *) input; + rendCtx = inputMc->base.ctx; + outConfig = *rendCtx.pOutConfig; + + initRendInputBase( &inputMc->base, inConfig, id, rendCtx ); + setZeroPanMatrix( inputMc->panGains ); + inputMc->customLsInput = defaultCustomLs(); + inputMc->tdRendWrapper = defaultTdRendWrapper(); + inputMc->crendWrapper = defaultCrendWrapper(); + initRotGains( inputMc->rot_gains_prev ); + inputMc->lfeRouting = defaultLfeRouting( inConfig, inputMc->customLsInput, outConfig, *inputMc->base.ctx.pCustomLsOut ); + + if ( outConfig == IVAS_REND_AUDIO_CONFIG_BINAURAL || outConfig == IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM ) + { + if ( ( error = initMcBinauralRendering( inputMc, inConfig, outConfig, hRendCfg ) ) != IVAS_ERR_OK ) + { + return error; + } + } + + if ( ( error = updateMcPanGains( inputMc, outConfig ) ) != IVAS_ERR_OK ) + { + return error; + } + + return IVAS_ERR_OK; +} + +static void clearInputMc( + input_mc *inputMc ) +{ + rendering_context rendCtx; + + rendCtx = inputMc->base.ctx; + + initRendInputBase( &inputMc->base, IVAS_REND_AUDIO_CONFIG_UNKNOWN, 0, rendCtx ); + + /* Free input's internal handles */ + if ( inputMc->efapInWrapper.hEfap != NULL ) + { + efap_free_data( &inputMc->efapInWrapper.hEfap ); + } + if ( inputMc->crendWrapper.hCrend != NULL ) + { + ivas_rend_closeCrend( &inputMc->crendWrapper ); + } + if ( inputMc->tdRendWrapper.hBinRendererTd != NULL ) + { + ivas_td_binaural_close( &inputMc->tdRendWrapper.hBinRendererTd ); + inputMc->tdRendWrapper.hHrtfTD = NULL; + } + + return; +} + +static ivas_error initSbaPanGainsForMcOut( + input_sba *inputSba, + const IVAS_REND_AudioConfig outConfig, + const LSSETUP_CUSTOM_STRUCT *outSetupCustom ) +{ + int16_t ambiOrderIn; + int16_t chInIdx, chOutIdx; + float *tmpDecMtx, *readPtr; + IVAS_OUTPUT_SETUP hOutSetup; + ivas_error error; + + if ( ( error = getAmbisonicsOrder( inputSba->base.inConfig, &ambiOrderIn ) ) != IVAS_ERR_OK ) + { + return error; + } + + if ( getAudioConfigType( outConfig ) != IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED ) + { + assert( !"Invalid configuration" ); + return IVAS_ERR_WRONG_PARAMS; + } + + switch ( outConfig ) + { + case IVAS_REND_AUDIO_CONFIG_MONO: + hOutSetup.ls_azimuth = ls_azimuth_CICP1; + hOutSetup.ls_elevation = ls_elevation_CICP1; + ivas_output_init( &hOutSetup, rendAudioConfigToIvasAudioConfig( outConfig ) ); + break; + case IVAS_REND_AUDIO_CONFIG_STEREO: + case IVAS_REND_AUDIO_CONFIG_5_1: + case IVAS_REND_AUDIO_CONFIG_7_1: + case IVAS_REND_AUDIO_CONFIG_5_1_2: + case IVAS_REND_AUDIO_CONFIG_5_1_4: + case IVAS_REND_AUDIO_CONFIG_7_1_4: + ivas_output_init( &hOutSetup, rendAudioConfigToIvasAudioConfig( outConfig ) ); + break; + case IVAS_REND_AUDIO_CONFIG_LS_CUSTOM: + ivas_ls_custom_setup( &hOutSetup, outSetupCustom ); + break; + default: + assert( !"Invalid speaker config" ); + return IVAS_ERR_WRONG_PARAMS; + } + + /* obtain and copy over HOA decoding matrix */ + tmpDecMtx = NULL; + if ( ( error = ivas_sba_get_hoa_dec_matrix( hOutSetup, &tmpDecMtx, ambiOrderIn ) ) != IVAS_ERR_OK ) + { + return error; + } + + readPtr = &tmpDecMtx[0]; + for ( chOutIdx = 0; chOutIdx < hOutSetup.nchan_out_woLFE + hOutSetup.num_lfe; ++chOutIdx ) + { + for ( chInIdx = 0; chInIdx < SBA_NHARM_HOA3; ++chInIdx ) + { + if ( hOutSetup.num_lfe > 0 && chOutIdx == hOutSetup.index_lfe[0] ) + { + continue; /* nothing to be rendered to LFE */ + } + inputSba->hoaDecMtx[chInIdx][chOutIdx] = *readPtr++; + } + } + + count_free( tmpDecMtx ); + + return IVAS_ERR_OK; +} + +static ivas_error initSbaPanGainsForSbaOut( + input_sba *inputSba, + const IVAS_REND_AudioConfig outConfig ) +{ + ivas_error error; + error = IVAS_ERR_OK; + + if ( getAudioConfigType( outConfig ) != IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS ) + { + assert( !"Invalid configuration" ); + return IVAS_ERR_WRONG_PARAMS; + } + + fillIdentityPanMatrix( inputSba->hoaDecMtx ); + + return error; +} + +static ivas_error updateSbaPanGains( + input_sba *inputSba, + const IVAS_REND_AudioConfig outConfig, + RENDER_CONFIG_DATA *hRendCfg ) +{ + ivas_error error; + IVAS_REND_AudioConfig inConfig; + rendering_context rendCtx; + + /* Reset to all zeros - some functions below only write non-zero elements. */ + setZeroPanMatrix( inputSba->hoaDecMtx ); + + inConfig = inputSba->base.inConfig; + rendCtx = inputSba->base.ctx; + + switch ( getAudioConfigType( outConfig ) ) + { + case IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED: + error = initSbaPanGainsForMcOut( inputSba, outConfig, inputSba->base.ctx.pCustomLsOut ); + break; + case IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS: + error = initSbaPanGainsForSbaOut( inputSba, outConfig ); + break; + case IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL: + switch ( outConfig ) + { + case IVAS_REND_AUDIO_CONFIG_BINAURAL: + error = ivas_rend_openCrend( &inputSba->crendWrapper, inConfig, outConfig, hRendCfg, *rendCtx.pOutSampleRate ); + break; + case IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM: + if ( ( error = initSbaPanGainsForMcOut( inputSba, IVAS_REND_AUDIO_CONFIG_7_1_4, NULL ) ) != IVAS_ERR_OK ) + { + return error; + } + error = ivas_rend_openCrend( &inputSba->crendWrapper, IVAS_REND_AUDIO_CONFIG_7_1_4, outConfig, hRendCfg, *rendCtx.pOutSampleRate ); + break; + default: + return IVAS_ERR_INVALID_OUTPUT_FORMAT; + } + break; + default: + return IVAS_ERR_INVALID_OUTPUT_FORMAT; + } + /* Check error here to keep switch statement more compact */ + if ( error != IVAS_ERR_OK ) + { + return error; + } + + return IVAS_ERR_OK; +} + +static ivas_error setRendInputActiveSba( + void *input, + const IVAS_REND_AudioConfig inConfig, + const IVAS_REND_InputId id, + RENDER_CONFIG_DATA *hRendCfg ) +{ + ivas_error error; + rendering_context rendCtx; + IVAS_REND_AudioConfig outConfig; + input_sba *inputSba; + + inputSba = (input_sba *) input; + rendCtx = inputSba->base.ctx; + outConfig = *rendCtx.pOutConfig; + + initRendInputBase( &inputSba->base, inConfig, id, rendCtx ); + setZeroPanMatrix( inputSba->hoaDecMtx ); + inputSba->crendWrapper = defaultCrendWrapper(); + initRotGains( inputSba->rot_gains_prev ); + + if ( ( error = updateSbaPanGains( inputSba, outConfig, hRendCfg ) ) != IVAS_ERR_OK ) + { + return error; + } + + return error; +} + +static void clearInputSba( + input_sba *inputSba ) +{ + rendering_context rendCtx; + + rendCtx = inputSba->base.ctx; + + initRendInputBase( &inputSba->base, IVAS_REND_AUDIO_CONFIG_UNKNOWN, 0, rendCtx ); + + /* Free input's internal handles */ + if ( inputSba->crendWrapper.hCrend != NULL ) + { + ivas_rend_closeCrend( &inputSba->crendWrapper ); + } + + return; +} + +ivas_error IVAS_REND_Open( + IVAS_REND_HANDLE *phIvasRend, + const int32_t outputSampleRate, + const IVAS_REND_AudioConfig outConfig ) +{ + int16_t i; + IVAS_REND_HANDLE hIvasRend; + ivas_error error; + int16_t numOutChannels; + + /*-----------------------------------------------------------------* + * Validate function arguments + *-----------------------------------------------------------------*/ + + if ( phIvasRend == NULL ) + { + return IVAS_ERR_UNEXPECTED_NULL_POINTER; + } + if ( ( error = validateOutputAudioConfig( outConfig ) ) != IVAS_ERR_OK ) + { + return error; + } + if ( ( error = validateOutputSampleRate( outputSampleRate, outConfig ) ) != IVAS_ERR_OK ) + { + return error; + } + + *phIvasRend = (IVAS_REND_HANDLE) count_malloc( sizeof( struct IVAS_REND ) ); + if ( *phIvasRend == NULL ) + { + return IVAS_ERR_FAILED_ALLOC; + } + + hIvasRend = *phIvasRend; + hIvasRend->sampleRateOut = outputSampleRate; + hIvasRend->outputConfig = outConfig; + hIvasRend->customLsOut = defaultCustomLs(); + hIvasRend->hLimiter = NULL; + hIvasRend->efapOutWrapper.hEfap = NULL; + hIvasRend->efapOutWrapper.pCustomLsSetup = NULL; +#ifdef DEBUGGING + hIvasRend->numClipping = 0; +#endif + + /* Initialize limiter */ + if ( ( error = IVAS_REND_NumOutChannels( hIvasRend, &numOutChannels ) ) != IVAS_ERR_OK ) + { + return error; + } + initLimiter( &hIvasRend->hLimiter, numOutChannels, outputSampleRate ); + + /* Initialize headrotation data */ + initHeadRotation( hIvasRend ); + + /* Initialize EFAP */ + initEfap( &hIvasRend->efapOutWrapper, outConfig, &hIvasRend->customLsOut ); + + /* Initialize inputs */ + for ( i = 0; i < RENDERER_MAX_ISM_INPUTS; ++i ) + { + initRendInputBase( &hIvasRend->inputsIsm[i].base, IVAS_REND_AUDIO_CONFIG_UNKNOWN, 0, getRendCtx( hIvasRend ) ); + hIvasRend->inputsIsm[i].crendWrapper.hCrend = NULL; + hIvasRend->inputsIsm[i].tdRendWrapper.hBinRendererTd = NULL; + } + for ( i = 0; i < RENDERER_MAX_MC_INPUTS; ++i ) + { + initRendInputBase( &hIvasRend->inputsMc[i].base, IVAS_REND_AUDIO_CONFIG_UNKNOWN, 0, getRendCtx( hIvasRend ) ); + hIvasRend->inputsMc[i].efapInWrapper.hEfap = NULL; + hIvasRend->inputsMc[i].crendWrapper.hCrend = NULL; + hIvasRend->inputsMc[i].tdRendWrapper.hBinRendererTd = NULL; + } + for ( i = 0; i < RENDERER_MAX_SBA_INPUTS; ++i ) + { + initRendInputBase( &hIvasRend->inputsSba[i].base, IVAS_REND_AUDIO_CONFIG_UNKNOWN, 0, getRendCtx( hIvasRend ) ); + hIvasRend->inputsSba[i].crendWrapper.hCrend = NULL; + } + + return IVAS_ERR_OK; +} + +static LSSETUP_CUSTOM_STRUCT makeCustomLsSetup( + const IVAS_CUSTOM_LS_DATA rendCustomLsLayout ) +{ + int16_t i; + LSSETUP_CUSTOM_STRUCT customLs; + + /* Copy layout description */ + customLs.num_spk = rendCustomLsLayout.num_spk; + mvr2r( rendCustomLsLayout.azimuth, customLs.ls_azimuth, rendCustomLsLayout.num_spk ); + mvr2r( rendCustomLsLayout.elevation, customLs.ls_elevation, rendCustomLsLayout.num_spk ); + + customLs.is_planar_setup = 1; + for ( i = 0; i < rendCustomLsLayout.num_spk; ++i ) + { + if ( fabsf( rendCustomLsLayout.elevation[i] ) > EPSILON ) + { + customLs.is_planar_setup = 0; + break; + } + } + + customLs.num_lfe = rendCustomLsLayout.num_lfe; + mvs2s( rendCustomLsLayout.lfe_idx, customLs.lfe_idx, rendCustomLsLayout.num_lfe ); + + return customLs; +} + +static ivas_error validateCustomLsLayout( + const IVAS_CUSTOM_LS_DATA layout ) +{ + int16_t i; + + /* Negative number of speakers or LFEs makes no sense */ + if ( layout.num_spk < 0 || layout.num_lfe < 0 ) + { + return IVAS_ERR_INVALID_CUSTOM_LS_LAYOUT; + } + /* There must be at least one speaker or LFE in the layout */ + if ( layout.num_spk + layout.num_lfe <= 0 ) + { + return IVAS_ERR_INVALID_CUSTOM_LS_LAYOUT; + } + /* LFE indices must be positive */ + for ( i = 0; i < layout.num_lfe; ++i ) + { + if ( layout.lfe_idx[i] < 0 ) + { + return IVAS_ERR_INVALID_CUSTOM_LS_LAYOUT; + } + } + + return IVAS_ERR_OK; +} + +ivas_error IVAS_REND_ConfigureCustomOutputLoudspeakerLayout( + IVAS_REND_HANDLE hIvasRend, + const IVAS_CUSTOM_LS_DATA layout ) +{ + int16_t i, numOutChannels; + ivas_error error; + input_mc *inputMc; + input_sba *inputSba; + + /*-----------------------------------------------------------------* + * Validate function arguments + *-----------------------------------------------------------------*/ + + if ( hIvasRend == NULL ) + { + return IVAS_ERR_UNEXPECTED_NULL_POINTER; + } + if ( hIvasRend->outputConfig != IVAS_REND_AUDIO_CONFIG_LS_CUSTOM ) + { + /* Specifying details of custom speaker layout only makes sense if output config is set to custom speaker layout */ + return IVAS_ERR_INVALID_OUTPUT_FORMAT; + } + if ( ( error = validateCustomLsLayout( layout ) ) != IVAS_ERR_OK ) + { + return error; + } + + hIvasRend->customLsOut = makeCustomLsSetup( layout ); + + /* Re-initialize limiter - number of output channels may have changed */ + if ( ( error = IVAS_REND_NumOutChannels( hIvasRend, &numOutChannels ) ) != IVAS_ERR_OK ) + { + return error; + } + initLimiter( &hIvasRend->hLimiter, numOutChannels, hIvasRend->sampleRateOut ); + + /* Re-initialize EFAP - output layout has changed or has been fully defined for the first time */ + initEfap( &hIvasRend->efapOutWrapper, hIvasRend->outputConfig, &hIvasRend->customLsOut ); + + /* Re-initialize panning gains for each active MC input, This includes re-initializing + * LFE handling for the new output layout, which means custom LFE handling is overwritten, + * if previously set for any MC input. */ + for ( i = 0; i < RENDERER_MAX_MC_INPUTS; ++i ) + { + inputMc = &hIvasRend->inputsMc[i]; + if ( inputMc->base.inConfig == IVAS_REND_AUDIO_CONFIG_UNKNOWN ) + { + /* Input inactive, skip. */ + continue; + } + + inputMc->lfeRouting = defaultLfeRouting( inputMc->base.inConfig, inputMc->customLsInput, hIvasRend->outputConfig, *inputMc->base.ctx.pCustomLsOut ); + + if ( ( error = updateMcPanGains( inputMc, hIvasRend->outputConfig ) ) != IVAS_ERR_OK ) + { + return error; + } + } + + /* Re-initialize panning gains for each active SBA input */ + for ( i = 0; i < RENDERER_MAX_SBA_INPUTS; ++i ) + { + inputSba = &hIvasRend->inputsSba[i]; + if ( inputSba->base.inConfig == IVAS_REND_AUDIO_CONFIG_UNKNOWN ) + { + /* Input inactive, skip. */ + continue; + } + if ( ( error = updateSbaPanGains( inputSba, hIvasRend->outputConfig, hIvasRend->hRendererConfig ) ) != IVAS_ERR_OK ) + { + return error; + } + } + + return IVAS_ERR_OK; +} + +ivas_error IVAS_REND_NumOutChannels( + IVAS_REND_CONST_HANDLE hIvasRend, + int16_t *numOutChannels ) +{ + ivas_error error; + + /*-----------------------------------------------------------------* + * Validate function arguments + *-----------------------------------------------------------------*/ + + if ( hIvasRend == NULL || numOutChannels == NULL ) + { + return IVAS_ERR_UNEXPECTED_NULL_POINTER; + } + + /* Handle special cases where additional info is needed from the renderer, otherwise use getAudioConfigNumChannels() */ + switch ( hIvasRend->outputConfig ) + { + case IVAS_REND_AUDIO_CONFIG_LS_CUSTOM: + *numOutChannels = hIvasRend->customLsOut.num_spk + hIvasRend->customLsOut.num_lfe; + error = IVAS_ERR_OK; + break; + default: + error = getAudioConfigNumChannels( hIvasRend->outputConfig, numOutChannels ); + break; + } + if ( error != IVAS_ERR_OK ) + { + return error; + } + + return IVAS_ERR_OK; +} + +static IVAS_REND_InputId makeInputId( + IVAS_REND_AudioConfig config, + const int32_t inputIndex ) +{ + /* Put config type in second byte (from LSB), put index + 1 in first byte + * + * Index is incremented here so that a valid ID can never be 0. */ + return (IVAS_REND_InputId) ( ( getAudioConfigType( config ) << 8 ) | ( inputIndex + 1 ) ); +} + +static ivas_error getInputById( + IVAS_REND_HANDLE hIvasRend, + IVAS_REND_InputId inputId, + void **ppInput ) +{ + int32_t inputIndex; + IVAS_REND_AudioConfigType configType; + input_base *pInputBase; + + /* Reverse makeInputId() */ + inputIndex = ( inputId & 0xFF ) - 1; + configType = ( inputId & 0xFF00 ) >> 8; + + /* Validate values derived from input ID */ + if ( inputIndex < 0 ) + { + return IVAS_ERR_INVALID_INPUT_ID; + } + switch ( configType ) + { + case IVAS_REND_AUDIO_CONFIG_TYPE_OBJECT_BASED: + if ( inputIndex > RENDERER_MAX_ISM_INPUTS ) + { + return IVAS_ERR_INVALID_INPUT_ID; + } + pInputBase = &hIvasRend->inputsIsm[inputIndex].base; + break; + case IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED: + if ( inputIndex > RENDERER_MAX_MC_INPUTS ) + { + return IVAS_ERR_INVALID_INPUT_ID; + } + pInputBase = &hIvasRend->inputsMc[inputIndex].base; + break; + case IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS: + if ( inputIndex > RENDERER_MAX_SBA_INPUTS ) + { + return IVAS_ERR_INVALID_INPUT_ID; + } + pInputBase = &hIvasRend->inputsSba[inputIndex].base; + break; + default: + return IVAS_ERR_INVALID_INPUT_ID; + } + + /* Ensure input ID matches and that input is active */ + if ( pInputBase->id != inputId || pInputBase->inConfig == IVAS_REND_AUDIO_CONFIG_UNKNOWN ) + { + return IVAS_ERR_INVALID_INPUT_ID; + } + + /* Validation done, set value via output parameter */ + *ppInput = pInputBase; + + return IVAS_ERR_OK; +} + +/* Unfortunately code duplication here is the only way to avoid warnings about const casting */ +static ivas_error getConstInputById( + IVAS_REND_CONST_HANDLE hIvasRend, + const IVAS_REND_InputId inputId, + const void **ppInput ) +{ + int32_t inputIndex; + IVAS_REND_AudioConfigType configType; + const input_base *pInputBase; + + /* Reverse makeInputId() */ + inputIndex = ( inputId & 0xFF ) - 1; + configType = ( inputId & 0xFF00 ) >> 8; + + /* Validate values derived from input ID */ + if ( inputIndex < 0 ) + { + return IVAS_ERR_INVALID_INPUT_ID; + } + switch ( configType ) + { + case IVAS_REND_AUDIO_CONFIG_TYPE_OBJECT_BASED: + if ( inputIndex > RENDERER_MAX_ISM_INPUTS ) + { + return IVAS_ERR_INVALID_INPUT_ID; + } + pInputBase = &hIvasRend->inputsIsm[inputIndex].base; + break; + case IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED: + if ( inputIndex > RENDERER_MAX_MC_INPUTS ) + { + return IVAS_ERR_INVALID_INPUT_ID; + } + pInputBase = &hIvasRend->inputsMc[inputIndex].base; + break; + case IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS: + if ( inputIndex > RENDERER_MAX_SBA_INPUTS ) + { + return IVAS_ERR_INVALID_INPUT_ID; + } + pInputBase = &hIvasRend->inputsSba[inputIndex].base; + break; + default: + return IVAS_ERR_INVALID_INPUT_ID; + } + + /* Ensure input ID matches and that input is active */ + if ( pInputBase->id != inputId || pInputBase->inConfig == IVAS_REND_AUDIO_CONFIG_UNKNOWN ) + { + return IVAS_ERR_INVALID_INPUT_ID; + } + + /* Validation done, set value via output parameter */ + *ppInput = pInputBase; + + return IVAS_ERR_OK; +} + +static ivas_error findFreeInputSlot( + const void *inputs, + int32_t inputStructSize, + int32_t maxInputs, + int32_t *inputIndex ) +{ + /* Using a void pointer and a separately provided size is a hack for this function + to be reusable for arrays of any input type (input_ism, input_mc, input_sba). + Assumptions: + - input_base is always the first member in the input struct + - provided size is correct + */ + + int32_t i; + bool canAddInput; + const uint8_t *pByte; + const input_base *pInputBase; + + canAddInput = false; + + /* Find first unused input in array */ + for ( i = 0, pByte = inputs; i < maxInputs; ++i, pByte += inputStructSize ) + { + pInputBase = (const input_base *) pByte; + + if ( pInputBase->inConfig == IVAS_REND_AUDIO_CONFIG_UNKNOWN ) + { + *inputIndex = i; + canAddInput = true; + break; + } + } + + if ( !canAddInput ) + { + return IVAS_ERR_TOO_MANY_INPUTS; + } + + return IVAS_ERR_OK; +} + +ivas_error IVAS_REND_AddInput( + IVAS_REND_HANDLE hIvasRend, + IVAS_REND_AudioConfig inConfig, + IVAS_REND_InputId *inputId ) +{ + ivas_error error; + int32_t maxNumInputsOfType; + void *inputsArray; + int32_t inputStructSize; + ivas_error ( *activateInput )( void *, IVAS_REND_AudioConfig, IVAS_REND_InputId, RENDER_CONFIG_DATA * ); + int32_t inputIndex; + + /*-----------------------------------------------------------------* + * Validate function arguments + *-----------------------------------------------------------------*/ + + if ( hIvasRend == NULL || inputId == NULL ) + { + return IVAS_ERR_UNEXPECTED_NULL_POINTER; + } + + switch ( getAudioConfigType( inConfig ) ) + { + case IVAS_REND_AUDIO_CONFIG_TYPE_OBJECT_BASED: + maxNumInputsOfType = RENDERER_MAX_ISM_INPUTS; + inputsArray = hIvasRend->inputsIsm; + inputStructSize = sizeof( *hIvasRend->inputsIsm ); + activateInput = setRendInputActiveIsm; + break; + case IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED: + maxNumInputsOfType = RENDERER_MAX_MC_INPUTS; + inputsArray = hIvasRend->inputsMc; + inputStructSize = sizeof( *hIvasRend->inputsMc ); + activateInput = setRendInputActiveMc; + break; + case IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS: + maxNumInputsOfType = RENDERER_MAX_SBA_INPUTS; + inputsArray = hIvasRend->inputsSba; + inputStructSize = sizeof( *hIvasRend->inputsSba ); + activateInput = setRendInputActiveSba; + break; + default: + return IVAS_ERR_INVALID_INPUT_FORMAT; + } + + /* Find first free input in array corresponding to input type */ + if ( ( error = findFreeInputSlot( inputsArray, inputStructSize, maxNumInputsOfType, &inputIndex ) ) != IVAS_ERR_OK ) + { + return error; + } + + *inputId = makeInputId( inConfig, inputIndex ); + + if ( ( error = activateInput( (uint8_t *) inputsArray + inputStructSize * inputIndex, inConfig, *inputId, hIvasRend->hRendererConfig ) ) != IVAS_ERR_OK ) + { + return error; + } + + return IVAS_ERR_OK; +} + +ivas_error IVAS_REND_ConfigureCustomInputLoudspeakerLayout( + IVAS_REND_HANDLE hIvasRend, + const IVAS_REND_InputId inputId, + const IVAS_CUSTOM_LS_DATA layout ) +{ + input_mc *inputMc; + ivas_error error; + + /*-----------------------------------------------------------------* + * Validate function arguments + *-----------------------------------------------------------------*/ + + if ( hIvasRend == NULL ) + { + return IVAS_ERR_UNEXPECTED_NULL_POINTER; + } + if ( ( error = validateCustomLsLayout( layout ) ) != IVAS_ERR_OK ) + { + return error; + } + if ( ( error = getInputById( hIvasRend, inputId, (void **) &inputMc ) ) != IVAS_ERR_OK ) + { + return error; + } + if ( inputMc->base.inConfig != IVAS_REND_AUDIO_CONFIG_LS_CUSTOM ) + { + /* Specifying details of custom speaker layout only makes sense if input config is set to custom speaker layout */ + return IVAS_ERR_INVALID_INPUT_FORMAT; + } + + /* Re-initialize panning gains for the MC input, This includes re-initializing LFE handling + * for the new input layout, which means custom LFE handling is overwritten, if previously + * set for the MC input. */ + inputMc->customLsInput = makeCustomLsSetup( layout ); + + inputMc->lfeRouting = defaultLfeRouting( inputMc->base.inConfig, inputMc->customLsInput, hIvasRend->outputConfig, *inputMc->base.ctx.pCustomLsOut ); + + initEfap( &inputMc->efapInWrapper, inputMc->base.inConfig, &inputMc->customLsInput ); + + if ( hIvasRend->outputConfig == IVAS_REND_AUDIO_CONFIG_BINAURAL || hIvasRend->outputConfig == IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM ) + { + if ( ( error = initMcBinauralRendering( inputMc, inputMc->base.inConfig, hIvasRend->outputConfig, hIvasRend->hRendererConfig ) ) != IVAS_ERR_OK ) + { + return error; + } + } + if ( ( error = updateMcPanGains( inputMc, hIvasRend->outputConfig ) ) != IVAS_ERR_OK ) + { + return error; + } + + return IVAS_ERR_OK; +} + +ivas_error IVAS_REND_SetInputGain( + IVAS_REND_HANDLE hIvasRend, + const IVAS_REND_InputId inputId, + const float gain /* linear gain, not in dB */ +) +{ + input_base *inputBase; + ivas_error error; + + /*-----------------------------------------------------------------* + * Validate function arguments + *-----------------------------------------------------------------*/ + + if ( hIvasRend == NULL ) + { + return IVAS_ERR_UNEXPECTED_NULL_POINTER; + } + if ( ( error = getInputById( hIvasRend, inputId, (void **) &inputBase ) ) != IVAS_ERR_OK ) + { + return error; + } + + inputBase->gain = gain; + + return IVAS_ERR_OK; +} + +static int32_t getNumLfeChannels( + input_mc *inputMc ) +{ + switch ( inputMc->base.inConfig ) + { + case IVAS_REND_AUDIO_CONFIG_5_1: + case IVAS_REND_AUDIO_CONFIG_7_1: + case IVAS_REND_AUDIO_CONFIG_5_1_2: + case IVAS_REND_AUDIO_CONFIG_5_1_4: + case IVAS_REND_AUDIO_CONFIG_7_1_4: + return 1; + case IVAS_REND_AUDIO_CONFIG_LS_CUSTOM: + return inputMc->customLsInput.num_lfe; + default: + break; + } + + return 0; +} + +ivas_error IVAS_REND_SetInputLfeRouting( + IVAS_REND_HANDLE hIvasRend, + const IVAS_REND_InputId inputId, + const IVAS_REND_LfeRouting lfeRouting ) +{ + input_base *pInputBase; + input_mc *pInputMc; + ivas_error error; + + /*-----------------------------------------------------------------* + * Validate function arguments + *-----------------------------------------------------------------*/ + + if ( hIvasRend == NULL ) + { + return IVAS_ERR_UNEXPECTED_NULL_POINTER; + } + if ( ( error = getInputById( hIvasRend, inputId, (void **) &pInputBase ) ) != IVAS_ERR_OK ) + { + return error; + } + if ( getAudioConfigType( pInputBase->inConfig ) != IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED ) + { + /* Custom LFE routing only makes sense with channel-based input */ + return IVAS_ERR_INVALID_INPUT_FORMAT; + } + pInputMc = (input_mc *) pInputBase; + + if ( getNumLfeChannels( pInputMc ) != lfeRouting.numLfeChannels ) + { + return IVAS_ERR_WRONG_NUM_CHANNELS; + } + + pInputMc->lfeRouting = lfeRouting; + if ( ( error = updateMcPanGains( pInputMc, hIvasRend->outputConfig ) ) != IVAS_ERR_OK ) + { + return error; + } + + return IVAS_ERR_OK; +} + +ivas_error IVAS_REND_RemoveInput( + IVAS_REND_HANDLE hIvasRend, + const IVAS_REND_InputId inputId ) +{ + ivas_error error; + input_base *inputBase; + + /*-----------------------------------------------------------------* + * Validate function arguments + *-----------------------------------------------------------------*/ + + if ( hIvasRend == NULL ) + { + return IVAS_ERR_UNEXPECTED_NULL_POINTER; + } + if ( ( error = getInputById( hIvasRend, inputId, (void **) &inputBase ) ) != IVAS_ERR_OK ) + { + return error; + } + + switch ( getAudioConfigType( inputBase->inConfig ) ) + { + case IVAS_REND_AUDIO_CONFIG_TYPE_OBJECT_BASED: + clearInputIsm( (input_ism *) inputBase ); + break; + case IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED: + clearInputMc( (input_mc *) inputBase ); + break; + case IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS: + clearInputSba( (input_sba *) inputBase ); + break; + default: + return IVAS_ERR_INVALID_INPUT_FORMAT; + } + + return IVAS_ERR_OK; +} + +ivas_error IVAS_REND_GetInputNumChannels( + IVAS_REND_CONST_HANDLE hIvasRend, + const IVAS_REND_InputId inputId, + int16_t *numChannels ) +{ + ivas_error error; + const input_base *pInput; + + /*-----------------------------------------------------------------* + * Validate function arguments + *-----------------------------------------------------------------*/ + + if ( hIvasRend == NULL || numChannels == NULL ) + { + return IVAS_ERR_UNEXPECTED_NULL_POINTER; + } + + if ( ( error = getConstInputById( hIvasRend, inputId, (const void **) &pInput ) ) != IVAS_ERR_OK ) + { + return error; + } + if ( ( error = getRendInputNumChannels( pInput, numChannels ) ) != IVAS_ERR_OK ) + { + return error; + } + + return IVAS_ERR_OK; +} + +ivas_error IVAS_REND_GetDelay( + IVAS_REND_CONST_HANDLE hIvasRend, /* i : Renderer state */ + int16_t *nSamples, /* o : Renderer delay in samples */ + int32_t *timeScale /* o : Time scale of the delay, equal to renderer output sampling rate */ +) +{ + /* TODO tmu : this function only returns the maximum delay across all inputs + * Ideally each input has its own delay buffer and everything is aligned (binaural and LFE filtering delays are nonuniform) + */ + int16_t i; + int32_t latency_ns; + + /*-----------------------------------------------------------------* + * Validate function arguments + *-----------------------------------------------------------------*/ + + if ( hIvasRend == NULL || nSamples == NULL || timeScale == NULL ) + { + return IVAS_ERR_UNEXPECTED_NULL_POINTER; + } + + *timeScale = hIvasRend->sampleRateOut; + *nSamples = 0; + + /* Compute the maximum delay across all inputs */ + for ( i = 0; i < RENDERER_MAX_ISM_INPUTS; i++ ) + { + if ( hIvasRend->inputsIsm[i].base.inConfig != IVAS_REND_AUDIO_CONFIG_UNKNOWN ) + { + latency_ns = max( hIvasRend->inputsIsm[i].crendWrapper.binaural_latency_ns, + hIvasRend->inputsIsm[i].tdRendWrapper.binaural_latency_ns ); + *nSamples = max( *nSamples, NS2SA( *timeScale, latency_ns ) ); + } + } + + for ( i = 0; i < RENDERER_MAX_MC_INPUTS; i++ ) + { + if ( hIvasRend->inputsMc[i].base.inConfig != IVAS_REND_AUDIO_CONFIG_UNKNOWN ) + { + latency_ns = max( hIvasRend->inputsMc[i].crendWrapper.binaural_latency_ns, + hIvasRend->inputsMc[i].tdRendWrapper.binaural_latency_ns ); + *nSamples = max( *nSamples, NS2SA( *timeScale, latency_ns ) ); + } + } + + for ( i = 0; i < RENDERER_MAX_SBA_INPUTS; i++ ) + { + if ( hIvasRend->inputsSba[i].base.inConfig != IVAS_REND_AUDIO_CONFIG_UNKNOWN ) + { + latency_ns = hIvasRend->inputsSba[i].crendWrapper.binaural_latency_ns; + *nSamples = max( *nSamples, NS2SA( *timeScale, latency_ns ) ); + } + } + + return IVAS_ERR_OK; +} + +ivas_error IVAS_REND_FeedInputAudio( + IVAS_REND_HANDLE hIvasRend, + const IVAS_REND_InputId inputId, + const IVAS_REND_ReadOnlyAudioBuffer inputAudio ) +{ + ivas_error error; + input_base *inputBase; + int16_t numInputChannels; + + /*-----------------------------------------------------------------* + * Validate function arguments + *-----------------------------------------------------------------*/ + + if ( hIvasRend == NULL || inputAudio.data == NULL ) + { + return IVAS_ERR_UNEXPECTED_NULL_POINTER; + } + if ( inputAudio.config.numSamplesPerChannel <= 0 || MAX_BUFFER_LENGTH_PER_CHANNEL < inputAudio.config.numSamplesPerChannel ) + { + return IVAS_ERR_INVALID_BUFFER_SIZE; + } + if ( inputAudio.config.numChannels <= 0 || MAX_INPUT_CHANNELS < inputAudio.config.numChannels ) + { + return IVAS_ERR_WRONG_NUM_CHANNELS; + } + if ( getAudioConfigType( hIvasRend->outputConfig ) == IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL && + inputAudio.config.numSamplesPerChannel * 1000 != BINAURAL_RENDERING_FRAME_SIZE_MS * hIvasRend->sampleRateOut ) + { + /* Binaural rendering requires specific frame size */ + return IVAS_ERR_INVALID_BUFFER_SIZE; + } + + if ( ( error = getInputById( hIvasRend, inputId, (void **) &inputBase ) ) != IVAS_ERR_OK ) + { + return error; + } + if ( ( error = getRendInputNumChannels( inputBase, &numInputChannels ) ) != IVAS_ERR_OK ) + { + return error; + } + if ( numInputChannels != inputAudio.config.numChannels ) + { + return IVAS_ERR_WRONG_NUM_CHANNELS; + } + + inputBase->inputBuffer.config = inputAudio.config; + + mvr2r( inputAudio.data, inputBase->inputBuffer.data, inputAudio.config.numSamplesPerChannel * inputAudio.config.numChannels ); + + inputBase->numNewSamplesPerChannel = inputAudio.config.numSamplesPerChannel; + + return IVAS_ERR_OK; +} + +ivas_error IVAS_REND_FeedInputObjectMetadata( + IVAS_REND_HANDLE hIvasRend, + const IVAS_REND_InputId inputId, + const IVAS_REND_AudioObjectPosition objectPosition ) +{ + input_base *inputBase; + input_ism *inputIsm; + ivas_error error; + + /*-----------------------------------------------------------------* + * Validate function arguments + *-----------------------------------------------------------------*/ + + if ( hIvasRend == NULL ) + { + return IVAS_ERR_UNEXPECTED_NULL_POINTER; + } + if ( ( error = getInputById( hIvasRend, inputId, (void **) &inputBase ) ) != IVAS_ERR_OK ) + { + return error; + } + if ( inputBase->inConfig != IVAS_REND_AUDIO_CONFIG_OBJECT ) + { + /* Object metadata should only be fed for object inputs */ + return IVAS_ERR_METADATA_NOT_EXPECTED; + } + + inputIsm = (input_ism *) inputBase; + inputIsm->previousPos = inputIsm->currentPos; + inputIsm->currentPos = objectPosition; + + return IVAS_ERR_OK; +} + +ivas_error IVAS_REND_InitConfig( IVAS_REND_HANDLE st, + bool rendererConfigEnabled ) +{ + ivas_error error; + + if ( rendererConfigEnabled ) + { + st->rendererConfigEnabled = 1; + } + else + { + st->rendererConfigEnabled = 0; + } + + if ( ( error = ivas_render_config_open( &( st->hRendererConfig ) ) ) != IVAS_ERR_OK ) + { + return error; + } + + if ( ivas_render_config_init_from_rom( &st->hRendererConfig, st->rendererConfigEnabled ) != IVAS_ERR_OK ) + { + return IVAS_ERR_INTERNAL_FATAL; + } + + return IVAS_ERR_OK; +} + +int16_t IVAS_REND_GetRenderConfig( + IVAS_REND_HANDLE hIvasRend, /* i/o: IVAS decoder handle */ + const IVAS_RENDER_CONFIG_HANDLE hRCout /* o : Render configuration handle */ +) +{ + RENDER_CONFIG_HANDLE hRCin; + + if ( hIvasRend == NULL || hIvasRend->hRendererConfig == NULL || hRCout == NULL ) + { + return IVAS_ERR_UNEXPECTED_NULL_POINTER; + } + + hRCin = hIvasRend->hRendererConfig; +#ifdef DEBUGGING + switch ( hRCin->renderer_type_override ) + { + case RENDER_TYPE_OVERRIDE_CREND: + hRCout->renderer_type_override = IVAS_RENDER_TYPE_OVERRIDE_CREND; + break; + case RENDER_TYPE_OVERRIDE_FASTCONV: + hRCout->renderer_type_override = IVAS_RENDER_TYPE_OVERRIDE_FASTCONV; + break; + default: + hRCout->renderer_type_override = IVAS_RENDER_TYPE_OVERRIDE_NONE; + break; + } +#endif + hRCout->room_acoustics.override = hRCin->roomAcoustics.override; + hRCout->room_acoustics.use_brir = hRCin->roomAcoustics.use_brir; + hRCout->room_acoustics.late_reverb_on = hRCin->roomAcoustics.late_reverb_on; + hRCout->room_acoustics.nBands = hRCin->roomAcoustics.nBands; + hRCout->room_acoustics.acousticPreDelay = hRCin->roomAcoustics.acousticPreDelay; + hRCout->room_acoustics.inputPreDelay = hRCin->roomAcoustics.inputPreDelay; + + mvr2r( hRCin->roomAcoustics.pFc_input, hRCout->room_acoustics.pFc_input, CLDFB_NO_CHANNELS_MAX ); + mvr2r( hRCin->roomAcoustics.pAcoustic_rt60, hRCout->room_acoustics.pAcoustic_rt60, CLDFB_NO_CHANNELS_MAX ); + mvr2r( hRCin->roomAcoustics.pAcoustic_dsr, hRCout->room_acoustics.pAcoustic_dsr, CLDFB_NO_CHANNELS_MAX ); + + return IVAS_ERR_OK; +} + + +int16_t IVAS_REND_FeedRenderConfig( + IVAS_REND_HANDLE hIvasRend, /* i/o: IVAS decoder handle */ + const IVAS_RENDER_CONFIG_DATA renderConfig /* i : Render configuration struct */ +) +{ + RENDER_CONFIG_HANDLE hRenderConfig; + + if ( hIvasRend == NULL || hIvasRend->hRendererConfig == NULL ) + { + return IVAS_ERR_UNEXPECTED_NULL_POINTER; + } + + hRenderConfig = hIvasRend->hRendererConfig; +#ifdef DEBUGGING + hRenderConfig->renderer_type_override = RENDER_TYPE_OVERRIDE_NONE; + if ( renderConfig.renderer_type_override == IVAS_RENDER_TYPE_OVERRIDE_FASTCONV ) + { + hRenderConfig->renderer_type_override = RENDER_TYPE_OVERRIDE_FASTCONV; + } + if ( renderConfig.renderer_type_override == IVAS_RENDER_TYPE_OVERRIDE_CREND ) + { + hRenderConfig->renderer_type_override = RENDER_TYPE_OVERRIDE_CREND; + } +#endif + hRenderConfig->roomAcoustics.override = renderConfig.room_acoustics.override; + hRenderConfig->roomAcoustics.use_brir = renderConfig.room_acoustics.use_brir; + hRenderConfig->roomAcoustics.late_reverb_on = renderConfig.room_acoustics.late_reverb_on; + hRenderConfig->roomAcoustics.nBands = renderConfig.room_acoustics.nBands; + hRenderConfig->roomAcoustics.acousticPreDelay = renderConfig.room_acoustics.acousticPreDelay; + hRenderConfig->roomAcoustics.inputPreDelay = renderConfig.room_acoustics.inputPreDelay; + mvr2r( renderConfig.room_acoustics.pFc_input, hRenderConfig->roomAcoustics.pFc_input, CLDFB_NO_CHANNELS_MAX ); + mvr2r( renderConfig.room_acoustics.pAcoustic_rt60, hRenderConfig->roomAcoustics.pAcoustic_rt60, CLDFB_NO_CHANNELS_MAX ); + mvr2r( renderConfig.room_acoustics.pAcoustic_dsr, hRenderConfig->roomAcoustics.pAcoustic_dsr, CLDFB_NO_CHANNELS_MAX ); + + return IVAS_ERR_OK; +} + + +ivas_error IVAS_REND_SetHeadRotation( + IVAS_REND_HANDLE hIvasRend, + const IVAS_QUATERNION headRot[RENDERER_HEAD_POSITIONS_PER_FRAME] ) +{ + int16_t i; + + /*-----------------------------------------------------------------* + * Validate function arguments + *-----------------------------------------------------------------*/ + + if ( hIvasRend == NULL ) + { + return IVAS_ERR_UNEXPECTED_NULL_POINTER; + } + if ( getAudioConfigType( hIvasRend->outputConfig ) != IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL ) + { + /* Head rotation can be set only with binaural output */ + return IVAS_ERR_METADATA_NOT_EXPECTED; + } + + if ( headRot == NULL ) + { + hIvasRend->headRotData.headRotEnabled = 0; + } + else + { + hIvasRend->headRotData.headRotEnabled = 1; + for ( i = 0; i < RENDERER_HEAD_POSITIONS_PER_FRAME; ++i ) + { + hIvasRend->headRotData.headPositions[i] = headRot[i]; + } + } + + return IVAS_ERR_OK; +} + +/* Take one channel from input buffer and copy it to each channel + in output buffer, with different gain applied per output channel. + This function takes 2 gain vectors - one for the beginning and one + for the end of the buffer. Gain values are lineraly interpolated + for all samples in between. */ +static void renderBufferChannelLerp( + const IVAS_REND_AudioBuffer inAudio, + const int32_t inChannelIdx, + const float *const gainsCurrent, + const float *const gainsPrev, + IVAS_REND_AudioBuffer outAudio ) +{ + const float *inSmpl; + float *outSmpl; + float fadeIn; + float fadeOut; + int32_t i; + const float *lastInSmpl; + int16_t outChnlIdx; + float currentGain; + float previousGain; + + /* Pointer to behind last input sample */ + lastInSmpl = getSmplPtr( inAudio, inChannelIdx, inAudio.config.numSamplesPerChannel ); + + for ( outChnlIdx = 0; outChnlIdx < outAudio.config.numChannels; ++outChnlIdx ) + { + currentGain = gainsCurrent[outChnlIdx]; + previousGain = gainsPrev == NULL ? 0.f : gainsPrev[outChnlIdx]; + + /* Process current output channel only if applying non-zero gains */ + if ( fabsf( currentGain ) > EPSILON || ( gainsPrev != NULL && fabsf( previousGain ) > EPSILON ) ) + { + /* Reset input pointer to the beginning of input channel */ + inSmpl = getSmplPtr( inAudio, inChannelIdx, 0 ); + + /* Set output pointer to first output channel sample */ + outSmpl = getSmplPtr( outAudio, outChnlIdx, 0 ); + + if ( gainsPrev == NULL || fabsf( previousGain - currentGain ) <= EPSILON ) + { + /* If no interpolation from previous frame, apply current gain */ + do + { + *outSmpl += currentGain * ( *inSmpl ); + ++outSmpl; + ++inSmpl; + + } while ( inSmpl != lastInSmpl ); + } + else + { + i = 0; + + /* Otherwise use weighted average between previous and current gain */ + do + { + /* TODO(sgi): This is calculated for each output channel - can be optimised */ + fadeIn = (float) i / ( outAudio.config.numSamplesPerChannel - 1 ); + fadeOut = 1.0f - fadeIn; + + *outSmpl += ( fadeIn * currentGain + fadeOut * previousGain ) * ( *inSmpl ); + ++outSmpl; + ++inSmpl; + ++i; + } while ( inSmpl != lastInSmpl ); + } + } + } +} + +/* Take one channel from input buffer and copy it to each channel + in output buffer, with different gain applied per output channel */ +static void renderBufferChannel( + const IVAS_REND_AudioBuffer inAudio, + const int32_t inChannelIdx, + const float *const outputGains, + IVAS_REND_AudioBuffer outAudio ) +{ + renderBufferChannelLerp( inAudio, inChannelIdx, outputGains, NULL, outAudio ); + + return; +} + +static ivas_error rotateFrameMc( + IVAS_REND_AudioBuffer inAudio, /* i : Input Audio buffer */ + IVAS_REND_AudioConfig inConfig, /* i : Input Audio config */ + LSSETUP_CUSTOM_STRUCT inCustomLs, /* i : Input Custom LS setup */ + const IVAS_REND_HeadRotData *headRotData, /* i : Head rotation data */ + rotation_gains gains_prev, /* i/o: Previous frame rotation gains */ + const EFAP_HANDLE hEFAPdata, /* i : EFAP structure */ + IVAS_REND_AudioBuffer outAudio /* o : Output Audio buffer */ +) +{ + int16_t i; + int16_t subframe_idx, subframe_len; + int16_t azimuth, elevation; + int16_t is_planar_setup, lfe_idx; + int16_t nchan; + int16_t ch_in, ch_out; + int16_t ch_in_woLFE, ch_out_woLFE; + float *readPtr, *writePtr; + const float *ls_azimuth, *ls_elevation; + rotation_matrix Rmat; + rotation_gains gains; + float tmp_gains[MAX_INPUT_CHANNELS]; + + wmops_sub_start( "rotateFrameMc" ); + + if ( inConfig != IVAS_REND_AUDIO_CONFIG_LS_CUSTOM ) + { + getAudioConfigNumChannels( inConfig, &nchan ); + } + else + { + nchan = inCustomLs.num_spk + inCustomLs.num_lfe; + } + + getMcConfigValues( inConfig, inCustomLs, &ls_azimuth, &ls_elevation, &lfe_idx, &is_planar_setup ); + + /* initialize gains to passthrough */ + for ( ch_in = 0; ch_in < nchan; ch_in++ ) + { + set_zero( gains[ch_in], (int16_t) nchan ); + gains[ch_in][ch_in] = 1.f; + } + + /* subframe loop */ + subframe_len = inAudio.config.numSamplesPerChannel / RENDERER_HEAD_POSITIONS_PER_FRAME; + for ( subframe_idx = 0; subframe_idx < RENDERER_HEAD_POSITIONS_PER_FRAME; subframe_idx++ ) + { + /* Get next quaternion and calculate rotation matrix */ + QuatToRotMat( headRotData->headPositions[subframe_idx], Rmat ); + + for ( ch_in = 0; ch_in < nchan; ch_in++ ) + { + /* skip LFE */ + if ( ch_in == lfe_idx ) + { + continue; + } + + /* input channel index without LFE */ + ch_in_woLFE = ( ( lfe_idx > 0 ) && ( ch_in >= lfe_idx ) ) ? ch_in - 1 : ch_in; + + /* gains for current subframe rotation */ + rotateAziEle( ls_azimuth[ch_in_woLFE], ls_elevation[ch_in_woLFE], &azimuth, &elevation, Rmat, is_planar_setup ); + + if ( hEFAPdata != NULL && ( ls_azimuth[ch_in_woLFE] != azimuth || ls_elevation[ch_in_woLFE] != elevation ) ) + { + efap_determine_gains( hEFAPdata, tmp_gains, azimuth, elevation, EFAP_MODE_EFAP ); + + for ( ch_out = 0; ch_out < nchan; ch_out++ ) + { + /* skip LFE */ + if ( ch_out == lfe_idx ) + { + continue; + } + + /* output channel index without LFE */ + ch_out_woLFE = ( ( lfe_idx > 0 ) && ( ch_out >= lfe_idx ) ) ? ch_out - 1 : ch_out; + + gains[ch_in][ch_out] = tmp_gains[ch_out_woLFE]; + } + } + } + + /* apply panning gains by mtx multiplication */ + for ( ch_out = 0; ch_out < nchan; ch_out++ ) + { + for ( ch_in = 0; ch_in < nchan; ch_in++ ) + { + writePtr = getSmplPtr( outAudio, ch_out, subframe_idx * subframe_len ); + readPtr = getSmplPtr( inAudio, ch_in, subframe_idx * subframe_len ); + /* crossfade with previous rotation gains */ + for ( i = 0; i < subframe_len; i++ ) + { + *writePtr++ += + ( *readPtr ) * ( ( 1 - headRotData->crossfade[i] ) * gains_prev[ch_in][ch_out] ) + + ( *readPtr ) * ( headRotData->crossfade[i] * gains[ch_in][ch_out] ); + readPtr++; + } + } + } + + /* move gains to gains_prev */ + for ( i = 0; i < nchan; i++ ) + { + mvr2r( gains[i], gains_prev[i], (int16_t) nchan ); + } + } + + wmops_sub_end(); + + return IVAS_ERR_OK; +} + +static ivas_error rotateFrameSba( + IVAS_REND_AudioBuffer inAudio, /* i : Input Audio buffer */ + IVAS_REND_AudioConfig inConfig, /* i : Input Audio config */ + const IVAS_REND_HeadRotData *headRotData, /* i : Head rotation data */ + rotation_gains gains_prev, /* i/o: Previous frame rotation gains */ + IVAS_REND_AudioBuffer outAudio /* o : Output Audio buffer */ +) +{ + int16_t i, l, n, m; + int16_t m1, m2; + int16_t shd_rot_max_order; + int16_t subframe_idx, subframe_len; + float *readPtr, *writePtr; + rotation_matrix Rmat; + float tmpRot[2 * HEADROT_ORDER + 1]; + rotation_gains gains; + + wmops_sub_start( "rotateFrameSba" ); + + getAmbisonicsOrder( inConfig, &shd_rot_max_order ); + + /* subframe loop */ + subframe_len = inAudio.config.numSamplesPerChannel / RENDERER_HEAD_POSITIONS_PER_FRAME; + for ( subframe_idx = 0; subframe_idx < RENDERER_HEAD_POSITIONS_PER_FRAME; subframe_idx++ ) + { + /* initialize rotation matrices with zeros */ + for ( i = 0; i < HEADROT_SHMAT_DIM; i++ ) + { + set_zero( gains[i], HEADROT_SHMAT_DIM ); + } + + /* Get next quaternion and calculate rotation matrix */ + QuatToRotMat( headRotData->headPositions[subframe_idx], Rmat ); + + /* calculate ambisonics rotation matrices for the previous and current frames */ + SHrotmatgen( gains, Rmat, shd_rot_max_order ); + + for ( i = 0; i < subframe_len; i++ ) + { + /* As the rotation matrix becomes block diagonal in a SH basis, we can*/ + /* apply each angular-momentum block individually to save complexity. */ + + /* loop over l blocks */ + m1 = 1; + m2 = 4; + for ( l = 1; l <= shd_rot_max_order; l++ ) + { + /* compute mtx-vector product for this l */ + for ( n = m1; n < m2; n++ ) + { + tmpRot[n - m1] = 0.f; + + for ( m = m1; m < m2; m++ ) + { + readPtr = getSmplPtr( inAudio, m, subframe_idx * subframe_len + i ); + /* crossfade with previous rotation gains */ + tmpRot[n - m1] += headRotData->crossfade[i] * gains[n][m] * ( *readPtr ) + + ( 1 - headRotData->crossfade[i] ) * gains_prev[n][m] * ( *readPtr ); + } + } + } + /* write back the result */ + for ( n = m1; n < m2; n++ ) + { + writePtr = getSmplPtr( outAudio, n, subframe_idx * subframe_len + i ); + ( *writePtr ) = tmpRot[n - m1]; + } + m1 = m2; + m2 += 2 * ( l + 1 ) + 1; + } + + /*unoptimized code for reference (full matrix multiplication)*/ + // for ( n = 0; n < nchan; n++ ) + // { + // tmpRot[n] = 0.f; + // + // for ( m = 0; m < nchan; m++ ) + // { + // tmpRot[n] += SHrotmat[n][m] * output[m][i]; + // } + // } + // for ( n = 0; n < nchan; n++ ) + // { + // output[n][i] = tmpRot[n]; + // } + + /* move SHrotmat to SHrotmat_prev */ + for ( i = 0; i < HEADROT_SHMAT_DIM; i++ ) + { + mvr2r( gains[i], gains_prev[i], HEADROT_SHMAT_DIM ); + } + } + + wmops_sub_end(); + + return IVAS_ERR_OK; +} + +static ivas_error renderIsmToBinaural( + const input_ism *ismInput, + IVAS_REND_AudioBuffer outAudio ) +{ + float tmpTDRendBuffer[MAX_OUTPUT_CHANNELS][L_FRAME48k]; + ivas_error error; + + wmops_sub_start( "renderIsmToBinaural" ); + + copyBufferTo2dArray( ismInput->base.inputBuffer, tmpTDRendBuffer ); + + /* TODO tmu : missing: interpolation between positions, 5ms rendering */ + if ( ( error = ivas_rend_TDObjRenderFrame( &ismInput->tdRendWrapper, + ismInput->base.inConfig, + NULL, + ismInput->base.ctx.pHeadRotData, + &ismInput->currentPos, + outAudio.config.numSamplesPerChannel, + *ismInput->base.ctx.pOutSampleRate, + tmpTDRendBuffer ) ) != IVAS_ERR_OK ) + { + return error; + } + + accumulate2dArrayToBuffer( tmpTDRendBuffer, &outAudio ); + + wmops_sub_end(); + + return IVAS_ERR_OK; +} + +static ivas_error renderIsmToBinauralRoom( + input_ism *ismInput, + IVAS_REND_AudioBuffer outAudio ) +{ + int16_t i; + int16_t azi_rot, ele_rot; + int16_t subframe_idx, subframe_len; + int16_t tmp; + rotation_matrix Rmat; + float tmpCrendBuffer[MAX_OUTPUT_CHANNELS][L_FRAME48k]; + IVAS_QUATERNION quat; + ivas_error error; + pan_vector currentPanGains; + pan_vector previousPanGains; + IVAS_REND_AudioBuffer tmpMcBuffer; + IVAS_REND_AudioObjectPosition rotatedPos; + const IVAS_REND_HeadRotData *headRotData; + + wmops_sub_start( "renderIsmToBinauralRoom" ); + + headRotData = ismInput->base.ctx.pHeadRotData; + rotatedPos = defaultObjectPosition(); + + if ( headRotData->headRotEnabled ) + { + subframe_len = ismInput->base.inputBuffer.config.numSamplesPerChannel / RENDERER_HEAD_POSITIONS_PER_FRAME; + // for ( subframe_idx = 0; subframe_idx < RENDERER_HEAD_POSITIONS_PER_FRAME; subframe_idx++ ) + for ( subframe_idx = 0; subframe_idx < 1; subframe_idx++ ) + { + quat.w = headRotData->headPositions[subframe_idx].w; + quat.x = headRotData->headPositions[subframe_idx].x; + quat.y = headRotData->headPositions[subframe_idx].y; + quat.z = headRotData->headPositions[subframe_idx].z; + + QuatToRotMat( quat, Rmat ); + } + (void) subframe_len; // avoid warning + } + + /* TODO tmu : missing: interpolation between positions, 5ms rendering */ + /* TODO(sgi): Possible optimization: less processing needed if position didn't change */ + /* TODO tmu2sgi: needs a lot of cleanup, we could also add rot_gains_prev to ismInput and use that */ + /* previous position gains */ + if ( headRotData->headRotEnabled ) + { + rotateAziEle( ismInput->previousPos.azimuth, ismInput->previousPos.elevation, &azi_rot, &ele_rot, ismInput->rot_mat_prev, 0 ); + rotatedPos.azimuth = (float) azi_rot; + rotatedPos.elevation = (float) ele_rot; + } + if ( ( error = getEfapGains( *ismInput->base.ctx.pEfapOutWrapper, + ( headRotData->headRotEnabled ) ? rotatedPos.azimuth : ismInput->previousPos.azimuth, + ( headRotData->headRotEnabled ) ? rotatedPos.elevation : ismInput->previousPos.elevation, + previousPanGains ) ) != IVAS_ERR_OK ) + { + return error; + } + + /* current position gains */ + if ( headRotData->headRotEnabled ) + { + rotateAziEle( ismInput->currentPos.azimuth, ismInput->currentPos.elevation, &azi_rot, &ele_rot, Rmat, 0 ); + rotatedPos.azimuth = (float) azi_rot; + rotatedPos.elevation = (float) ele_rot; + } + if ( ( error = getEfapGains( *ismInput->base.ctx.pEfapOutWrapper, + ( headRotData->headRotEnabled ) ? rotatedPos.azimuth : ismInput->currentPos.azimuth, + ( headRotData->headRotEnabled ) ? rotatedPos.elevation : ismInput->currentPos.elevation, + currentPanGains ) ) != IVAS_ERR_OK ) + { + return error; + } + + for ( i = 0; i < 3; i++ ) + { + mvr2r( Rmat[i], ismInput->rot_mat_prev[i], 3 ); + } + + /* intermediate rendering to 7_1_4 */ + tmpMcBuffer = ismInput->base.inputBuffer; + getAudioConfigNumChannels( IVAS_REND_AUDIO_CONFIG_7_1_4, &tmp ); + tmpMcBuffer.config.numChannels = tmp; + tmpMcBuffer.data = count_malloc( tmpMcBuffer.config.numSamplesPerChannel * tmpMcBuffer.config.numChannels * sizeof( float ) ); + set_zero( tmpMcBuffer.data, tmpMcBuffer.config.numSamplesPerChannel * tmpMcBuffer.config.numChannels ); + + renderBufferChannelLerp( ismInput->base.inputBuffer, 0, currentPanGains, previousPanGains, tmpMcBuffer ); + + copyBufferTo2dArray( tmpMcBuffer, tmpCrendBuffer ); + + ivas_rend_crendProcess( &ismInput->crendWrapper, IVAS_REND_AUDIO_CONFIG_7_1_4, IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM, tmpCrendBuffer, *ismInput->base.ctx.pOutSampleRate ); + + accumulate2dArrayToBuffer( tmpCrendBuffer, &outAudio ); + + count_free( tmpMcBuffer.data ); + + wmops_sub_end(); + + return IVAS_ERR_OK; +} + +static ivas_error renderIsmToMc( + const input_ism *ismInput, + const IVAS_REND_AudioBuffer outAudio ) +{ + pan_vector currentPanGains; + pan_vector previousPanGains; + ivas_error error; + + wmops_sub_start( "renderIsmToMc" ); + + /* TODO(sgi): Possible optimization: less processing needed if position didn't change */ + if ( ( error = getEfapGains( *ismInput->base.ctx.pEfapOutWrapper, ismInput->currentPos.azimuth, ismInput->currentPos.elevation, currentPanGains ) ) != IVAS_ERR_OK ) + { + return error; + } + if ( ( error = getEfapGains( *ismInput->base.ctx.pEfapOutWrapper, ismInput->previousPos.azimuth, ismInput->previousPos.elevation, previousPanGains ) ) != IVAS_ERR_OK ) + { + return error; + } + + /* Assume num channels in audio buffer to be 1. + * This should have been validated in IVAS_REND_FeedInputAudio() */ + renderBufferChannelLerp( ismInput->base.inputBuffer, 0, currentPanGains, previousPanGains, outAudio ); + + wmops_sub_end(); + + return IVAS_ERR_OK; +} + +static ivas_error renderIsmToSba( + const input_ism *ismInput, + const IVAS_REND_AudioConfig outConfig, + const IVAS_REND_AudioBuffer outAudio ) +{ + int16_t ambiOrderOut; + int16_t numOutChannels; + pan_vector currentPanGains; + pan_vector previousPanGains; + ivas_error error; + error = IVAS_ERR_OK; + + wmops_sub_start( "renderIsmToSba" ); + + if ( ( error = getAudioConfigNumChannels( outConfig, &numOutChannels ) ) != IVAS_ERR_OK ) + { + return error; + } + if ( ( error = getAmbisonicsOrder( outConfig, &ambiOrderOut ) ) != IVAS_ERR_OK ) + { + return error; + } + + ivas_dirac_dec_get_response( (int16_t) ismInput->previousPos.azimuth, (int16_t) ismInput->previousPos.elevation, previousPanGains, ambiOrderOut ); + + if ( ( ismInput->currentPos.azimuth == ismInput->previousPos.azimuth ) && + ( ismInput->currentPos.elevation == ismInput->previousPos.elevation ) ) + { + mvr2r( previousPanGains, currentPanGains, MAX_OUTPUT_CHANNELS ); + } + else + { + ivas_dirac_dec_get_response( (int16_t) ismInput->currentPos.azimuth, (int16_t) ismInput->currentPos.elevation, currentPanGains, ambiOrderOut ); + } + + /* Assume num channels in audio buffer to be 1. + * This should have been validated in IVAS_REND_FeedInputAudio() */ + renderBufferChannelLerp( ismInput->base.inputBuffer, 0, currentPanGains, previousPanGains, outAudio ); + + wmops_sub_end(); + + return error; +} + +static ivas_error renderInputIsm( + input_ism *ismInput, + const IVAS_REND_AudioConfig outConfig, + const IVAS_REND_AudioBuffer outAudio ) +{ + ivas_error error; + IVAS_REND_AudioBuffer inAudio; + + inAudio = ismInput->base.inputBuffer; + + if ( ismInput->base.numNewSamplesPerChannel != outAudio.config.numSamplesPerChannel ) + { + /* Mismatch between the number of input samples vs number of requested output samples - currently not allowed */ + return IVAS_ERR_INVALID_BUFFER_SIZE; + } + ismInput->base.numNewSamplesPerChannel = 0; + + /* Apply input gain to new audio */ + v_multc( inAudio.data, ismInput->base.gain, inAudio.data, inAudio.config.numSamplesPerChannel * inAudio.config.numChannels ); + + + switch ( getAudioConfigType( outConfig ) ) + { + case IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED: + error = renderIsmToMc( ismInput, outAudio ); + break; + case IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS: + error = renderIsmToSba( ismInput, outConfig, outAudio ); + break; + case IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL: + switch ( outConfig ) + { + case IVAS_REND_AUDIO_CONFIG_BINAURAL: + error = renderIsmToBinaural( ismInput, outAudio ); + break; + case IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM: + error = renderIsmToBinauralRoom( ismInput, outAudio ); + break; + default: + return IVAS_ERR_INVALID_OUTPUT_FORMAT; + } + break; + default: + return IVAS_ERR_INVALID_OUTPUT_FORMAT; + } + /* Check error here to keep switch statement more compact */ + if ( error != IVAS_ERR_OK ) + { + return error; + } + + return IVAS_ERR_OK; +} + +static ivas_error renderActiveInputsIsm( + IVAS_REND_HANDLE hIvasRend, + IVAS_REND_AudioBuffer outAudio ) +{ + int16_t i; + input_ism *pCurrentInput; + ivas_error error; + + for ( i = 0, pCurrentInput = hIvasRend->inputsIsm; i < RENDERER_MAX_ISM_INPUTS; ++i, ++pCurrentInput ) + { + if ( pCurrentInput->base.inConfig == IVAS_REND_AUDIO_CONFIG_UNKNOWN ) + { + /* Skip inactive inputs */ + continue; + } + if ( ( error = renderInputIsm( pCurrentInput, hIvasRend->outputConfig, outAudio ) ) != IVAS_ERR_OK ) + { + return error; + } + } + + return IVAS_ERR_OK; +} + +static ivas_error renderLfeToBinaural( + const input_mc *mcInput, + IVAS_REND_AudioBuffer outAudio ) +{ + int16_t i; + int16_t lfe_idx; + float gain; + float *readPtr, *writePtr; + + assert( ( outAudio.config.numChannels == 2 ) && "Must be binaural output" ); + + wmops_sub_start( "renderLfeToBinaural" ); + + gain = GAIN_LFE; + + if ( mcInput->base.inConfig != IVAS_REND_AUDIO_CONFIG_LS_CUSTOM ) + { + lfe_idx = LFE_CHANNEL; + } + else if ( mcInput->customLsInput.num_lfe > 0 ) + { + lfe_idx = mcInput->customLsInput.lfe_idx[0]; + } + else + { + /* no LFE to render */ + return IVAS_ERR_OK; + } + + /* Copy LFE to left and right ears */ + readPtr = getSmplPtr( mcInput->base.inputBuffer, lfe_idx, 0 ); + writePtr = getSmplPtr( outAudio, 0, 0 ); + for ( i = 0; i < mcInput->base.inputBuffer.config.numSamplesPerChannel; i++ ) + { + *writePtr++ += gain * ( *readPtr++ ); + } + + readPtr = getSmplPtr( mcInput->base.inputBuffer, lfe_idx, 0 ); + writePtr = getSmplPtr( outAudio, 1, 0 ); + for ( i = 0; i < mcInput->base.inputBuffer.config.numSamplesPerChannel; i++ ) + { + *writePtr++ += gain * ( *readPtr++ ); + } + + wmops_sub_end(); + + return IVAS_ERR_OK; +} + +static ivas_error renderMcToBinaural( + input_mc *mcInput, + const IVAS_REND_AudioConfig outConfig, + IVAS_REND_AudioBuffer outAudio ) +{ + int8_t headRotEnabled; + float tmpRendBuffer[MAX_OUTPUT_CHANNELS][L_FRAME48k]; + IVAS_REND_AudioConfig inConfig; + + ivas_error error; + IVAS_REND_AudioBuffer tmpRotBuffer; + + wmops_sub_start( "renderMcToBinaural" ); + + headRotEnabled = mcInput->base.ctx.pHeadRotData->headRotEnabled; + inConfig = mcInput->base.inConfig; + + if ( ( inConfig == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM ) || + ( headRotEnabled && ( inConfig == IVAS_REND_AUDIO_CONFIG_5_1 || inConfig == IVAS_REND_AUDIO_CONFIG_7_1 ) ) ) + { + copyBufferTo2dArray( mcInput->base.inputBuffer, tmpRendBuffer ); + + if ( ( error = ivas_rend_TDObjRenderFrame( &mcInput->tdRendWrapper, + mcInput->base.inConfig, + &mcInput->customLsInput, + mcInput->base.ctx.pHeadRotData, + NULL, + mcInput->base.inputBuffer.config.numSamplesPerChannel, + *mcInput->base.ctx.pOutSampleRate, + tmpRendBuffer ) ) != IVAS_ERR_OK ) + { + return error; + } + } + else + { + /* apply rotation */ + if ( headRotEnabled ) + { + tmpRotBuffer = mcInput->base.inputBuffer; + tmpRotBuffer.data = count_malloc( tmpRotBuffer.config.numSamplesPerChannel * tmpRotBuffer.config.numChannels * sizeof( float ) ); + set_zero( tmpRotBuffer.data, tmpRotBuffer.config.numSamplesPerChannel * tmpRotBuffer.config.numChannels ); + + rotateFrameMc( mcInput->base.inputBuffer, + mcInput->base.inConfig, + mcInput->customLsInput, + mcInput->base.ctx.pHeadRotData, + mcInput->rot_gains_prev, + mcInput->efapInWrapper.hEfap, + tmpRotBuffer ); + + copyBufferTo2dArray( tmpRotBuffer, tmpRendBuffer ); + count_free( tmpRotBuffer.data ); + } + else + { + copyBufferTo2dArray( mcInput->base.inputBuffer, tmpRendBuffer ); + } + + /* call CREND */ + if ( ( error = ivas_rend_crendProcess( &mcInput->crendWrapper, mcInput->base.inConfig, outConfig, tmpRendBuffer, *mcInput->base.ctx.pOutSampleRate ) ) != IVAS_ERR_OK ) + { + return error; + } + } + + accumulate2dArrayToBuffer( tmpRendBuffer, &outAudio ); + + /* TODO tmu : needs delay compensation */ + renderLfeToBinaural( mcInput, outAudio ); + + wmops_sub_end(); + + return IVAS_ERR_OK; +} + +static ivas_error renderMcToBinauralRoom( + input_mc *mcInput, + const IVAS_REND_AudioConfig outConfig, + IVAS_REND_AudioBuffer outAudio ) +{ + float tmpCrendBuffer[MAX_OUTPUT_CHANNELS][L_FRAME48k]; + + ivas_error error; + IVAS_REND_AudioBuffer tmpRotBuffer; + + wmops_sub_start( "renderMcToBinauralRoom" ); + + /* apply rotation */ + if ( mcInput->base.ctx.pHeadRotData->headRotEnabled ) + { + tmpRotBuffer = mcInput->base.inputBuffer; + tmpRotBuffer.data = count_malloc( tmpRotBuffer.config.numSamplesPerChannel * tmpRotBuffer.config.numChannels * sizeof( float ) ); + set_zero( tmpRotBuffer.data, tmpRotBuffer.config.numSamplesPerChannel * tmpRotBuffer.config.numChannels ); + + rotateFrameMc( mcInput->base.inputBuffer, + mcInput->base.inConfig, + mcInput->customLsInput, + mcInput->base.ctx.pHeadRotData, + mcInput->rot_gains_prev, + mcInput->efapInWrapper.hEfap, + tmpRotBuffer ); + + copyBufferTo2dArray( tmpRotBuffer, tmpCrendBuffer ); + count_free( tmpRotBuffer.data ); + } + else + { + copyBufferTo2dArray( mcInput->base.inputBuffer, tmpCrendBuffer ); + } + + /* call CREND */ + if ( ( error = ivas_rend_crendProcess( &mcInput->crendWrapper, mcInput->base.inConfig, outConfig, tmpCrendBuffer, *mcInput->base.ctx.pOutSampleRate ) ) != IVAS_ERR_OK ) + { + return error; + } + + accumulate2dArrayToBuffer( tmpCrendBuffer, &outAudio ); + + + /* TODO tmu : needs delay compensation */ + renderLfeToBinaural( mcInput, outAudio ); + + wmops_sub_end(); + + return IVAS_ERR_OK; +} + +static ivas_error renderMcCustomLsToBinauralRoom( + input_mc *mcInput, + const IVAS_REND_AudioConfig outConfig, + IVAS_REND_AudioBuffer outAudio ) +{ + int8_t headRotEnabled; + int16_t i; + int16_t tmp; + float tmpCrendBuffer[MAX_OUTPUT_CHANNELS][L_FRAME48k]; + ivas_error error; + IVAS_REND_AudioBuffer tmpRotBuffer; + IVAS_REND_AudioBuffer tmpMcBuffer; + IVAS_REND_AudioBuffer *tmpBufPtr; + + wmops_sub_start( "renderMcCustomLsToBinauralRoom" ); + + tmpRotBuffer = outAudio; /* avoid compilation warning */ + + headRotEnabled = mcInput->base.ctx.pHeadRotData->headRotEnabled; + + /* apply rotation */ + if ( headRotEnabled ) + { + tmpRotBuffer = mcInput->base.inputBuffer; + tmpRotBuffer.data = count_malloc( tmpRotBuffer.config.numSamplesPerChannel * tmpRotBuffer.config.numChannels * sizeof( float ) ); + set_zero( tmpRotBuffer.data, tmpRotBuffer.config.numSamplesPerChannel * tmpRotBuffer.config.numChannels ); + + rotateFrameMc( mcInput->base.inputBuffer, + mcInput->base.inConfig, + mcInput->customLsInput, + mcInput->base.ctx.pHeadRotData, + mcInput->rot_gains_prev, + mcInput->efapInWrapper.hEfap, + tmpRotBuffer ); + } + + /* intermediate conversion to 7_1_4 */ + tmpMcBuffer = mcInput->base.inputBuffer; + getAudioConfigNumChannels( IVAS_REND_AUDIO_CONFIG_7_1_4, &tmp ); + tmpMcBuffer.config.numChannels = (int16_t) tmp; + tmpMcBuffer.data = count_malloc( tmpMcBuffer.config.numSamplesPerChannel * tmpMcBuffer.config.numChannels * sizeof( float ) ); + set_zero( tmpMcBuffer.data, tmpMcBuffer.config.numSamplesPerChannel * tmpMcBuffer.config.numChannels ); + + tmpBufPtr = ( headRotEnabled ) ? &tmpRotBuffer : &mcInput->base.inputBuffer; + for ( i = 0; i < mcInput->base.inputBuffer.config.numChannels; i++ ) + { + renderBufferChannel( *tmpBufPtr, i, mcInput->panGains[i], tmpMcBuffer ); + } + copyBufferTo2dArray( tmpMcBuffer, tmpCrendBuffer ); + + /* call CREND */ + if ( ( error = ivas_rend_crendProcess( &mcInput->crendWrapper, IVAS_REND_AUDIO_CONFIG_7_1_4, outConfig, tmpCrendBuffer, *mcInput->base.ctx.pOutSampleRate ) ) != IVAS_ERR_OK ) + { + return error; + } + + accumulate2dArrayToBuffer( tmpCrendBuffer, &outAudio ); + + /* TODO tmu : needs delay compensation */ + renderLfeToBinaural( mcInput, outAudio ); + + if ( headRotEnabled ) + { + count_free( tmpRotBuffer.data ); + } + count_free( tmpMcBuffer.data ); + + wmops_sub_end(); + + return IVAS_ERR_OK; +} + +static ivas_error renderMcToMc( + const input_mc *mcInput, + IVAS_REND_AudioBuffer outAudio ) +{ + int16_t i; + IVAS_REND_AudioBuffer inAudio; + + wmops_sub_start( "renderMcToMc" ); + + inAudio = mcInput->base.inputBuffer; + + for ( i = 0; i < inAudio.config.numChannels; ++i ) + { + renderBufferChannel( inAudio, i, mcInput->panGains[i], outAudio ); + } + + wmops_sub_end(); + + return IVAS_ERR_OK; +} + +static ivas_error renderMcToSba( + const input_mc *mcInput, + IVAS_REND_AudioBuffer outAudio ) +{ + int16_t i; + IVAS_REND_AudioBuffer inAudio; + + wmops_sub_start( "renderMcToSba" ); + + inAudio = mcInput->base.inputBuffer; + + for ( i = 0; i < inAudio.config.numChannels; ++i ) + { + renderBufferChannel( inAudio, i, mcInput->panGains[i], outAudio ); + } + + wmops_sub_end(); + + return IVAS_ERR_OK; +} + +static ivas_error renderInputMc( + input_mc *mcInput, + IVAS_REND_AudioConfig outConfig, + IVAS_REND_AudioBuffer outAudio ) +{ + ivas_error error; + IVAS_REND_AudioBuffer inAudio; + + inAudio = mcInput->base.inputBuffer; + + if ( mcInput->base.numNewSamplesPerChannel != outAudio.config.numSamplesPerChannel ) + { + /* Mismatch between the number of input samples vs number of requested output samples - currently not allowed */ + return IVAS_ERR_INVALID_BUFFER_SIZE; + } + mcInput->base.numNewSamplesPerChannel = 0; + + /* Apply input gain to new audio */ + v_multc( inAudio.data, + mcInput->base.gain, + inAudio.data, + inAudio.config.numSamplesPerChannel * inAudio.config.numChannels ); + + switch ( getAudioConfigType( outConfig ) ) + { + case IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED: + error = renderMcToMc( mcInput, outAudio ); + break; + case IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS: + error = renderMcToSba( mcInput, outAudio ); + break; + case IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL: + switch ( outConfig ) + { + case IVAS_REND_AUDIO_CONFIG_BINAURAL: + error = renderMcToBinaural( mcInput, outConfig, outAudio ); + break; + case IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM: + if ( mcInput->base.inConfig == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM ) + { + error = renderMcCustomLsToBinauralRoom( mcInput, outConfig, outAudio ); + } + else + { + error = renderMcToBinauralRoom( mcInput, outConfig, outAudio ); + } + break; + default: + return IVAS_ERR_INVALID_OUTPUT_FORMAT; + } + break; + default: + return IVAS_ERR_INVALID_OUTPUT_FORMAT; + } + /* Check error here to keep switch statement more compact */ + if ( error != IVAS_ERR_OK ) + { + return error; + } + + return IVAS_ERR_OK; +} + +static ivas_error renderActiveInputsMc( + IVAS_REND_HANDLE hIvasRend, + IVAS_REND_AudioBuffer outAudio ) +{ + int16_t i; + input_mc *pCurrentInput; + ivas_error error; + + for ( i = 0, pCurrentInput = hIvasRend->inputsMc; i < RENDERER_MAX_MC_INPUTS; ++i, ++pCurrentInput ) + { + if ( pCurrentInput->base.inConfig == IVAS_REND_AUDIO_CONFIG_UNKNOWN ) + { + /* Skip inactive inputs */ + continue; + } + if ( ( error = renderInputMc( pCurrentInput, hIvasRend->outputConfig, outAudio ) ) != IVAS_ERR_OK ) + { + return error; + } + } + + return IVAS_ERR_OK; +} + +static ivas_error renderSbaToMc( + const input_sba *sbaInput, + IVAS_REND_AudioBuffer outAudio ) +{ + int16_t i; + IVAS_REND_AudioBuffer inAudio; + + wmops_sub_start( "renderSbaToMc" ); + + inAudio = sbaInput->base.inputBuffer; + + for ( i = 0; i < inAudio.config.numChannels; ++i ) + { + renderBufferChannel( inAudio, i, sbaInput->hoaDecMtx[i], outAudio ); + } + + wmops_sub_end(); + + return IVAS_ERR_OK; +} + +static ivas_error renderSbaToSba( + const input_sba *sbaInput, + IVAS_REND_AudioBuffer outAudio ) +{ + int16_t i; + IVAS_REND_AudioBuffer inAudio; + + wmops_sub_start( "renderSbaToSba" ); + + inAudio = sbaInput->base.inputBuffer; + + for ( i = 0; i < inAudio.config.numChannels; ++i ) + { + renderBufferChannel( inAudio, i, sbaInput->hoaDecMtx[i], outAudio ); + } + + wmops_sub_end(); + + return IVAS_ERR_OK; +} + +static ivas_error renderSbaToBinaural( + input_sba *sbaInput, + const IVAS_REND_AudioConfig outConfig, + IVAS_REND_AudioBuffer outAudio ) +{ + float tmpCrendBuffer[MAX_OUTPUT_CHANNELS][L_FRAME48k]; + + ivas_error error; + IVAS_REND_AudioBuffer tmpRotBuffer; + + wmops_sub_start( "renderSbaToBinaural" ); + + /* apply rotation */ + if ( sbaInput->base.ctx.pHeadRotData->headRotEnabled ) + { + tmpRotBuffer = sbaInput->base.inputBuffer; + tmpRotBuffer.data = count_malloc( tmpRotBuffer.config.numSamplesPerChannel * tmpRotBuffer.config.numChannels * sizeof( float ) ); + /* copy input for in-place rotation */ + mvr2r( sbaInput->base.inputBuffer.data, tmpRotBuffer.data, + tmpRotBuffer.config.numChannels * tmpRotBuffer.config.numSamplesPerChannel ); + + rotateFrameSba( sbaInput->base.inputBuffer, + sbaInput->base.inConfig, + sbaInput->base.ctx.pHeadRotData, + sbaInput->rot_gains_prev, + tmpRotBuffer ); + + copyBufferTo2dArray( tmpRotBuffer, tmpCrendBuffer ); + count_free( tmpRotBuffer.data ); + } + else + { + copyBufferTo2dArray( sbaInput->base.inputBuffer, tmpCrendBuffer ); + } + + /* call CREND */ + if ( ( error = ivas_rend_crendProcess( &sbaInput->crendWrapper, sbaInput->base.inConfig, outConfig, tmpCrendBuffer, *sbaInput->base.ctx.pOutSampleRate ) ) != IVAS_ERR_OK ) + { + return error; + } + + accumulate2dArrayToBuffer( tmpCrendBuffer, &outAudio ); + + wmops_sub_end(); + + return IVAS_ERR_OK; +} + +static ivas_error renderSbaToBinauralRoom( + input_sba *sbaInput, + const IVAS_REND_AudioConfig outConfig, + IVAS_REND_AudioBuffer outAudio ) +{ + int8_t headRotEnabled; + int16_t i; + int16_t tmp; + float tmpCrendBuffer[MAX_OUTPUT_CHANNELS][L_FRAME48k]; + + ivas_error error; + IVAS_REND_AudioBuffer tmpRotBuffer; + IVAS_REND_AudioBuffer tmpMcBuffer; + IVAS_REND_AudioBuffer *tmpBufPtr; + + tmpRotBuffer = outAudio; /* avoid compilation warning */ + + wmops_sub_start( "renderSbaToBinauralRoom" ); + + headRotEnabled = sbaInput->base.ctx.pHeadRotData->headRotEnabled; + + /* apply rotation */ + if ( headRotEnabled ) + { + tmpRotBuffer = sbaInput->base.inputBuffer; + tmpRotBuffer.data = count_malloc( tmpRotBuffer.config.numSamplesPerChannel * tmpRotBuffer.config.numChannels * sizeof( float ) ); + /* copy input for in-place rotation */ + mvr2r( sbaInput->base.inputBuffer.data, tmpRotBuffer.data, tmpRotBuffer.config.numChannels * tmpRotBuffer.config.numSamplesPerChannel ); + + rotateFrameSba( sbaInput->base.inputBuffer, sbaInput->base.inConfig, sbaInput->base.ctx.pHeadRotData, sbaInput->rot_gains_prev, tmpRotBuffer ); + } + + /* intermediate rendering to 7_1_4 */ + tmpMcBuffer = sbaInput->base.inputBuffer; + getAudioConfigNumChannels( IVAS_REND_AUDIO_CONFIG_7_1_4, &tmp ); + tmpMcBuffer.config.numChannels = (int16_t) tmp; + tmpMcBuffer.data = count_malloc( tmpMcBuffer.config.numSamplesPerChannel * tmpMcBuffer.config.numChannels * sizeof( float ) ); + set_zero( tmpMcBuffer.data, tmpMcBuffer.config.numChannels * tmpMcBuffer.config.numSamplesPerChannel ); + + tmpBufPtr = ( headRotEnabled ) ? &tmpRotBuffer : &sbaInput->base.inputBuffer; + for ( i = 0; i < sbaInput->base.inputBuffer.config.numChannels; i++ ) + { + renderBufferChannel( *tmpBufPtr, i, sbaInput->hoaDecMtx[i], tmpMcBuffer ); + } + + copyBufferTo2dArray( tmpMcBuffer, tmpCrendBuffer ); + + /* call CREND */ + if ( ( error = ivas_rend_crendProcess( &sbaInput->crendWrapper, IVAS_REND_AUDIO_CONFIG_7_1_4, outConfig, tmpCrendBuffer, *sbaInput->base.ctx.pOutSampleRate ) ) != IVAS_ERR_OK ) + { + return error; + } + + accumulate2dArrayToBuffer( tmpCrendBuffer, &outAudio ); + + if ( headRotEnabled ) + { + count_free( tmpRotBuffer.data ); + } + count_free( tmpMcBuffer.data ); + + wmops_sub_end(); + + return IVAS_ERR_OK; +} + +static ivas_error renderInputSba( + input_sba *sbaInput, + const IVAS_REND_AudioConfig outConfig, + IVAS_REND_AudioBuffer outAudio ) +{ + ivas_error error; + IVAS_REND_AudioBuffer inAudio; + + inAudio = sbaInput->base.inputBuffer; + + if ( sbaInput->base.numNewSamplesPerChannel != outAudio.config.numSamplesPerChannel ) + { + /* Mismatch between the number of input samples vs number of requested output samples - currently not allowed */ + return IVAS_ERR_INVALID_BUFFER_SIZE; + } + sbaInput->base.numNewSamplesPerChannel = 0; + + /* Apply input gain to new audio */ + v_multc( inAudio.data, + sbaInput->base.gain, + inAudio.data, + inAudio.config.numSamplesPerChannel * inAudio.config.numChannels ); + + switch ( getAudioConfigType( outConfig ) ) + { + case IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED: + error = renderSbaToMc( sbaInput, outAudio ); + break; + case IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS: + error = renderSbaToSba( sbaInput, outAudio ); + break; + case IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL: + switch ( outConfig ) + { + case IVAS_REND_AUDIO_CONFIG_BINAURAL: + error = renderSbaToBinaural( sbaInput, outConfig, outAudio ); + break; + case IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM: + error = renderSbaToBinauralRoom( sbaInput, outConfig, outAudio ); + break; + default: + return IVAS_ERR_INVALID_OUTPUT_FORMAT; + } + break; + default: + return IVAS_ERR_INVALID_OUTPUT_FORMAT; + } + /* Check error here to keep switch statement more compact */ + if ( error != IVAS_ERR_OK ) + { + return error; + } + + return IVAS_ERR_OK; +} + +static ivas_error renderActiveInputsSba( + IVAS_REND_HANDLE hIvasRend, + IVAS_REND_AudioBuffer outAudio ) +{ + int16_t i; + input_sba *pCurrentInput; + ivas_error error; + + for ( i = 0, pCurrentInput = hIvasRend->inputsSba; i < RENDERER_MAX_SBA_INPUTS; ++i, ++pCurrentInput ) + { + if ( pCurrentInput->base.inConfig == IVAS_REND_AUDIO_CONFIG_UNKNOWN ) + { + /* Skip inactive inputs */ + continue; + } + + if ( ( error = renderInputSba( pCurrentInput, hIvasRend->outputConfig, outAudio ) ) != IVAS_ERR_OK ) + { + return error; + } + } + + return IVAS_ERR_OK; +} + +ivas_error IVAS_REND_GetSamples( + IVAS_REND_HANDLE hIvasRend, + IVAS_REND_AudioBuffer outAudio ) +{ + ivas_error error; + int16_t numOutChannels; + + /*-----------------------------------------------------------------* + * Validate function arguments + *-----------------------------------------------------------------*/ + + if ( hIvasRend == NULL || outAudio.data == NULL ) + { + return IVAS_ERR_UNEXPECTED_NULL_POINTER; + } + if ( outAudio.config.numSamplesPerChannel <= 0 || MAX_BUFFER_LENGTH_PER_CHANNEL < outAudio.config.numSamplesPerChannel ) + { + return IVAS_ERR_INVALID_BUFFER_SIZE; + } + if ( outAudio.config.numChannels <= 0 || MAX_OUTPUT_CHANNELS < outAudio.config.numChannels ) + { + return IVAS_ERR_WRONG_NUM_CHANNELS; + } + if ( getAudioConfigType( hIvasRend->outputConfig ) == IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL && + outAudio.config.numSamplesPerChannel * 1000 != BINAURAL_RENDERING_FRAME_SIZE_MS * hIvasRend->sampleRateOut ) + { + /* Binaural rendering requires specific frame size */ + return IVAS_ERR_INVALID_BUFFER_SIZE; + } + if ( ( error = IVAS_REND_NumOutChannels( hIvasRend, &numOutChannels ) ) != IVAS_ERR_OK ) + { + return error; + } + if ( numOutChannels != outAudio.config.numChannels ) + { + return IVAS_ERR_WRONG_NUM_CHANNELS; + } + + /* Clear output buffer */ + set_zero( outAudio.data, outAudio.config.numChannels * outAudio.config.numSamplesPerChannel ); + + if ( ( error = renderActiveInputsIsm( hIvasRend, outAudio ) ) != IVAS_ERR_OK ) + { + return error; + } + if ( ( error = renderActiveInputsMc( hIvasRend, outAudio ) ) != IVAS_ERR_OK ) + { + return error; + } + if ( ( error = renderActiveInputsSba( hIvasRend, outAudio ) ) != IVAS_ERR_OK ) + { + return error; + } + +#ifdef DEBUGGING + hIvasRend->numClipping += +#endif + limitRendererOutput( hIvasRend->hLimiter, outAudio.data, outAudio.config.numSamplesPerChannel, IVAS_LIMITER_THRESHOLD ); + + return IVAS_ERR_OK; +} + +void IVAS_REND_Close( + IVAS_REND_HANDLE *phIvasRend ) +{ + uint16_t i; + IVAS_REND_HANDLE hIvasRend; + + /*-----------------------------------------------------------------* + * Validate function arguments + *-----------------------------------------------------------------*/ + + if ( phIvasRend == NULL || *phIvasRend == NULL ) + { + return; + } + hIvasRend = *phIvasRend; + + if ( hIvasRend->efapOutWrapper.hEfap != NULL ) + { + efap_free_data( &hIvasRend->efapOutWrapper.hEfap ); + } + + /* clear inputs */ + for ( i = 0; i < RENDERER_MAX_ISM_INPUTS; ++i ) + { + clearInputIsm( &hIvasRend->inputsIsm[i] ); + } + for ( i = 0; i < RENDERER_MAX_MC_INPUTS; ++i ) + { + clearInputMc( &hIvasRend->inputsMc[i] ); + } + for ( i = 0; i < RENDERER_MAX_SBA_INPUTS; ++i ) + { + clearInputSba( &hIvasRend->inputsSba[i] ); + } + + /* clear Config. Renderer */ + ivas_render_config_close( &( hIvasRend->hRendererConfig ) ); + + ivas_limiter_close( &hIvasRend->hLimiter ); + + count_free( hIvasRend ); + *phIvasRend = NULL; + + return; +} + +#ifdef DEBUGGING +int32_t IVAS_REND_GetNoCLipping( + IVAS_REND_CONST_HANDLE hIvasRend ) +{ + return hIvasRend->numClipping; +} + +int32_t IVAS_REND_GetCntFramesLimited( + IVAS_REND_CONST_HANDLE hIvasRend ) +{ + if ( hIvasRend->hLimiter == NULL ) + { + return 0; + } + + return hIvasRend->hLimiter->cnt_frames_limited; +} +#endif +#endif diff --git a/lib_rend/lib_rend.h b/lib_rend/lib_rend.h new file mode 100644 index 0000000000000000000000000000000000000000..99d4b7af25f6079d0b29367129ef6d5b99af4f06 --- /dev/null +++ b/lib_rend/lib_rend.h @@ -0,0 +1,283 @@ +/****************************************************************************************************** + + (C) 2022 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository. All Rights Reserved. + + This software is protected by copyright law and by international treaties. + The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository retain full ownership rights in their respective contributions in + the software. This notice grants no license of any kind, including but not limited to patent + license, nor is any license granted by implication, estoppel or otherwise. + + Contributors are required to enter into the IVAS codec Public Collaboration agreement before making + contributions. + + This software is provided "AS IS", without any express or implied warranties. The software is in the + development stage. It is intended exclusively for experts who have experience with such software and + solely for the purpose of inspection. All implied warranties of non-infringement, merchantability + and fitness for a particular purpose are hereby disclaimed and excluded. + + Any dispute, controversy or claim arising under or in relation to providing this software shall be + submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in + accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and + the United Nations Convention on Contracts on the International Sales of Goods. + +*******************************************************************************************************/ + +#ifndef LIB_REND_H +#define LIB_REND_H + +#include +#include +#include + +#include "options.h" +#include "common_api_types.h" +#include "ivas_error.h" + +#ifdef EXT_RENDERER + +#define RENDERER_MAX_ISM_INPUTS 4 +#define RENDERER_MAX_MC_INPUTS 1 +#define RENDERER_MAX_SBA_INPUTS 1 +#define RENDERER_MAX_MASA_INPUTS 1 + +#define RENDERER_HEAD_POSITIONS_PER_FRAME 4 + +typedef struct +{ + int16_t numSamplesPerChannel; + int16_t numChannels; +} IVAS_REND_AudioBufferConfig; + +typedef struct +{ + IVAS_REND_AudioBufferConfig config; + float *data; +} IVAS_REND_AudioBuffer; + +typedef struct +{ + IVAS_REND_AudioBufferConfig config; + const float *data; +} IVAS_REND_ReadOnlyAudioBuffer; + +typedef struct +{ + float azimuth; + float elevation; +} IVAS_REND_AudioObjectPosition; + +typedef struct IVAS_REND *IVAS_REND_HANDLE; +typedef struct IVAS_REND const *IVAS_REND_CONST_HANDLE; + +typedef enum +{ + IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED = 0, + IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS, + IVAS_REND_AUDIO_CONFIG_TYPE_OBJECT_BASED, + IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL, + IVAS_REND_AUDIO_CONFIG_TYPE_MASA, + IVAS_REND_AUDIO_CONFIG_TYPE_UNKNOWN, +} IVAS_REND_AudioConfigType; + +/* TODO(sgi): Harmonize with AUDIO_CONFIG */ +/* + Note: numerical values carry specific information here. + + MSB LSB + -------------------------------------------------------------------------------- + ... unused (assumed all 0) ... | config type (1 byte) | config variant (1 byte) | + -------------------------------------------------------------------------------- + + Where "config type" is the general type from the following list: + - unknown + - channel-based + - ambisonics + - object-based + - binaural + - MASA + + Config variants are concrete configs of each type. + */ +typedef enum +{ + IVAS_REND_AUDIO_CONFIG_MONO = IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED << 8 | 0, + IVAS_REND_AUDIO_CONFIG_STEREO = IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED << 8 | 1, + IVAS_REND_AUDIO_CONFIG_5_1 = IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED << 8 | 2, + IVAS_REND_AUDIO_CONFIG_7_1 = IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED << 8 | 3, + IVAS_REND_AUDIO_CONFIG_5_1_2 = IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED << 8 | 4, + IVAS_REND_AUDIO_CONFIG_5_1_4 = IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED << 8 | 5, + IVAS_REND_AUDIO_CONFIG_7_1_4 = IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED << 8 | 6, + IVAS_REND_AUDIO_CONFIG_LS_CUSTOM = IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED << 8 | 255, + + IVAS_REND_AUDIO_CONFIG_FOA = IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS << 8 | 0, + IVAS_REND_AUDIO_CONFIG_HOA2 = IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS << 8 | 1, + IVAS_REND_AUDIO_CONFIG_HOA3 = IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS << 8 | 2, + + IVAS_REND_AUDIO_CONFIG_OBJECT = IVAS_REND_AUDIO_CONFIG_TYPE_OBJECT_BASED << 8 | 0, + + IVAS_REND_AUDIO_CONFIG_BINAURAL = IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL << 8 | 0, + IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM = IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL << 8 | 1, + + IVAS_REND_AUDIO_CONFIG_MASA1 = IVAS_REND_AUDIO_CONFIG_TYPE_MASA << 8 | 0, + IVAS_REND_AUDIO_CONFIG_MASA2 = IVAS_REND_AUDIO_CONFIG_TYPE_MASA << 8 | 1, + + IVAS_REND_AUDIO_CONFIG_UNKNOWN = IVAS_REND_AUDIO_CONFIG_TYPE_UNKNOWN << 8 | 0, +} IVAS_REND_AudioConfig; + +typedef uint16_t IVAS_REND_InputId; + +typedef struct +{ + int16_t numLfeChannels; + float lfeOutputGains[IVAS_MAX_INPUT_LFE_CHANNELS][IVAS_MAX_OUTPUT_CHANNELS]; +} IVAS_REND_LfeRouting; + +/* clang-format off */ +/*----------------------------------------------------------------------------------* + * Function prototypes + *----------------------------------------------------------------------------------*/ + +/* Functions to be called before rendering */ + +ivas_error IVAS_REND_Open( + IVAS_REND_HANDLE *phIvasRend, /* i/o: Pointer to renderer handle */ + const int32_t outputSampleRate, /* i : output sampling rate */ + const IVAS_REND_AudioConfig outConfig /* i : output audio config */ +); + +/* Note: this will reset custom LFE routings set for any MC input */ +ivas_error IVAS_REND_ConfigureCustomOutputLoudspeakerLayout( + IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */ + const IVAS_CUSTOM_LS_DATA layout /* i : custom loudspeaker layout for renderer output */ +); + +/* Support for custom HRTFs will be added in the future. */ +/* Note: this affects output delay */ +ivas_error IVAS_REND_SetCustomHrtf( + IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */ + void* TODO +); + +/* Functions to be called before/during rendering */ + +ivas_error IVAS_REND_NumOutChannels( + IVAS_REND_CONST_HANDLE hIvasRend, /* i : Renderer handle */ + int16_t *numOutChannels /* o : number of output channels */ +); + +ivas_error IVAS_REND_AddInput( + IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */ + const IVAS_REND_AudioConfig inConfig, /* i : audio config for a new input */ + IVAS_REND_InputId *inputId /* o : ID of the new input */ +); + +/* Note: this will reset any custom LFE routing set for the input */ +ivas_error IVAS_REND_ConfigureCustomInputLoudspeakerLayout( + IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */ + const IVAS_REND_InputId inputId, /* i : ID of the input */ + const IVAS_CUSTOM_LS_DATA layout /* i : custom loudspeaker layout for input */ +); + +ivas_error IVAS_REND_SetInputGain( + IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */ + const IVAS_REND_InputId inputId, /* i : ID of the input */ + const float gain /* i : linear gain (not in dB) */ +); + +ivas_error IVAS_REND_SetInputLfeRouting( + IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */ + const IVAS_REND_InputId inputId, /* i : ID of the input */ + const IVAS_REND_LfeRouting lfeRouting /* i : custom LFE routing struct */ +); + +ivas_error IVAS_REND_RemoveInput( + IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */ + const IVAS_REND_InputId inputId /* i : ID of the input */ +); + +ivas_error IVAS_REND_GetInputNumChannels( + IVAS_REND_CONST_HANDLE hIvasRend, /* i : Renderer handle */ + const IVAS_REND_InputId inputId, /* i : ID of the input */ + int16_t *numChannels /* o : number of channels of the input */ +); + +ivas_error IVAS_REND_GetDelay( + IVAS_REND_CONST_HANDLE hIvasRend, /* i : Renderer handle */ + int16_t *nSamples, /* o : Renderer delay in samples */ + int32_t *timeScale /* o : Time scale of the delay, equal to renderer output sampling rate */ +); + +/* Functions to be called during rendering */ + +ivas_error IVAS_REND_FeedInputAudio( + IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */ + const IVAS_REND_InputId inputId, /* i : ID of the input */ + const IVAS_REND_ReadOnlyAudioBuffer inputAudio /* i : buffer with input audio */ +); + +ivas_error IVAS_REND_FeedInputObjectMetadata( + IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */ + const IVAS_REND_InputId inputId, /* i : ID of the input */ + const IVAS_REND_AudioObjectPosition objectPosition /* i : object position struct */ +); + +/* Support for MASA input will be added in the future. */ +ivas_error IVAS_REND_FeedInputMasaMetadata( + IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */ + const IVAS_REND_InputId inputId, /* i : ID of the input */ + void* TODO +); + +ivas_error IVAS_REND_InitConfig( + IVAS_REND_HANDLE st, /* i/o: Renderer handle */ + bool rendererConfigEnabled /* i : flag indicating if a renderer configuration file was supplied */ +); + +int16_t IVAS_REND_GetRenderConfig( + IVAS_REND_HANDLE hIvasRend, /* i/o: IVAS decoder handle */ + const IVAS_RENDER_CONFIG_HANDLE hRCout /* o : Render configuration handle */ +); + +int16_t IVAS_REND_FeedRenderConfig( + IVAS_REND_HANDLE hIvasRend, /* i/o: IVAS decoder handle */ + const IVAS_RENDER_CONFIG_DATA renderConfig /* i : Render configuration struct */ +); + +ivas_error IVAS_REND_SetHeadRotation( + IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */ + const IVAS_QUATERNION headRot[RENDERER_HEAD_POSITIONS_PER_FRAME] /* i : head positions for next rendering call */ +); + +ivas_error IVAS_REND_GetSamples( + IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */ + IVAS_REND_AudioBuffer outAudio /* i/o: buffer for output audio */ +); + +/* Functions to be called after rendering */ + +void IVAS_REND_Close( + IVAS_REND_HANDLE* phIvasRend /* i/o: Pointer to renderer handle */ +); + +#ifdef DEBUGGING +int32_t IVAS_REND_GetNoCLipping( + IVAS_REND_CONST_HANDLE hIvasRend /* i : Renderer handle */ +); + +int32_t IVAS_REND_GetCntFramesLimited( + IVAS_REND_CONST_HANDLE hIvasRend /* i : Renderer handle */ +); +#endif + +/* clang-format on */ + +#endif /* LIB_REND_H */ +#endif diff --git a/lib_util/audio_file_reader.c b/lib_util/audio_file_reader.c index d391735a9457cdc3062b5da037172dd1c6be458c..d2c38cbb17327b88784b6880e16a64cc68e03450 100644 --- a/lib_util/audio_file_reader.c +++ b/lib_util/audio_file_reader.c @@ -32,6 +32,7 @@ #include "audio_file_reader.h" #include "tinywavein_c.h" +#include #include #include "wmops.h" @@ -54,12 +55,13 @@ static int8_t AudioFileReader_open_raw( static int8_t AudioFileReader_open_wav( AudioFileReader *self, const char *fileName, - uint32_t expSampleRate ) + int32_t *sampleRate ) { - uint32_t sampleRate, samplesInFile; + uint32_t sampleRate_, samplesInFile; int16_t bps; - self->wavFile = OpenWav( fileName, &sampleRate, &self->numChannels, &samplesInFile, &bps ); + self->wavFile = OpenWav( fileName, &sampleRate_, &self->numChannels, &samplesInFile, &bps ); + *sampleRate = sampleRate_; if ( !self->wavFile ) { @@ -67,21 +69,15 @@ static int8_t AudioFileReader_open_wav( return -1; } - if ( sampleRate != expSampleRate ) - { - fprintf( stderr, "Input wav file has unexpected samplerate (should be %d): %s\n", expSampleRate, fileName ); - return -1; - } - return 0; } /*! r: AudioFileReader handle */ ivas_error AudioFileReader_open( - AudioFileReader **audioReader, /* o : AudioFileReader handle */ - const char *fileName, /* i : path to wav/raw pcm file */ - uint32_t expSampleRate /* i : expected sample rate */ + AudioFileReader **audioReader, /* o : AudioFileReader handle */ + const char *fileName, /* i : path to wav/raw pcm file */ + int32_t *sampleRate /* o : sample rate of wav file, unused with pcm */ ) { AudioFileReader *self; @@ -108,7 +104,7 @@ ivas_error AudioFileReader_open( if ( fileNameLen > wavSuffixLen && strncmp( fileName + fileNameLen - wavSuffixLen, wavSuffix, wavSuffixLen ) == 0 ) { - retCode = AudioFileReader_open_wav( self, fileName, expSampleRate ); + retCode = AudioFileReader_open_wav( self, fileName, sampleRate ); } else { diff --git a/lib_util/audio_file_reader.h b/lib_util/audio_file_reader.h index d878e3cab42cc4ac5e478bcc266f5784f2c7ecd7..0fc9b1f4a1b0c175d4bda22e18ce05cfd3afbc21 100644 --- a/lib_util/audio_file_reader.h +++ b/lib_util/audio_file_reader.h @@ -42,9 +42,9 @@ typedef struct AudioFileReader AudioFileReader; /* clang-format off */ ivas_error AudioFileReader_open( - AudioFileReader **audioReader, /* o : AudioFileReader handle */ - const char *fileName, /* i : path to wav/raw pcm file */ - uint32_t expSampleRate /* i : expected sample rate */ + AudioFileReader **audioReader, /* o : AudioFileReader handle */ + const char *fileName, /* i : path to wav/raw pcm file */ + int32_t *sampleRate /* o : sample rate of wav file, unused with pcm */ ); /*! r: number of read samples */ diff --git a/lib_util/cmdln_parser.c b/lib_util/cmdln_parser.c new file mode 100644 index 0000000000000000000000000000000000000000..1904f880beffe875eab46023c73d7461ca810533 --- /dev/null +++ b/lib_util/cmdln_parser.c @@ -0,0 +1,365 @@ +/****************************************************************************************************** + + (C) 2022 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository. All Rights Reserved. + + This software is protected by copyright law and by international treaties. + The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository retain full ownership rights in their respective contributions in + the software. This notice grants no license of any kind, including but not limited to patent + license, nor is any license granted by implication, estoppel or otherwise. + + Contributors are required to enter into the IVAS codec Public Collaboration agreement before making + contributions. + + This software is provided "AS IS", without any express or implied warranties. The software is in the + development stage. It is intended exclusively for experts who have experience with such software and + solely for the purpose of inspection. All implied warranties of non-infringement, merchantability + and fitness for a particular purpose are hereby disclaimed and excluded. + + Any dispute, controversy or claim arising under or in relation to providing this software shall be + submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in + accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and + the United Nations Convention on Contracts on the International Sales of Goods. + +*******************************************************************************************************/ + +#include "cmdln_parser.h" + +#include +#include +#include +#include + +#define MAX_SUPPORTED_OPTS ( 1024 ) +#define MAX_OPTION_LENGTH ( 1024 ) + +typedef CmdLnParser_Option OptionProps; + +typedef struct +{ + OptionProps props; + int8_t hasBeenParsed; +} Option; + +static int16_t validateNoDuplicateIds( const OptionProps *props, int32_t numOpts ) +{ + for ( int32_t i = 0; i < numOpts; ++i ) + { + for ( int32_t j = i + 1; j < numOpts; ++j ) + { + if ( props[i].id == props[j].id ) + { + fprintf( stderr, "[dev] Duplicate ID == %d between options %s and %s\n", props[i].id, props[i].match, props[j].match ); + return -1; + } + } + } + + return 0; +} + +static int16_t validateOptionProps( OptionProps props ) +{ + /* Check required properties */ + if ( props.match == NULL ) + { + /* TODO(sgi): Don't print out usage after this - props.match is used there */ + fprintf( stderr, "[dev] Option with ID == %d - missing required property \"match\"\n", props.id ); + return -1; + } + + if ( props.id == 0 ) + { + fprintf( stderr, "[dev] Invalid ID for option %s. ID == %d is reserved.\n", props.match, props.id ); + return -1; + } + + return 0; +} + +/* Validate given OptionProps and use them to initialize array of Options */ +static int16_t initOpts( const OptionProps *options, int32_t numOpts, Option *opts ) +{ + for ( int32_t i = 0; i < numOpts; ++i ) + { + if ( validateOptionProps( options[i] ) != 0 ) + { + return -1; + } + + Option tmp = { + .hasBeenParsed = 0 + }; + tmp.props = options[i]; /* Cannot assign in aggregate initializer above - causes Visual Studio warning */ + + opts[i] = tmp; + } + + /* Check for duplicate IDs */ + if ( validateNoDuplicateIds( options, numOpts ) != 0 ) + { + return -1; + } + + return 0; +} + +static int8_t stringLooksLikeOption( const char *str ) +{ + if ( str[0] == '-' ) + { + return 1; + } + + return 0; +} + +static const char *stringToOptionName( const char *str ) +{ + while ( *str == '-' ) + { + ++str; + } + + return str; +} + +static int8_t optionMatchesString( Option opt, const char *str ) +{ + if ( !stringLooksLikeOption( str ) ) + { + return 0; + } + + const char *optionName = stringToOptionName( str ); + + if ( strncmp( optionName, opt.props.match, MAX_OPTION_LENGTH ) == 0 || strncmp( optionName, opt.props.matchShort, MAX_OPTION_LENGTH ) == 0 ) + { + return 1; + } + + return 0; +} + +static int16_t parseOpts( int32_t argc, + char **argv, + Option *opts, + int32_t numOpts, + void *pOutputStruct, + CmdLnParser_FnPtr_ParseOption parseOption ) +{ + Option *currOpt = NULL; + int32_t currOptIdx = 1; + Option *nextOpt = NULL; + int32_t nextOptIdx = 0; + int16_t numValues = 0; + + /* Go through all given argv */ + for ( int32_t argIdx = 1; argIdx < argc; ++argIdx ) + { + /* For current argument from argv go through all options and try to match */ + for ( int32_t optIdx = 0; optIdx < numOpts; ++optIdx ) + { + Option *optToMatch = &opts[optIdx]; + if ( optionMatchesString( *optToMatch, argv[argIdx] ) ) + { + nextOpt = optToMatch; + nextOptIdx = argIdx; + + /* Check if already parsed */ + if ( optToMatch->hasBeenParsed ) + { + fprintf( stderr, "Duplicate option: %s (%s)\n", optToMatch->props.match, optToMatch->props.matchShort ); + return -1; + } + + break; + } + } + + /* If no option matched, it is either a value belonging to current option or an invalid option */ + if ( nextOpt == NULL ) + { + /* Invalid option */ + if ( stringLooksLikeOption( argv[argIdx] ) ) + { + fprintf( stderr, "Unknown option `%s`\n", stringToOptionName( argv[argIdx] ) ); + return -1; + } + + /* Otherwise, value following current option. + * Exception: at the beginning of parsing (when current option is NULL) no values are allowed, throw error*/ + if ( currOpt != NULL ) + { + ++numValues; + } + else + { + fprintf( stderr, "Unexpected token `%s`\n", argv[argIdx] ); + return -1; + } + } + + /* If current argument is a recognized option or no more arguments left, parse current option into output struct*/ + if ( nextOpt != NULL ) + { + if ( currOpt != NULL ) + { + parseOption( currOpt->props.id, &argv[currOptIdx + 1], numValues, pOutputStruct ); + currOpt->hasBeenParsed = 1; + } + + currOpt = nextOpt; + currOptIdx = nextOptIdx; + nextOpt = NULL; + nextOptIdx = 0; + numValues = 0; + } + } + + /* Parse last option */ + if ( currOpt != NULL ) + { + parseOption( currOpt->props.id, &argv[currOptIdx + 1], numValues, pOutputStruct ); + currOpt->hasBeenParsed = 1; + } + + return 0; +} + +static const char *getBasename( const char *path ) +{ + /* Find last forward slash in path */ + const char *namePtr = strrchr( path, '/' ); + if ( namePtr != NULL ) + { + return namePtr + 1; + } + + /* If not found, try to find last backslash in path */ + namePtr = strrchr( path, '\\' ); + if ( namePtr != NULL ) + { + return namePtr + 1; + } + + /* If also not found, return full path, which implictly should be the basename */ + return path; +} + +static int32_t totalOptionNameLength( const OptionProps opt ) +{ + return strlen( opt.match ) + strlen( opt.matchShort ); +} + +static void printWhitespace( int32_t n ) +{ + for ( int32_t i = 0; i < n; ++i ) + { + fprintf( stderr, " " ); + } +} + +static void printOptDescriptionAligned( const char *descPtr, int32_t descriptionColumnIdx ) +{ + if ( descPtr == NULL ) + { + fprintf( stderr, "\n" ); + return; + } + + while ( 1 ) + { + if ( *descPtr == '\0' ) + { + fprintf( stderr, "\n" ); + break; + } + + fprintf( stderr, "%c", *descPtr ); + if ( *descPtr == '\n' ) + { + printWhitespace( descriptionColumnIdx ); + } + ++descPtr; + } +} + +static void printUsage( + const char *argv0, + const OptionProps *optionProps, + int32_t numOptions ) +{ + fprintf( stderr, "\n" ); + fprintf( stderr, "Usage: %s [options]\n", getBasename( argv0 ) ); + fprintf( stderr, "\n" ); + fprintf( stderr, "Valid options:\n" ); + + /* Find option with longest name, used for pretty formatting */ + int32_t maxOptNameLength = 0; + for ( int32_t i = 0; i < numOptions; ++i ) + { + const int32_t optNameLength = totalOptionNameLength( optionProps[i] ); + if ( maxOptNameLength < optNameLength ) + { + maxOptNameLength = optNameLength; + } + } + + const int32_t preDescriptionWhitespace = 8; + const int32_t leftColumnAdditionalChars = 7; + for ( int32_t i = 0; i < numOptions; ++i ) + { + OptionProps opt = optionProps[i]; + const int32_t optNameLength = totalOptionNameLength( optionProps[i] ); + + /* TODO(sgi): make matchShort optional */ + fprintf( stderr, " --%s, -%s", opt.match, opt.matchShort ); + + printWhitespace( maxOptNameLength - optNameLength + preDescriptionWhitespace ); + printOptDescriptionAligned( opt.description, maxOptNameLength + preDescriptionWhitespace + leftColumnAdditionalChars ); + } +} + +int16_t CmdLnParser_parseArgs( int32_t argc, + char **argv, + const OptionProps *optionProps, + int32_t numOptions, + void *pOutputStruct, + CmdLnParser_FnPtr_ParseOption parseOption ) +{ + assert( numOptions <= MAX_SUPPORTED_OPTS ); + + /* Prepare option array */ + Option opts[MAX_SUPPORTED_OPTS]; + if ( initOpts( optionProps, numOptions, opts ) != 0 ) + { + goto fail; + } + + /* Iterate over argv and parse */ + if ( parseOpts( argc, argv, opts, numOptions, pOutputStruct, parseOption ) != 0 ) + { + goto fail; + } + + return 0; + +fail: + printUsage( argv[0], optionProps, numOptions ); + return -1; +} + +void CmdLnParser_printUsage( char *executableName, + const CmdLnParser_Option *options, + int32_t numOptions ) +{ + printUsage( executableName, options, numOptions ); +} diff --git a/lib_util/ivas_rom_prerenderer.h b/lib_util/cmdln_parser.h similarity index 63% rename from lib_util/ivas_rom_prerenderer.h rename to lib_util/cmdln_parser.h index 6be6ae9863aa5f0ae109633b6b30092786f3d3a5..8fecc70d9e6c0149687322c878376764f5a36c66 100644 --- a/lib_util/ivas_rom_prerenderer.h +++ b/lib_util/cmdln_parser.h @@ -30,24 +30,38 @@ *******************************************************************************************************/ -#ifndef IVAS_ROM_PRERENDERER_H -#define IVAS_ROM_PRERENDERER_H +#ifndef CMDLN_PARSER_H +#define CMDLN_PARSER_H #include +#include + #include "options.h" -/*----------------------------------------------------------------------------------* - * Prerenderer SBA & MC enc/dec matrices - *----------------------------------------------------------------------------------*/ - -extern const float hoa_dec_mtx_CICP1[16]; -extern const float ls_azimuth_CICP1[1]; -extern const float ls_elevation_CICP1[1]; -extern const uint32_t ls_LFE_last_idx_CICP1[1]; -extern const uint32_t ls_LFE_last_idx_CICP2[2]; -extern const uint32_t ls_LFE_last_idx_CICP6[6]; -extern const uint32_t ls_LFE_last_idx_CICP12[8]; -extern const uint32_t ls_LFE_last_idx_CICP16[10]; -extern const uint32_t ls_LFE_last_idx_CICP19[12]; - -#endif /* IVAS_ROM_PRERENDERER_H */ +typedef struct +{ + int32_t id; + const char *match; + const char *matchShort; + const char *description; +} CmdLnParser_Option; + +/* Function for parsing option values into an output struct, to be implemented by the user */ +typedef void ( *CmdLnParser_FnPtr_ParseOption )( int32_t optionId, /* i : option ID of matched option */ + char **optionValues, /* i : array of string values following the matched option in argv */ + int16_t numOptionValues, /* i : number of string values following the matched option in argv */ + void *pOutputStruct /* o : struct to store parsed values */ +); + +int16_t CmdLnParser_parseArgs( int32_t argc, + char **argv, + const CmdLnParser_Option *options, + int32_t numOptions, + void *pOutputStruct, + CmdLnParser_FnPtr_ParseOption parseOption ); + +void CmdLnParser_printUsage( char *executableName, + const CmdLnParser_Option *options, + int32_t numOptions ); + +#endif /* CMDLN_PARSER_H */ diff --git a/lib_util/ivas_prerenderer.c b/lib_util/ivas_prerenderer.c deleted file mode 100644 index a2c0b4c7104d40f44f9986ff3a944264cd9ff30d..0000000000000000000000000000000000000000 --- a/lib_util/ivas_prerenderer.c +++ /dev/null @@ -1,1733 +0,0 @@ -/****************************************************************************************************** - - (C) 2022 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, - Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., - Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, - Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other - contributors to this repository. All Rights Reserved. - - This software is protected by copyright law and by international treaties. - The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB, - Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., - Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, - Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other - contributors to this repository retain full ownership rights in their respective contributions in - the software. This notice grants no license of any kind, including but not limited to patent - license, nor is any license granted by implication, estoppel or otherwise. - - Contributors are required to enter into the IVAS codec Public Collaboration agreement before making - contributions. - - This software is provided "AS IS", without any express or implied warranties. The software is in the - development stage. It is intended exclusively for experts who have experience with such software and - solely for the purpose of inspection. All implied warranties of non-infringement, merchantability - and fitness for a particular purpose are hereby disclaimed and excluded. - - Any dispute, controversy or claim arising under or in relation to providing this software shall be - submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in - accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and - the United Nations Convention on Contracts on the International Sales of Goods. - -*******************************************************************************************************/ - -#include "options.h" -#include "ivas_prerenderer.h" -#include "ivas_prot.h" -#include "ivas_rom_com.h" -#include "ivas_rom_dec.h" -#include "ivas_rom_prerenderer.h" -#include "prot.h" -#include "wmops.h" - -#include -#include -#include -#include -#include - -#define LIMITER_THRESHOLD 0.9988493699f /* -0.01 dBFS */ - -struct Prndr_Prerenderer -{ - uint32_t sampleRate; - - int8_t isConfigured; /* flag */ - int8_t firstFrame; /* flag */ - - /* I/O */ - Prndr_InputConfig inConfig; - Prndr_OutputConfig outConfig; - - /* =========== Panning =========== */ - EFAP_HANDLE efapRenderer; - - Prndr_ObjPanInfo *objPanInfo; /* size: [numInObjects] */ - - float ***speakerPanGains; /* size: [numInMc][numSpeakers][numOutChannels] */ - - float *tmpGainBuffer; /* size: [numOutChannels] */ - float *noLfePanBuffer; /* size: [numOutChannels] */ - float *crossfade; /* size: [frameSize] */ - /* =============================== */ - - /* Helpers */ - int16_t numOutChannels; /* Total number of output channels */ - int16_t numInChannels; /* Total number of input channels */ - int16_t numInChannelsObj; /* Total number of input channels of object inputs */ - int16_t numInChannelsAmbi; /* Total number of input channels of ambisonics inputs */ - int16_t numInChannelsMc; /* Total number of input channels of multichannel inputs */ - - /* For each channel of MC inputs mcPassThrough contains the corresponding - * output channel index if a passthrough is possible, otherwise contains -1 */ - int32_t *mcPassthrough; /* size: [numInChannelsMc] */ - - /* =========== LFE Handling =========== */ - /* Do not drop LFE when rendering to a layout that does not have - * an LFE channel - render LFE into other channels*/ - int8_t neverDropLfe; /* flag */ - float *lfePanGains; - - /* =========== limiter handle =========== */ - IVAS_LIMITER_HANDLE hLimiter; - - /* Ambisonics decoding matrix */ - float *ambi_dec_mtx; -}; - -/*---------------------------------------------------------------------* - * Prototypes - *---------------------------------------------------------------------*/ -/* clang-off */ -static void renderAmbiToAmbi( - const Prndr_Prerenderer *const st, - const Prndr_AudioBuffer inAudio, - Prndr_AudioBuffer outAudio ); - -static void renderChannelsToAmbi( - Prndr_Prerenderer *const st, - const Prndr_AudioBuffer inAudio, - Prndr_AudioBuffer outAudio ); - -static void renderObjectsToAmbi( - Prndr_Prerenderer *const st, - const Prndr_AudioBuffer inAudio, - const Prndr_AudioObjectMetadataBuffer metadataBuffer, - Prndr_AudioBuffer outAudio ); - -static void renderAmbiToChannels( - const Prndr_Prerenderer *const st, - const Prndr_AudioBuffer inAudio, - Prndr_AudioBuffer outAudio ); - -static void renderChannelsToChannels( - const Prndr_Prerenderer *const st, - const Prndr_AudioBuffer inAudio, - Prndr_AudioBuffer outAudio ); - -static void renderObjectsToChannels( - Prndr_Prerenderer *const st, - const Prndr_AudioBuffer inAudio, - const Prndr_AudioObjectMetadataBuffer metadataBuffer, - Prndr_AudioBuffer outAudio ); - -static void renderSingleObjectToAmbi( - Prndr_Prerenderer *const st, - const Prndr_AudioBuffer inAudio, - const uint32_t itemChnlIdx, /* Index of the item within input audio buffer */ - Prndr_ObjPanInfo *prevPanInfo, - const Prndr_AudioObjectPosition curFrmPos, - const float gain_lin, - Prndr_AudioBuffer outAudio ); - -static void renderSingleObjectToChannels( - Prndr_Prerenderer *const st, - const Prndr_AudioBuffer inAudio, - const uint32_t itemChnlIdx, /* Index of the item within input audio buffer */ - Prndr_ObjPanInfo *prevPanInfo, - const Prndr_AudioObjectPosition curFrmPos, - const float gain_lin, - Prndr_AudioBuffer outAudio ); - -/* Multiply a single channel by a vector of gains and add result to corresponding output channels */ -static void applyChannelGainsAndAddToOutput( - const Prndr_AudioBuffer inAudio, - const uint32_t itemChnlIdx, /* Index of the item within input audio buffer */ - const float *const gainsCurrent, /* Vector of gains for current frame, corresponding to output channels */ - const float *const gainsPrev, /* Vector of previously applied gains, used for interpolation. Set to NULL for no interpolation */ - const float gain_lin, /* Additional linear gain to be applied when mixing with output buffer */ - const float *const crossfade, - Prndr_AudioBuffer outAudio ); - -static void prepareMcPanGains( - Prndr_Prerenderer *const st ); - -static void prepareLfeHandling( - Prndr_Prerenderer *const st ); - -static void prepareMcPassthrough( - Prndr_Prerenderer *const st ); - -static void passthroughChannel( - const Prndr_AudioBuffer inAudio, - const uint32_t srcChnlIdx, - const uint32_t dstChnlIdx, - const float gain_lin, - Prndr_AudioBuffer outAudio ); - -static void getSpeakerGains( - const Prndr_Prerenderer *const st, - const float azi, - const float ele, - float *const spkGains ); - -static int16_t Prndr_getNumChannelsAmbisonics( - Prndr_Ambisonics ambisonics ); - -static int16_t Prndr_getAmbisonicsOrder( - Prndr_Ambisonics ambisonics ); - -static int16_t Prndr_getNumChannelsInSpeakerLayout( - Prndr_SpeakerLayout layout ); - -static int16_t Prndr_getNumNonLfeChannelsInSpeakerLayout( - Prndr_SpeakerLayout layout ); - -static const float *Prndr_getSpeakerAzimuths( - Prndr_SpeakerLayout layout ); - -static const float *Prndr_getSpeakerElevations( - Prndr_SpeakerLayout layout ); - -static const uint32_t *Prndr_getReorderedChannelIndices( - Prndr_SpeakerLayout layout ); - -static ivas_error Prndr_getHoaRenderMtx( - const Prndr_OutputConfig outConfig, - float **decMtx, - uint32_t ambiOrder ); - -static void Prndr_getHoaDecVecForAmbiChnl( - uint32_t ambiChnnlIdx, - const Prndr_OutputConfig outConfig, - const float *decMtx, - float *decCoeffs ); - -static void ivas_limiter_prerenderer( - IVAS_LIMITER_HANDLE hLimiter, /* i/o: limiter struct handle */ - float *output, /* i/o: I/O buffer */ - const int16_t output_frame, /* i : number of samples per channel in the buffer */ - const float threshold /* i : signal amplitude above which limiting starts to be applied */ -); - -static float dBToLin( const float gain_dB ); -/* clang-on */ -/* ========================================================================== */ - -Prndr_Prerenderer *Prndr_Prerenderer_open() -{ - Prndr_Prerenderer *st; - st = (Prndr_Prerenderer *) count_malloc( sizeof( Prndr_Prerenderer ) ); - st->isConfigured = 0; - st->efapRenderer = NULL; - - st->objPanInfo = NULL; - st->speakerPanGains = NULL; - st->tmpGainBuffer = NULL; - st->noLfePanBuffer = NULL; - st->crossfade = NULL; - st->mcPassthrough = NULL; - st->neverDropLfe = 0; - st->lfePanGains = NULL; - st->hLimiter = NULL; - st->ambi_dec_mtx = NULL; - - return st; -} - -ivas_error Prndr_Prerenderer_configure( Prndr_Prerenderer *const st, - const Prndr_InputConfig inConfig, - const Prndr_OutputConfig outConfig, - int32_t frameSize, - uint32_t sampleRate ) -{ - uint32_t i; - int32_t j; - ivas_error error; - - error = IVAS_ERR_OK; - - /* ============================= Error checks ============================= */ - assert( st != NULL && "Can't configure prerenderer - pointer is NULL" ); - assert( !st->isConfigured && "Re-configuring a prerenderer is not supported" ); - assert( !( outConfig.ambisonics != prndr_ambisonics_none && outConfig.speakerLayout != prndr_speaker_layout_none ) && "Multiple outputs not supported" ); - assert( !( outConfig.ambisonics == prndr_ambisonics_none && outConfig.speakerLayout == prndr_speaker_layout_none ) && "At least one output must be selected" ); - assert( !( inConfig.numAudioObjects == 0 && inConfig.numMultiChannelBuses == 0 && inConfig.numAmbisonicsBuses == 0 ) && "At least one input must be active" ); - - /* ========================== Store useful values ========================= */ - st->sampleRate = sampleRate; - st->isConfigured = 1; - st->firstFrame = 1; - st->inConfig = inConfig; - st->outConfig = outConfig; - - /* Save total number of channels in ambisonics inputs */ - st->numInChannelsAmbi = 0; - for ( i = 0; i < inConfig.numAmbisonicsBuses; ++i ) - { - st->numInChannelsAmbi += Prndr_getNumChannelsAmbisonics( inConfig.ambisonicsBuses[i].ambisonicsConfig ); - } - - /* Save total number of channels in MC input */ - st->numInChannelsMc = 0; - for ( i = 0; i < inConfig.numMultiChannelBuses; ++i ) - { - st->numInChannelsMc += Prndr_getNumChannelsInSpeakerLayout( inConfig.multiChannelBuses[i].speakerLayout ); - } - - /* Save total number of channels of audio object inputs */ - st->numInChannelsObj = st->inConfig.numAudioObjects; - - /* Save total number of input channels */ - st->numInChannels = st->numInChannelsObj + st->numInChannelsAmbi + st->numInChannelsMc; - - if ( st->outConfig.ambisonics != prndr_ambisonics_none ) - { - /* Save number of output channels */ - st->numOutChannels = Prndr_getNumChannelsAmbisonics( st->outConfig.ambisonics ); - } - - /* ============================ Prepare panning and ambisonics =========================== */ - if ( st->outConfig.speakerLayout != prndr_speaker_layout_none ) - { - if ( st->outConfig.speakerLayout == prndr_speaker_layout_custom ) - { - /* Save number of output channels */ - st->numOutChannels = st->outConfig.outSetupCustom->num_spk + st->outConfig.outSetupCustom->num_lfe; - - /* Open and initialize EFAP struct */ - if ( ( error = efap_init_data( &st->efapRenderer, st->outConfig.outSetupCustom->ls_azimuth, st->outConfig.outSetupCustom->ls_elevation, st->outConfig.outSetupCustom->num_spk, EFAP_MODE_EFAP ) ) != IVAS_ERR_OK ) - { - return error; - } - } - else - { - /* Save number of output channels */ - st->numOutChannels = Prndr_getNumChannelsInSpeakerLayout( st->outConfig.speakerLayout ); - - /* Open and initialize EFAP struct */ - if ( ( error = efap_init_data( &st->efapRenderer, Prndr_getSpeakerAzimuths( st->outConfig.speakerLayout ), Prndr_getSpeakerElevations( st->outConfig.speakerLayout ), Prndr_getNumNonLfeChannelsInSpeakerLayout( st->outConfig.speakerLayout ), EFAP_MODE_EFAP ) ) != IVAS_ERR_OK ) - { - return error; - } - } - - - assert( st->efapRenderer != NULL && "Could not init EFAP" ); - - /* Compute Ambisonics to loudspeaker decoding matrix */ - if ( ( error = Prndr_getHoaRenderMtx( st->outConfig, &st->ambi_dec_mtx, 3 ) ) != IVAS_ERR_OK ) - { - return error; - } - } - - /* Allocate temporary pan/enc buffer to avoid allocations during rendering */ - st->tmpGainBuffer = count_calloc( st->numOutChannels, sizeof( float ) ); - - /* Allocate temporary buffer for panning gains with lfe omitted */ - if ( st->outConfig.speakerLayout != prndr_speaker_layout_none ) - { - if ( st->outConfig.speakerLayout == prndr_speaker_layout_custom ) - { - st->noLfePanBuffer = count_calloc( st->outConfig.outSetupCustom->num_spk, sizeof( float ) ); - } - else - { - st->noLfePanBuffer = count_calloc( Prndr_getNumNonLfeChannelsInSpeakerLayout( st->outConfig.speakerLayout ), sizeof( float ) ); - } - } - - /* Create lookup tables for panning/encoding speaker signals */ - if ( st->inConfig.numMultiChannelBuses != 0 ) - { - prepareMcPanGains( st ); - prepareLfeHandling( st ); - } - - /* Allocate structs for interpolation of object pan/enc gains between frames */ - if ( st->inConfig.numAudioObjects != 0 ) - { - st->objPanInfo = count_calloc( st->inConfig.numAudioObjects, sizeof( Prndr_ObjPanInfo ) ); - - for ( i = 0; i < st->inConfig.numAudioObjects; ++i ) - { - st->objPanInfo[i].panGains = count_calloc( st->numOutChannels, sizeof( float ) ); - } - } - - /* =========================== Prepare crossfades ========================= */ - st->crossfade = count_calloc( frameSize, sizeof( float ) ); - - for ( j = 0; j < frameSize; ++j ) - { - st->crossfade[j] = (float) j / ( frameSize - 1 ); - } - - /* ========================= Prepare optimizations ======================== */ - /* Make note of possible processing shortcuts in cases where input and output - * config is the same or similar. This only needs to be done for MC I/O, since - * Ambisonics I/O can always be passed through and objects can never be passed - * through */ - if ( st->inConfig.numMultiChannelBuses > 0 && st->outConfig.speakerLayout != prndr_speaker_layout_none ) - { - prepareMcPassthrough( st ); - } - - /* ============================ Configure limiter =========================== */ - st->hLimiter = ivas_limiter_open( st->numOutChannels, st->sampleRate ); - - return error; -} - -void Prndr_Prerenderer_render( - Prndr_Prerenderer *const st, - const Prndr_AudioBuffer inAudio, - const Prndr_AudioObjectMetadataBuffer metadataBuffer, - Prndr_AudioBuffer outAudio ) -{ -#ifdef WMOPS - wmops_sub_start( "Prndr_Prerenderer_render" ); -#endif - - /* ============================= Error checks ============================= */ - assert( st != NULL && "Can't render - prerenderer pointer is NULL" ); - assert( st->isConfigured && "Can't render - prerenderer pointer is not configured" ); - assert( inAudio.config.sampleRate == outAudio.config.sampleRate && "Input and output sample rate must be the same" ); - assert( inAudio.config.bufferSize == outAudio.config.bufferSize && "Input and output frame size must be the same" ); - assert( inAudio.config.numChannels == st->numInChannels && "Number of input channels does not match between prerenderer and input config" ); - assert( outAudio.config.numChannels == st->numOutChannels && "Number of input channels does not match between prerenderer and input config" ); - assert( inAudio.config.sampleRate != 0 && "Invalid sample rate" ); - assert( inAudio.config.bufferSize != 0 && "Invalid frame size" ); - assert( inAudio.data != NULL && "Can't render - input buffer is empty" ); - - /* ========================== Actual processing =========================== */ - /* Clear output buffer */ - set_zero( outAudio.data, outAudio.config.numChannels * outAudio.config.bufferSize ); - - /* Render target format: Ambisonics */ - if ( st->outConfig.ambisonics != prndr_ambisonics_none ) - { - if ( st->inConfig.numAmbisonicsBuses != 0 ) - { - renderAmbiToAmbi( st, inAudio, outAudio ); - } - - if ( st->inConfig.numMultiChannelBuses != 0 ) - { - renderChannelsToAmbi( st, inAudio, outAudio ); - } - - if ( st->inConfig.numAudioObjects != 0 ) - { - renderObjectsToAmbi( st, inAudio, metadataBuffer, outAudio ); - } - } /* Render target format: multichannel */ - else if ( st->outConfig.speakerLayout != prndr_speaker_layout_none ) - { - if ( st->inConfig.numAmbisonicsBuses != 0 ) - { - renderAmbiToChannels( st, inAudio, outAudio ); - } - - if ( st->inConfig.numMultiChannelBuses != 0 ) - { - renderChannelsToChannels( st, inAudio, outAudio ); - } - - if ( st->inConfig.numAudioObjects != 0 ) - { - renderObjectsToChannels( st, inAudio, metadataBuffer, outAudio ); - } - } - - /* Apply limiting in place */ - ivas_limiter_prerenderer( - st->hLimiter, - outAudio.data, - outAudio.config.bufferSize, - LIMITER_THRESHOLD ); - - if ( st->firstFrame ) - { - st->firstFrame = 0; - } - -#ifdef WMOPS - wmops_sub_end(); -#endif -} - -void Prndr_Prerenderer_setNeverDropLfe( - Prndr_Prerenderer *st, - int8_t neverDropLfe ) -{ - st->neverDropLfe = neverDropLfe; -} - -int16_t Prndr_Prerenderer_getInChannels( - Prndr_Prerenderer *st ) -{ - assert( st != NULL && "Can't get number of input channels - prerenderer pointer is NULL" ); - if ( st ) - { - return st->numInChannels; - } - return 0; -} - -int16_t Prndr_Prerenderer_getOutChannels( - Prndr_Prerenderer *st ) -{ - assert( st != NULL && "Can't get number of output channels - prerenderer pointer is NULL" ); - if ( st ) - { - return st->numOutChannels; - } - return 0; -} - -void Prndr_Prerenderer_close( Prndr_Prerenderer *st ) -{ - uint32_t i; - uint32_t j; - uint32_t numChannels; - assert( st != NULL && "Can't close prerenderer - pointer is NULL" ); - - if ( st->efapRenderer != NULL ) - { - efap_free_data( &st->efapRenderer ); - } - - if ( st->objPanInfo != NULL ) - { - for ( i = 0; i < st->inConfig.numAudioObjects; ++i ) - { - if ( st->objPanInfo[i].panGains != NULL ) - { - count_free( st->objPanInfo[i].panGains ); - } - } - - count_free( st->objPanInfo ); - } - - if ( st->speakerPanGains != NULL ) - { - for ( i = 0; i < st->inConfig.numMultiChannelBuses; ++i ) - { - numChannels = Prndr_getNumChannelsInSpeakerLayout( st->inConfig.multiChannelBuses[i].speakerLayout ); - - for ( j = 0; j < numChannels; ++j ) - { - count_free( st->speakerPanGains[i][j] ); - } - - count_free( st->speakerPanGains[i] ); - } - - count_free( st->speakerPanGains ); - } - - if ( st->outConfig.speakerLayout == prndr_speaker_layout_custom ) - { - count_free( st->outConfig.outSetupCustom ); - st->outConfig.outSetupCustom = NULL; - } - - if ( st->tmpGainBuffer != NULL ) - { - count_free( st->tmpGainBuffer ); - } - - if ( st->noLfePanBuffer != NULL ) - { - count_free( st->noLfePanBuffer ); - } - - if ( st->crossfade != NULL ) - { - count_free( st->crossfade ); - } - - if ( st->mcPassthrough != NULL ) - { - count_free( st->mcPassthrough ); - } - - if ( st->lfePanGains ) - { - count_free( st->lfePanGains ); - } - - if ( st->ambi_dec_mtx != NULL ) - { - count_free( st->ambi_dec_mtx ); - } - - ivas_limiter_close( &st->hLimiter ); - - count_free( st ); -} - -/* ============================= Local functions ============================ */ -static float *get_smpl_ptr( Prndr_AudioBuffer buffer, uint32_t chnlIdx, uint32_t smplIdx ) -{ - return buffer.data + chnlIdx * buffer.config.bufferSize + smplIdx; -} - -static void renderAmbiToAmbi( const Prndr_Prerenderer *const st, - const Prndr_AudioBuffer inAudio, - Prndr_AudioBuffer outAudio ) -{ - float *inSmpl; - float *outSmpl; - int16_t lastChannelIdx; - int16_t smplIdx; - int16_t chnlIdx; - uint32_t inAmbiChannelIdx; - uint32_t ambiIdx; - float gain_lin; - - -#ifdef WMOPS - wmops_sub_start( "renderAmbiToAmbi" ); -#endif - - /* Iterate over given Ambisonics inputs */ - for ( ambiIdx = 0; ambiIdx < st->inConfig.numAmbisonicsBuses; ++ambiIdx ) - { - inAmbiChannelIdx = st->inConfig.ambisonicsBuses[ambiIdx].inputChannelIndex; - - /* Find out how many channels to process */ - lastChannelIdx = min( Prndr_getNumChannelsAmbisonics( st->inConfig.ambisonicsBuses[ambiIdx].ambisonicsConfig ), st->numOutChannels ) - 1; - - gain_lin = dBToLin( st->inConfig.ambisonicsBuses[ambiIdx].gain_dB ); - - /* Passthrough channels */ - for ( chnlIdx = 0; chnlIdx <= lastChannelIdx; ++chnlIdx ) - { - inSmpl = get_smpl_ptr( inAudio, chnlIdx + inAmbiChannelIdx, 0 ); - outSmpl = get_smpl_ptr( outAudio, chnlIdx, 0 ); - - for ( smplIdx = 0; smplIdx < inAudio.config.bufferSize; ++smplIdx ) - { - *outSmpl += *inSmpl * gain_lin; - - ++inSmpl; - ++outSmpl; - } - } - } - -#ifdef WMOPS - wmops_sub_end(); -#endif -} - -static void renderChannelsToAmbi( Prndr_Prerenderer *const st, - const Prndr_AudioBuffer inAudio, - Prndr_AudioBuffer outAudio ) -{ - const uint32_t *lfeLastIdxs; - uint32_t inMcChannelIdx; - uint32_t numNonLfeInChannels; - uint32_t numInChannels; - uint32_t mcIdx; - uint32_t inChIdx; - float gain_lin; - -#ifdef WMOPS - wmops_sub_start( "renderChannelsToAmbi" ); -#endif - - /* Iterate over given MC inputs */ - for ( mcIdx = 0; mcIdx < st->inConfig.numMultiChannelBuses; ++mcIdx ) - { - /* Get channel idx of current MC input within the multitrack buffer */ - inMcChannelIdx = st->inConfig.multiChannelBuses[mcIdx].inputChannelIndex; - - /* Number of input speakers */ - numInChannels = Prndr_getNumChannelsInSpeakerLayout( st->inConfig.multiChannelBuses[mcIdx].speakerLayout ); - numNonLfeInChannels = Prndr_getNumNonLfeChannelsInSpeakerLayout( st->inConfig.multiChannelBuses[mcIdx].speakerLayout ); - - lfeLastIdxs = Prndr_getReorderedChannelIndices( st->inConfig.multiChannelBuses[mcIdx].speakerLayout ); - - gain_lin = dBToLin( st->inConfig.multiChannelBuses[mcIdx].gain_dB ); - - /* Iterate over channels */ - for ( inChIdx = 0; inChIdx < numNonLfeInChannels; ++inChIdx ) - { - applyChannelGainsAndAddToOutput( inAudio, - inMcChannelIdx + lfeLastIdxs[inChIdx], - st->speakerPanGains[mcIdx][inChIdx], - NULL, - gain_lin, - st->crossfade, - outAudio ); - } - - if ( st->neverDropLfe ) - { - /* Render LFE channels into the scene */ - for ( ; inChIdx < numInChannels; ++inChIdx ) - { - applyChannelGainsAndAddToOutput( inAudio, - inMcChannelIdx + lfeLastIdxs[inChIdx], - st->lfePanGains, - NULL, - gain_lin, - st->crossfade, - outAudio ); - } - } - } - -#ifdef WMOPS - wmops_sub_end(); -#endif -} - -static void renderObjectsToAmbi( - Prndr_Prerenderer *const st, - const Prndr_AudioBuffer inAudio, - const Prndr_AudioObjectMetadataBuffer metadataBuffer, - Prndr_AudioBuffer outAudio ) -{ - const Prndr_AudioObject *curObj; - uint32_t objIdx; - Prndr_AudioObjectPosition pos; - float gain_lin; - -#ifdef WMOPS - wmops_sub_start( "renderObjectsToAmbi" ); -#endif - - assert( st->inConfig.numAudioObjects == metadataBuffer.numObjects && "Metadata provided for a different number of objects than found in input" ); - - /* Iterate over given audio objects */ - for ( objIdx = 0; objIdx < st->inConfig.numAudioObjects; ++objIdx ) - { - /* Get pointer to current object and its metadata */ - curObj = &st->inConfig.audioObjects[objIdx]; - pos = metadataBuffer.positions[objIdx]; - gain_lin = dBToLin( st->inConfig.audioObjects[objIdx].gain_dB ); - - /* Render to ambisonics */ - renderSingleObjectToAmbi( - st, - inAudio, - curObj->inputChannelIndex, - &st->objPanInfo[objIdx], - pos, - gain_lin, - outAudio ); - } - -#ifdef WMOPS - wmops_sub_end(); -#endif -} - -static void renderAmbiToChannels( const Prndr_Prerenderer *const st, - const Prndr_AudioBuffer inAudio, - Prndr_AudioBuffer outAudio ) -{ - uint32_t inAmbiChannelIdx; - uint32_t ambiIdx; - uint32_t numInAmbiChnls; - uint32_t ambiChnIdx; - float gain_lin; - -#ifdef WMOPS - wmops_sub_start( "renderAmbiToChannels" ); -#endif - - /* Iterate over all given ambisonics inputs */ - for ( ambiIdx = 0; ambiIdx < st->inConfig.numAmbisonicsBuses; ++ambiIdx ) - { - inAmbiChannelIdx = st->inConfig.ambisonicsBuses[ambiIdx].inputChannelIndex; - - /* Number of input channels */ - numInAmbiChnls = Prndr_getNumChannelsAmbisonics( st->inConfig.ambisonicsBuses[ambiIdx].ambisonicsConfig ); - - gain_lin = dBToLin( st->inConfig.multiChannelBuses[ambiIdx].gain_dB ); - - /* Render each ambisonics channel */ - for ( ambiChnIdx = 0; ambiChnIdx < numInAmbiChnls; ++ambiChnIdx ) - { - /* Write decoding gains to temp buffer */ - Prndr_getHoaDecVecForAmbiChnl( ambiChnIdx, st->outConfig, st->ambi_dec_mtx, st->tmpGainBuffer ); - - /* Apply decoding gains and add to output */ - applyChannelGainsAndAddToOutput( inAudio, - inAmbiChannelIdx + ambiChnIdx, - st->tmpGainBuffer, - NULL, - gain_lin, - st->crossfade, - outAudio ); - } - } - -#ifdef WMOPS - wmops_sub_end(); -#endif -} - -static void renderChannelsToChannels( - const Prndr_Prerenderer *const st, - const Prndr_AudioBuffer inAudio, - Prndr_AudioBuffer outAudio ) -{ - const uint32_t *lfeLastIdxs; - uint32_t inMcChannelIdx; - uint32_t numNonLfeInChannels; - uint32_t numInChannels; - uint32_t passThroughIdx; - uint32_t mcIdx; - uint32_t inChIdx; - float gain_lin; - -#ifdef WMOPS - wmops_sub_start( "renderChannelsToChannels" ); -#endif - - passThroughIdx = 0; - - /* Iterate over given MC inputs */ - for ( mcIdx = 0; mcIdx < st->inConfig.numMultiChannelBuses; ++mcIdx ) - { - /* Get channel idx of current MC input within the multitrack buffer */ - inMcChannelIdx = st->inConfig.multiChannelBuses[mcIdx].inputChannelIndex; - - /* Number of non-LFE input channels */ - numNonLfeInChannels = Prndr_getNumNonLfeChannelsInSpeakerLayout( st->inConfig.multiChannelBuses[mcIdx].speakerLayout ); - - /* Number of all input channels */ - numInChannels = Prndr_getNumChannelsInSpeakerLayout( st->inConfig.multiChannelBuses[mcIdx].speakerLayout ); - - lfeLastIdxs = Prndr_getReorderedChannelIndices( st->inConfig.multiChannelBuses[mcIdx].speakerLayout ); - - gain_lin = dBToLin( st->inConfig.multiChannelBuses[mcIdx].gain_dB ); - - /* Iterate over non-LFE channels */ - for ( inChIdx = 0; inChIdx < numNonLfeInChannels; ++inChIdx ) - { - if ( st->mcPassthrough[passThroughIdx] == -1 ) - { - applyChannelGainsAndAddToOutput( inAudio, - inMcChannelIdx + lfeLastIdxs[inChIdx], - st->speakerPanGains[mcIdx][inChIdx], - NULL, - gain_lin, - st->crossfade, - outAudio ); - } - else - { - passthroughChannel( inAudio, - inMcChannelIdx + lfeLastIdxs[inChIdx], - st->mcPassthrough[passThroughIdx], - gain_lin, - outAudio ); - } - - ++passThroughIdx; - } - - /* Iterate over LFE channels */ - for ( ; inChIdx < numInChannels; ++inChIdx ) - { - /* Pass through if possible */ - if ( st->mcPassthrough[passThroughIdx] != -1 ) - { - passthroughChannel( inAudio, - inMcChannelIdx + lfeLastIdxs[inChIdx], - st->mcPassthrough[passThroughIdx], - gain_lin, - outAudio ); - } - else - { - if ( st->neverDropLfe ) - { - applyChannelGainsAndAddToOutput( inAudio, - inMcChannelIdx + lfeLastIdxs[inChIdx], - st->lfePanGains, - NULL, - gain_lin, - st->crossfade, - outAudio ); - } - } - - ++passThroughIdx; - } - } - -#ifdef WMOPS - wmops_sub_end(); -#endif -} - -static void renderObjectsToChannels( - Prndr_Prerenderer *const st, - const Prndr_AudioBuffer inAudio, - const Prndr_AudioObjectMetadataBuffer metadataBuffer, - Prndr_AudioBuffer outAudio ) -{ - const Prndr_AudioObject *curObj; - uint32_t objIdx; - Prndr_AudioObjectPosition pos; - float gain_lin; - -#ifdef WMOPS - wmops_sub_start( "renderObjectsToChannels" ); -#endif - - assert( st->inConfig.numAudioObjects == metadataBuffer.numObjects && "Metadata provided for a different number of objects than found in input" ); - - /* Iterate over given audio objects */ - for ( objIdx = 0; objIdx < st->inConfig.numAudioObjects; ++objIdx ) - { - /* Get pointer to current object and its metadata */ - curObj = &st->inConfig.audioObjects[objIdx]; - pos = metadataBuffer.positions[objIdx]; - gain_lin = dBToLin( st->inConfig.audioObjects[objIdx].gain_dB ); - - /* Render to MC */ - renderSingleObjectToChannels( - st, - inAudio, - curObj->inputChannelIndex, - &st->objPanInfo[objIdx], - pos, - gain_lin, - outAudio ); - } - -#ifdef WMOPS - wmops_sub_end(); -#endif -} - -static void renderSingleObjectToAmbi( - Prndr_Prerenderer *const st, - const Prndr_AudioBuffer inAudio, - const uint32_t itemChnlIdx, - Prndr_ObjPanInfo *prevPanInfo, - const Prndr_AudioObjectPosition curFrmPos, - const float gain_lin, - Prndr_AudioBuffer outAudio ) -{ - float *swapPtr; - -#ifdef WMOPS - wmops_sub_start( "renderSingleObjectToAmbi" ); -#endif - - /* Update panning gains if position changed */ - if ( prevPanInfo->position.azimuth != curFrmPos.azimuth || - prevPanInfo->position.elevation != curFrmPos.elevation || - st->firstFrame ) - { - /* Write current panning gains to tmpBuffer */ - ivas_dirac_dec_get_response( curFrmPos.azimuth, - curFrmPos.elevation, - st->tmpGainBuffer, - Prndr_getAmbisonicsOrder( st->outConfig.ambisonics ) ); - - prevPanInfo->position.azimuth = curFrmPos.azimuth; - prevPanInfo->position.elevation = curFrmPos.elevation; - - applyChannelGainsAndAddToOutput( inAudio, - itemChnlIdx, - st->tmpGainBuffer, - st->firstFrame ? NULL : prevPanInfo->panGains, - gain_lin, - st->crossfade, - outAudio ); - - /* Save current gains as most recently applied gains */ - swapPtr = prevPanInfo->panGains; - prevPanInfo->panGains = st->tmpGainBuffer; - st->tmpGainBuffer = swapPtr; - } - /* Otherwise use most recent gains and no interpolation */ - else - { - applyChannelGainsAndAddToOutput( inAudio, - itemChnlIdx, - prevPanInfo->panGains, - NULL, - gain_lin, - st->crossfade, - outAudio ); - } - -#ifdef WMOPS - wmops_sub_end(); -#endif -} - -static void renderSingleObjectToChannels( - Prndr_Prerenderer *const st, - const Prndr_AudioBuffer inAudio, - const uint32_t itemChnlIdx, - Prndr_ObjPanInfo *prevPanInfo, - const Prndr_AudioObjectPosition curFrmPos, - const float gain_lin, - Prndr_AudioBuffer outAudio ) -{ - float *swapPtr; - -#ifdef WMOPS - wmops_sub_start( "renderSingleObjectToChannels" ); -#endif - - /* Update panning gains if position changed */ - if ( prevPanInfo->position.azimuth != curFrmPos.azimuth || - prevPanInfo->position.elevation != curFrmPos.elevation || - st->firstFrame ) - { - /* Write current panning gains to tmpBuffer */ - getSpeakerGains( st, curFrmPos.azimuth, curFrmPos.elevation, st->tmpGainBuffer ); - prevPanInfo->position.azimuth = curFrmPos.azimuth; - prevPanInfo->position.elevation = curFrmPos.elevation; - - applyChannelGainsAndAddToOutput( inAudio, - itemChnlIdx, - st->tmpGainBuffer, - st->firstFrame ? NULL : prevPanInfo->panGains, - gain_lin, - st->crossfade, - outAudio ); - - /* Save current gains as most recently applied gains */ - swapPtr = prevPanInfo->panGains; - prevPanInfo->panGains = st->tmpGainBuffer; - st->tmpGainBuffer = swapPtr; - } - /* Otherwise use most recent gains and no interpolation */ - else - { - applyChannelGainsAndAddToOutput( inAudio, - itemChnlIdx, - prevPanInfo->panGains, - NULL, - gain_lin, - st->crossfade, - outAudio ); - } - -#ifdef WMOPS - wmops_sub_end(); -#endif -} - -static void applyChannelGainsAndAddToOutput( - const Prndr_AudioBuffer inAudio, - const uint32_t itemChnlIdx, - const float *const gainsCurrent, - const float *const gainsPrev, - const float gain_lin, - const float *const crossfade, - Prndr_AudioBuffer outAudio ) -{ - float *inSmpl; - float *outSmpl; - const float *fadeIn; - const float *fadeOut; - const float *lastInSmpl; - int16_t outChnlIdx; - float currentGain; - float previousGain; - -#ifdef WMOPS - wmops_sub_start( "applyChannelGainsAndAddToOutput" ); -#endif - - - /* Pointer to behind last input sample */ - lastInSmpl = get_smpl_ptr( inAudio, itemChnlIdx, inAudio.config.bufferSize ); - - for ( outChnlIdx = 0; outChnlIdx < outAudio.config.numChannels; ++outChnlIdx ) - { -#ifdef WMOPS - wmops_sub_start( "applyChannelGainsAndAddToOutput_chnl_loop" ); -#endif - currentGain = gainsCurrent[outChnlIdx] * gain_lin; - previousGain = gainsPrev == NULL ? 0.f : gainsPrev[outChnlIdx] * gain_lin; - - /* Process current output channel only if applying non-zero gains */ - if ( fabsf( currentGain ) > EPSILON || ( gainsPrev != NULL && fabsf( previousGain ) > EPSILON ) ) - { - /* Reset crossfade pointers */ - fadeIn = crossfade; - fadeOut = &crossfade[inAudio.config.bufferSize - 1]; - - /* Reset input pointer to the beginning of input channel */ - inSmpl = get_smpl_ptr( inAudio, itemChnlIdx, 0 ); - - /* Set output pointer to first output channel sample */ - outSmpl = get_smpl_ptr( outAudio, outChnlIdx, 0 ); - - if ( gainsPrev == NULL || fabsf( previousGain - currentGain ) <= EPSILON ) - { -#ifdef WMOPS - wmops_sub_start( "applyChannelGainsAndAddToOutput_smpl_loop_no_intrpl" ); -#endif - /* If no interpolation from previous frame, apply current gain */ - do - { - *outSmpl += currentGain * ( *inSmpl ); - ++outSmpl; - ++inSmpl; - - } while ( inSmpl != lastInSmpl ); -#ifdef WMOPS - wmops_sub_end(); -#endif - } - else - { -#ifdef WMOPS - wmops_sub_start( "applyChannelGainsAndAddToOutput_smpl_loop_intrpl" ); -#endif - /* Otherwise use weighted average between previous and current gain */ - do - { - *outSmpl += ( ( *fadeIn ) * currentGain + ( *fadeOut ) * previousGain ) * ( *inSmpl ); - ++outSmpl; - ++inSmpl; - - ++fadeIn; - --fadeOut; - } while ( inSmpl != lastInSmpl ); -#ifdef WMOPS - wmops_sub_end(); -#endif - } - } - -#ifdef WMOPS - wmops_sub_end(); -#endif - } - -#ifdef WMOPS - wmops_sub_end(); -#endif -} - -static void prepareLfeHandling( - Prndr_Prerenderer *const st ) -{ - /* uint32_t i; */ - -#ifdef WMOPS - wmops_sub_start( "prepareLfeHandling" ); -#endif - - st->lfePanGains = count_calloc( st->numOutChannels, sizeof( float ) ); - - if ( st->outConfig.ambisonics != prndr_ambisonics_none ) - { - /* Pan LFE to south pole (experimental) */ - ivas_dirac_dec_get_response( 0, - -90, - st->lfePanGains, - Prndr_getAmbisonicsOrder( st->outConfig.ambisonics ) ); - - /* TODO(sgi): Apply 10 dB gain? Almost surely will clip */ - /* for (i=0; i< st->numOutChannels; ++i) { - st->lfePanGains[i] *= 3.1622776602f; - } */ - } - else - { - set_zero( st->lfePanGains, st->numOutChannels ); - - /* Pan LFE to L and R with 4dB gain each (== 10dB - 6dB) */ - if ( st->numOutChannels > 1 ) - { - st->lfePanGains[0] = 1.5848931925f; - st->lfePanGains[1] = 1.5848931925f; - } - else - { - /* Put LFE in center channel, do not add 10dB gain to avoid clipping */ - st->lfePanGains[1] = 1.f; - } - } - -#ifdef WMOPS - wmops_sub_end(); -#endif -} - -static void prepareMcPanGains( Prndr_Prerenderer *const st ) -{ - uint32_t mcIdx; - int32_t spkIdx; - int32_t numChannels; - const float *spkAzi; - const float *spkEle; - -#ifdef WMOPS - wmops_sub_start( "prepareMcPanGains" ); -#endif - - st->speakerPanGains = count_calloc( st->inConfig.numMultiChannelBuses, sizeof( float ** ) ); - - for ( mcIdx = 0; mcIdx < st->inConfig.numMultiChannelBuses; ++mcIdx ) - { - numChannels = Prndr_getNumChannelsInSpeakerLayout( st->inConfig.multiChannelBuses[mcIdx].speakerLayout ); - st->speakerPanGains[mcIdx] = count_calloc( numChannels, sizeof( float * ) ); - - spkAzi = Prndr_getSpeakerAzimuths( st->inConfig.multiChannelBuses[mcIdx].speakerLayout ); - spkEle = Prndr_getSpeakerElevations( st->inConfig.multiChannelBuses[mcIdx].speakerLayout ); - - if ( st->outConfig.ambisonics != prndr_ambisonics_none ) - { - for ( spkIdx = 0; spkIdx < numChannels; ++spkIdx ) - { - st->speakerPanGains[mcIdx][spkIdx] = count_calloc( st->numOutChannels, sizeof( float ) ); - ivas_dirac_dec_get_response( (int16_t) spkAzi[spkIdx], - (int16_t) spkEle[spkIdx], - st->speakerPanGains[mcIdx][spkIdx], - Prndr_getAmbisonicsOrder( st->outConfig.ambisonics ) ); - } - } - else if ( st->outConfig.speakerLayout != prndr_speaker_layout_none ) - { - for ( spkIdx = 0; spkIdx < numChannels; ++spkIdx ) - { - st->speakerPanGains[mcIdx][spkIdx] = count_calloc( st->numOutChannels, sizeof( float ) ); - getSpeakerGains( st, spkAzi[spkIdx], spkEle[spkIdx], st->speakerPanGains[mcIdx][spkIdx] ); - } - } - } - -#ifdef WMOPS - wmops_sub_end(); -#endif -} - -static void prepareMcPassthrough( Prndr_Prerenderer *const st ) -{ - /* Output config */ - const float *outSpkAzi; - const float *outSpkEle; - const uint32_t *lfeLastIdxs; - uint32_t numNonLfeOutChannels; - uint32_t numOutChannels; - uint32_t lfeLastIdx_lsCustom[MAX_OUTPUT_CHANNELS]; - - /* Input config */ - const float *inSpkAzi; - const float *inSpkEle; - uint32_t numInChannels; - uint32_t numNonLfeInChannels; - - /* Input channel index */ - uint32_t passThroughIdx; - - uint32_t mcIdx; - uint32_t inChIdx; - uint32_t outChIdx; - -#ifdef WMOPS - wmops_sub_start( "prepareMcPassthrough" ); -#endif - - st->mcPassthrough = count_calloc( st->numInChannelsMc, sizeof( int32_t ) ); - - - if ( st->outConfig.speakerLayout == prndr_speaker_layout_custom ) - { - /* Number of non-LFE output channels */ - numNonLfeOutChannels = st->outConfig.outSetupCustom->num_spk; - - /* Number of output channels */ - numOutChannels = st->outConfig.outSetupCustom->num_spk + st->outConfig.outSetupCustom->num_lfe; - - /* Output speaker coordinates */ - outSpkAzi = st->outConfig.outSetupCustom->ls_azimuth; - outSpkEle = st->outConfig.outSetupCustom->ls_elevation; - - /* num_spk + num_lfe must be <= MAX_OUTPUT_CHANNELS for custom loudspeaker layouts */ - if ( st->outConfig.outSetupCustom->num_lfe > 0 ) - { - lfeLastIdx_lsCustom[numNonLfeOutChannels] = st->outConfig.outSetupCustom->lfe_idx[0]; - } - - for ( outChIdx = 0; outChIdx < numNonLfeOutChannels; ++outChIdx ) - { - ( ( st->outConfig.outSetupCustom->num_lfe > 0 ) && ( (int16_t) outChIdx >= st->outConfig.outSetupCustom->lfe_idx[0] ) ) ? ( lfeLastIdx_lsCustom[outChIdx] = outChIdx + 1 ) : ( lfeLastIdx_lsCustom[outChIdx] = outChIdx ); - } - - lfeLastIdxs = &lfeLastIdx_lsCustom[0]; - } - else - { - /* Number of non-LFE output channels */ - numNonLfeOutChannels = Prndr_getNumNonLfeChannelsInSpeakerLayout( st->outConfig.speakerLayout ); - - /* Number of output channels */ - numOutChannels = Prndr_getNumChannelsInSpeakerLayout( st->outConfig.speakerLayout ); - - /* Output speaker coordinates */ - outSpkAzi = Prndr_getSpeakerAzimuths( st->outConfig.speakerLayout ); - outSpkEle = Prndr_getSpeakerElevations( st->outConfig.speakerLayout ); - - lfeLastIdxs = Prndr_getReorderedChannelIndices( st->outConfig.speakerLayout ); - } - - passThroughIdx = 0; - for ( mcIdx = 0; mcIdx < st->inConfig.numMultiChannelBuses; ++mcIdx ) - { - /* Number of non-LFE input channels for current MC input */ - numNonLfeInChannels = Prndr_getNumNonLfeChannelsInSpeakerLayout( st->inConfig.multiChannelBuses[mcIdx].speakerLayout ); - - /* Number of input channels for current MC input */ - numInChannels = Prndr_getNumChannelsInSpeakerLayout( st->inConfig.multiChannelBuses[mcIdx].speakerLayout ); - - /* Input speaker coordinates */ - inSpkAzi = Prndr_getSpeakerAzimuths( st->inConfig.multiChannelBuses[mcIdx].speakerLayout ); - inSpkEle = Prndr_getSpeakerElevations( st->inConfig.multiChannelBuses[mcIdx].speakerLayout ); - - /* Check if passthrough possible for, save I/O mapping */ - for ( inChIdx = 0; inChIdx < numNonLfeInChannels; ++inChIdx ) - { - st->mcPassthrough[passThroughIdx] = -1; - - for ( outChIdx = 0; outChIdx < numNonLfeOutChannels; ++outChIdx ) - { - if ( inSpkAzi[inChIdx] == outSpkAzi[outChIdx] && - inSpkEle[inChIdx] == outSpkEle[outChIdx] ) - { - st->mcPassthrough[passThroughIdx] = lfeLastIdxs[outChIdx]; - break; - } - } - - ++passThroughIdx; - } - - /* Setup LFE passthrough, save I/O mapping */ - outChIdx = numNonLfeOutChannels; - for ( ; inChIdx < numInChannels; ++inChIdx ) - { - if ( outChIdx < numOutChannels ) - { - st->mcPassthrough[passThroughIdx] = lfeLastIdxs[outChIdx]; - } - else - { - st->mcPassthrough[passThroughIdx] = -1; - } - - ++outChIdx; - ++passThroughIdx; - } - } - -#ifdef WMOPS - wmops_sub_end(); -#endif -} - -static void passthroughChannel( const Prndr_AudioBuffer inAudio, - const uint32_t srcChnlIdx, - const uint32_t dstChnlIdx, - const float gain_lin, - Prndr_AudioBuffer outAudio ) -{ - float *inSmpl; - float *outSmpl; - int16_t smplIdx; - -#ifdef WMOPS - wmops_sub_start( "passthroughChannel" ); -#endif - - inSmpl = get_smpl_ptr( inAudio, srcChnlIdx, 0 ); - outSmpl = get_smpl_ptr( outAudio, dstChnlIdx, 0 ); - - for ( smplIdx = 0; smplIdx < inAudio.config.bufferSize; ++smplIdx ) - { - *outSmpl += *inSmpl * gain_lin; - - ++outSmpl; - ++inSmpl; - } - -#ifdef WMOPS - wmops_sub_end(); -#endif -} - -static void getSpeakerGains( const Prndr_Prerenderer *const st, - const float azi, - const float ele, - float *const spkGains ) -{ - const uint32_t *lfeLastIdxs; - int16_t numNonLfeOutChannels; - int16_t noLfeIdx; - Prndr_SpeakerLayout speakerLayout; - -#ifdef WMOPS - wmops_sub_start( "getSpeakerGains" ); -#endif - - /* EFAP returns an array of gains only for non-LFE speakers */ - efap_determine_gains( st->efapRenderer, st->noLfePanBuffer, azi, ele, EFAP_MODE_EFAP ); - - speakerLayout = st->outConfig.speakerLayout; - if ( speakerLayout == prndr_speaker_layout_custom ) - { - uint32_t lfeIdx; - - numNonLfeOutChannels = st->outConfig.outSetupCustom->num_spk; - - /* Clear speaker gains - not all elements will be overwritten below */ - set_zero( spkGains, numNonLfeOutChannels + st->outConfig.outSetupCustom->num_lfe ); - - /* Copy to gain array where LFE channel(s) are included */ - for ( lfeIdx = 0, noLfeIdx = 0; noLfeIdx < numNonLfeOutChannels; lfeIdx++, noLfeIdx++ ) - { - if ( noLfeIdx == st->outConfig.outSetupCustom->lfe_idx[0] ) - { - lfeIdx++; - } - spkGains[lfeIdx] = st->noLfePanBuffer[noLfeIdx]; - } - } - else - { - numNonLfeOutChannels = Prndr_getNumNonLfeChannelsInSpeakerLayout( speakerLayout ); - lfeLastIdxs = Prndr_getReorderedChannelIndices( speakerLayout ); - - /* Clear speaker gains - not all elements will be overwritten below */ - set_zero( spkGains, Prndr_getNumChannelsInSpeakerLayout( speakerLayout ) ); - - /* Copy to gain array where LFE channel(s) are included */ - for ( noLfeIdx = 0; noLfeIdx < numNonLfeOutChannels; ++noLfeIdx ) - { - spkGains[lfeLastIdxs[noLfeIdx]] = st->noLfePanBuffer[noLfeIdx]; - } - } - -#ifdef WMOPS - wmops_sub_end(); -#endif -} - -int16_t Prndr_getNumChannelsAmbisonics( Prndr_Ambisonics ambisonics ) -{ - switch ( ambisonics ) - { - case prndr_ambisonics_none: - return 0; - case prndr_ambisonics_mono: - return 1; - case prndr_ambisonics_foa: - return 4; - case prndr_ambisonics_soa: - return 9; - case prndr_ambisonics_toa: - return 16; - default: - assert( !"Invalid ambisonics config" ); - } - - return 0; -} - -static int16_t Prndr_getAmbisonicsOrder( Prndr_Ambisonics ambisonics ) -{ - assert( ambisonics != prndr_ambisonics_none && "Invalid ambisonics config" ); - return ambisonics; /* Enum values map to ambisonics order */ -} - -int16_t Prndr_getNumChannelsInSpeakerLayout( Prndr_SpeakerLayout layout ) -{ - switch ( layout ) - { - case prndr_speaker_layout_none: - return 0; - case prndr_speaker_layout_mono: - return 1; - case prndr_speaker_layout_stereo: - return 2; - case prndr_speaker_layout_5_1: - return 6; - case prndr_speaker_layout_7_1: - return 8; - case prndr_speaker_layout_5_1_4: - return 10; - case prndr_speaker_layout_7_1_4: - return 12; - default: - assert( !"Invalid speaker layout" ); - } - - return 0; -} - -int16_t Prndr_getNumNonLfeChannelsInSpeakerLayout( Prndr_SpeakerLayout layout ) -{ - switch ( layout ) - { - case prndr_speaker_layout_mono: - return 1; - case prndr_speaker_layout_stereo: - return 2; - case prndr_speaker_layout_5_1: - return 5; - case prndr_speaker_layout_7_1: - return 7; - case prndr_speaker_layout_5_1_4: - return 9; - case prndr_speaker_layout_7_1_4: - return 11; - default: - assert( !"Invalid speaker layout" ); - } - - return 0; -} - -const float *Prndr_getSpeakerAzimuths( Prndr_SpeakerLayout layout ) -{ - switch ( layout ) - { - case prndr_speaker_layout_mono: - return ls_azimuth_CICP1; - case prndr_speaker_layout_stereo: - return ls_azimuth_CICP2; - case prndr_speaker_layout_5_1: - return ls_azimuth_CICP6; - case prndr_speaker_layout_7_1: - return ls_azimuth_CICP12; - case prndr_speaker_layout_5_1_4: - return ls_azimuth_CICP16; - case prndr_speaker_layout_7_1_4: - return ls_azimuth_CICP19; - default: - assert( !"Invalid speaker layout" ); - } - - return NULL; -} - -const float *Prndr_getSpeakerElevations( Prndr_SpeakerLayout layout ) -{ - switch ( layout ) - { - case prndr_speaker_layout_mono: - return ls_elevation_CICP1; - case prndr_speaker_layout_stereo: - return ls_elevation_CICP2; - case prndr_speaker_layout_5_1: - return ls_elevation_CICP6; - case prndr_speaker_layout_7_1: - return ls_elevation_CICP12; - case prndr_speaker_layout_5_1_4: - return ls_elevation_CICP16; - case prndr_speaker_layout_7_1_4: - return ls_elevation_CICP19; - default: - assert( !"Invalid speaker layout" ); - } - - return NULL; -} - -const uint32_t *Prndr_getReorderedChannelIndices( Prndr_SpeakerLayout layout ) -{ - switch ( layout ) - { - case prndr_speaker_layout_mono: - return ls_LFE_last_idx_CICP1; - case prndr_speaker_layout_stereo: - return ls_LFE_last_idx_CICP2; - case prndr_speaker_layout_5_1: - return ls_LFE_last_idx_CICP6; - case prndr_speaker_layout_7_1: - return ls_LFE_last_idx_CICP12; - case prndr_speaker_layout_5_1_4: - return ls_LFE_last_idx_CICP16; - case prndr_speaker_layout_7_1_4: - return ls_LFE_last_idx_CICP19; - default: - assert( !"Invalid speaker layout" ); - } - - return NULL; -} - -static ivas_error Prndr_getHoaRenderMtx( - const Prndr_OutputConfig outConfig, - float **decMtx, - uint32_t ambiOrder ) -{ - IVAS_OUTPUT_SETUP hOutSetup; - ivas_error error; - - error = IVAS_ERR_OK; - -#ifdef WMOPS - wmops_sub_start( "Prndr_getHoaRenderMtx" ); -#endif - - switch ( outConfig.speakerLayout ) - { - case prndr_speaker_layout_mono: - ivas_output_init( &hOutSetup, AUDIO_CONFIG_MONO ); - hOutSetup.ls_azimuth = ls_azimuth_CICP1; - hOutSetup.ls_elevation = ls_elevation_CICP1; - break; - case prndr_speaker_layout_stereo: - ivas_output_init( &hOutSetup, AUDIO_CONFIG_STEREO ); - break; - case prndr_speaker_layout_5_1: - ivas_output_init( &hOutSetup, AUDIO_CONFIG_5_1 ); - break; - case prndr_speaker_layout_7_1: - ivas_output_init( &hOutSetup, AUDIO_CONFIG_7_1 ); - break; - case prndr_speaker_layout_5_1_4: - ivas_output_init( &hOutSetup, AUDIO_CONFIG_5_1_4 ); - break; - case prndr_speaker_layout_7_1_4: - ivas_output_init( &hOutSetup, AUDIO_CONFIG_7_1_4 ); - break; - case prndr_speaker_layout_custom: - ivas_ls_custom_setup( &hOutSetup, outConfig.outSetupCustom ); - break; - default: - assert( !"Invalid speaker config" ); - return IVAS_ERR_WRONG_PARAMS; - } - - if ( ( error = ivas_sba_get_hoa_dec_matrix( hOutSetup, decMtx, (int16_t) ambiOrder ) ) != IVAS_ERR_OK ) - { - return error; - } - - -#ifdef WMOPS - wmops_sub_end(); -#endif - - return error; -} - -void Prndr_getHoaDecVecForAmbiChnl( - uint32_t ambiChnnlIdx, - const Prndr_OutputConfig outConfig, - const float *decMtx, - float *decCoeffs ) -{ - - const uint32_t *lfeLastIdxs; - int16_t numNonLfeChannels; - int16_t nonLfeChIdx; - -#ifdef WMOPS - wmops_sub_start( "Prndr_getHoaDecVecForAmbiChnl" ); -#endif - - - if ( outConfig.speakerLayout == prndr_speaker_layout_custom ) - { - uint32_t lfeIdx; - - numNonLfeChannels = outConfig.outSetupCustom->num_spk; - - /* Clear speaker gains - not all elements will be overwritten below */ - set_zero( decCoeffs, numNonLfeChannels + outConfig.outSetupCustom->num_lfe ); - - /* Copy to gain array where LFE channel(s) are included */ - for ( lfeIdx = 0, nonLfeChIdx = 0; nonLfeChIdx < numNonLfeChannels; lfeIdx++, nonLfeChIdx++ ) - { - if ( nonLfeChIdx == outConfig.outSetupCustom->lfe_idx[0] ) - { - lfeIdx++; - } - decCoeffs[lfeIdx] = decMtx[16 * nonLfeChIdx + ambiChnnlIdx]; - } - } - else - { - numNonLfeChannels = Prndr_getNumNonLfeChannelsInSpeakerLayout( outConfig.speakerLayout ); - - lfeLastIdxs = Prndr_getReorderedChannelIndices( outConfig.speakerLayout ); - - /* Clear decoding coefficients - not all elements will be overwritten below */ - set_zero( decCoeffs, Prndr_getNumChannelsInSpeakerLayout( outConfig.speakerLayout ) ); - - for ( nonLfeChIdx = 0; nonLfeChIdx < numNonLfeChannels; ++nonLfeChIdx ) - { - decCoeffs[lfeLastIdxs[nonLfeChIdx]] = decMtx[16 * nonLfeChIdx + ambiChnnlIdx]; - } - } - -#ifdef WMOPS - wmops_sub_end(); -#endif -} - -/*-------------------------------------------------------------------* - * ivas_limiter_prerenderer() - * - * In-place saturation control for multichannel buffers with adaptive release time - *-------------------------------------------------------------------*/ -static void ivas_limiter_prerenderer( - IVAS_LIMITER_HANDLE hLimiter, /* i/o: limiter struct handle */ - float *output, /* i/o: I/O buffer */ - const int16_t output_frame, /* i : number of samples per channel in the buffer */ - const float threshold /* i : signal amplitude above which limiting starts to be applied */ -) -{ - int16_t c; - float **channels; - int16_t num_channels; - - /* return early if given bad parameters */ - if ( hLimiter == NULL || output == NULL || output_frame <= 0 ) - { - return; - } - - channels = hLimiter->channel_ptrs; - num_channels = hLimiter->num_channels; - - for ( c = 0; c < num_channels; ++c ) - { - channels[c] = output + c * output_frame; - } - - limiter_process( hLimiter, output_frame, threshold, 0, NULL ); - - return; -} - -static float dBToLin( const float gain_dB ) -{ - return powf( 10.f, gain_dB / 20.f ); -} diff --git a/lib_util/ivas_prerenderer.h b/lib_util/ivas_prerenderer.h deleted file mode 100644 index f21f893dc98e7b9e93eb103971f77e44b3c7161a..0000000000000000000000000000000000000000 --- a/lib_util/ivas_prerenderer.h +++ /dev/null @@ -1,190 +0,0 @@ -/****************************************************************************************************** - - (C) 2022 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, - Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., - Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, - Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other - contributors to this repository. All Rights Reserved. - - This software is protected by copyright law and by international treaties. - The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB, - Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., - Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, - Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other - contributors to this repository retain full ownership rights in their respective contributions in - the software. This notice grants no license of any kind, including but not limited to patent - license, nor is any license granted by implication, estoppel or otherwise. - - Contributors are required to enter into the IVAS codec Public Collaboration agreement before making - contributions. - - This software is provided "AS IS", without any express or implied warranties. The software is in the - development stage. It is intended exclusively for experts who have experience with such software and - solely for the purpose of inspection. All implied warranties of non-infringement, merchantability - and fitness for a particular purpose are hereby disclaimed and excluded. - - Any dispute, controversy or claim arising under or in relation to providing this software shall be - submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in - accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and - the United Nations Convention on Contracts on the International Sales of Goods. - -*******************************************************************************************************/ - -#ifndef IVAS_PRERENDERER_H -#define IVAS_PRERENDERER_H - -#include -#include - -#include "options.h" -#include "common_api_types.h" -#include "ls_custom_file_reader.h" -#include "ivas_error.h" - -#define PRERENDERER_MAX_ISM_INPUTS 16 -#define PRERENDERER_MAX_MC_INPUTS 2 -#define PRERENDERER_MAX_SBA_INPUTS 2 - -typedef enum Prndr_Ambisonics -{ - prndr_ambisonics_none = -1, - prndr_ambisonics_mono = 0, - prndr_ambisonics_foa = 1, - prndr_ambisonics_soa = 2, - prndr_ambisonics_toa = 3 -} Prndr_Ambisonics; /* Numerical value corresponds to Ambisonics order */ - -typedef enum Prndr_SpeakerLayout -{ - prndr_speaker_layout_none = -1, - prndr_speaker_layout_custom = 0, - prndr_speaker_layout_mono = 1, - prndr_speaker_layout_stereo = 2, - prndr_speaker_layout_5_1 = 6, - prndr_speaker_layout_5_1_4 = 16, - prndr_speaker_layout_7_1 = 12, - prndr_speaker_layout_7_1_4 = 19 -} Prndr_SpeakerLayout; /* Numerical value corresponds to CICP index */ - -typedef struct Prndr_AudioObjectPosition -{ - int16_t azimuth; - int16_t elevation; -} Prndr_AudioObjectPosition; - -typedef struct Prndr_AudioObjectMetadataBuffer -{ - Prndr_AudioObjectPosition positions[PRERENDERER_MAX_ISM_INPUTS]; - uint32_t numObjects; -} Prndr_AudioObjectMetadataBuffer; - -typedef struct Prndr_AudioObject -{ - uint8_t inputChannelIndex; - float gain_dB; -} Prndr_AudioObject; - -typedef struct Prndr_ObjPanInfo -{ - Prndr_AudioObjectPosition position; - float *panGains; -} Prndr_ObjPanInfo; - -typedef struct Prndr_AmbisonicsBus -{ - Prndr_Ambisonics ambisonicsConfig; - uint8_t inputChannelIndex; - float gain_dB; -} Prndr_AmbisonicsBus; - -typedef struct Prndr_MultiChannelBus -{ - Prndr_SpeakerLayout speakerLayout; - uint8_t inputChannelIndex; - float gain_dB; -} Prndr_MultiChannelBus; - -typedef struct Prndr_AudioBufferConfig -{ - int32_t sampleRate; - int16_t bufferSize; - int16_t numChannels; -} Prndr_AudioBufferConfig; - -typedef struct Prndr_AudioBuffer -{ - Prndr_AudioBufferConfig config; - float *data; -} Prndr_AudioBuffer; - -typedef struct Prndr_InputConfig -{ - Prndr_AudioObject audioObjects[PRERENDERER_MAX_ISM_INPUTS]; - uint8_t numAudioObjects; - Prndr_MultiChannelBus multiChannelBuses[PRERENDERER_MAX_MC_INPUTS]; - uint8_t numMultiChannelBuses; - Prndr_AmbisonicsBus ambisonicsBuses[PRERENDERER_MAX_SBA_INPUTS]; - uint8_t numAmbisonicsBuses; -} Prndr_InputConfig; - -typedef struct Prndr_OutputConfig -{ - Prndr_SpeakerLayout speakerLayout; - Prndr_Ambisonics ambisonics; - IVAS_LSSETUP_CUSTOM_HANDLE outSetupCustom; -} Prndr_OutputConfig; - -typedef struct Prndr_Prerenderer Prndr_Prerenderer; - -/* clang-format off */ -/*----------------------------------------------------------------------------------* - * Prerenderer prototypes - *----------------------------------------------------------------------------------*/ - -/*! Creates a prerenderer state. - * r: pointer to opened prerenderer */ -Prndr_Prerenderer *Prndr_Prerenderer_open( - void -); - -/*! Configures the prerenderer - needs to be called after Prndr_Prerenderer_open(). */ -ivas_error Prndr_Prerenderer_configure( - Prndr_Prerenderer *const st, /* i : Prerenderer state */ - const Prndr_InputConfig inConfig, /* i : Input configuration */ - const Prndr_OutputConfig outConfig, /* i : Output configuration */ - int32_t frameSize, /* i : Processing frame size in samples */ - uint32_t sampleRate /* i : Processing sampling rate */ -); - -/*! Renders one frame of audio samples */ -void Prndr_Prerenderer_render( - Prndr_Prerenderer *const st, /* i : Prerenderer state */ - const Prndr_AudioBuffer inAudio, /* i : Buffer with pointer to input samples and associated info */ - const Prndr_AudioObjectMetadataBuffer metadataBuffer, /* i : Buffer with object metadata for current frame */ - Prndr_AudioBuffer outAudio /* o : Buffer with pointer to output samples and associated info */ -); - -/*! Enable/disable experimental LFE handling */ -void Prndr_Prerenderer_setNeverDropLfe( - Prndr_Prerenderer *st, /* i : Prerenderer state */ - int8_t neverDropLfe /* i : If 0, LFE channel will be dropped when rendering to configs w/o LFE. - If 1, tries to render LFE into other channels in an optimal way when rendering to configs w/o LFE. */ -); - -/*! Get number of input channels based on InputConfig */ -int16_t Prndr_Prerenderer_getInChannels( - Prndr_Prerenderer *st /* i : Prerenderer state */ -); - -/*! Get number of output channels based on OutputConfig */ -int16_t Prndr_Prerenderer_getOutChannels( - Prndr_Prerenderer *st /* i : Prerenderer state */ -); - -/*! Destructs the prerenderer state and frees memory */ -void Prndr_Prerenderer_close( - Prndr_Prerenderer *st /* i : Prerenderer state */ -); -/* clang-format on */ - -#endif /* IVAS_PRERENDERER_H */ diff --git a/lib_util/ivas_rom_prerenderer.c b/lib_util/ivas_rom_prerenderer.c deleted file mode 100644 index 43421165771e08f74c195272104c947335359d35..0000000000000000000000000000000000000000 --- a/lib_util/ivas_rom_prerenderer.c +++ /dev/null @@ -1,62 +0,0 @@ -/****************************************************************************************************** - - (C) 2022 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, - Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., - Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, - Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other - contributors to this repository. All Rights Reserved. - - This software is protected by copyright law and by international treaties. - The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB, - Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., - Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, - Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other - contributors to this repository retain full ownership rights in their respective contributions in - the software. This notice grants no license of any kind, including but not limited to patent - license, nor is any license granted by implication, estoppel or otherwise. - - Contributors are required to enter into the IVAS codec Public Collaboration agreement before making - contributions. - - This software is provided "AS IS", without any express or implied warranties. The software is in the - development stage. It is intended exclusively for experts who have experience with such software and - solely for the purpose of inspection. All implied warranties of non-infringement, merchantability - and fitness for a particular purpose are hereby disclaimed and excluded. - - Any dispute, controversy or claim arising under or in relation to providing this software shall be - submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in - accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and - the United Nations Convention on Contracts on the International Sales of Goods. - -*******************************************************************************************************/ - -#include "ivas_rom_prerenderer.h" -#include "wmops.h" - -/* clang-format off */ - -/*----------------------------------------------------------------------------------* - * Prerenderer SBA & MC enc/dec matrices - *----------------------------------------------------------------------------------*/ - -/* CICP1 - Mono */ -const float ls_azimuth_CICP1[1] = { 0.0f }; -const float ls_elevation_CICP1[1] = { 0.0f }; -const uint32_t ls_LFE_last_idx_CICP1[1] = { 0 }; - -/* CICP2 - Stereo */ -const uint32_t ls_LFE_last_idx_CICP2[2] = { 0, 1 }; - -/* CICP6 - 5.1 */ -const uint32_t ls_LFE_last_idx_CICP6[6] = { 0, 1, 2, 4, 5, 3 }; - -/* CICP12 - 7.1 */ -const uint32_t ls_LFE_last_idx_CICP12[8] = { 0, 1, 2, 4, 5, 6, 7, 3 }; - -/* CICP16 - 5.1.4 */ -const uint32_t ls_LFE_last_idx_CICP16[10] = { 0, 1, 2, 4, 5, 6, 7, 8, 9, 3 }; - -/* CICP19 - 7.1.4 */ -const uint32_t ls_LFE_last_idx_CICP19[12] = { 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 3 }; - -/* clang-format on */ diff --git a/lib_util/ls_custom_file_reader.c b/lib_util/ls_custom_file_reader.c index c961374c208e7e8e02f2ee3630b07308136f17ed..58d78de068c92ddbf52acdaca962512e15b04895 100644 --- a/lib_util/ls_custom_file_reader.c +++ b/lib_util/ls_custom_file_reader.c @@ -52,7 +52,7 @@ struct LsCustomFileReader *-----------------------------------------------------------------------*/ ivas_error CustomLsReader_open( - char *LsFilePath, /* i : LS custom layout file name */ + const char *LsFilePath, /* i : LS custom layout file name */ LsCustomFileReader **hLsCustomReader /* o : HeadRotFileReader handle */ ) { @@ -314,7 +314,7 @@ LS_CUSTOM_FILEREADER_ERROR CustomLsFileReading( } } - /* parse LFE indicies; skip if blank line */ + /* parse LFE indices; skip if blank line */ if ( ( fgets( line, 200, hLsCustomReader->file ) != NULL ) && ( strcmp( line, "\n" ) != 0 ) && ( strcmp( line, "\r\n" ) != 0 ) ) { for ( tok = strtok( line, "," ); tok && *tok; tok = strtok( NULL, ",\n" ) ) diff --git a/lib_util/ls_custom_file_reader.h b/lib_util/ls_custom_file_reader.h index 3f9e0d56d378ff5bbec1dba7de091f6ab01cfbf8..e35c06ea8842a76002a730c44f992f60c1e63bd7 100644 --- a/lib_util/ls_custom_file_reader.h +++ b/lib_util/ls_custom_file_reader.h @@ -65,7 +65,7 @@ typedef enum _LS_CUSTOM_FILEREADER_ERROR *-----------------------------------------------------------------------*/ ivas_error CustomLsReader_open( - char *LsFilePath, /* i : LS custom layout file name */ + const char *LsFilePath, /* i : LS custom layout file name */ LsCustomFileReader **hLsCustomReader /* o : HeadRotFileReader handle */ ); diff --git a/lib_util/render_config_reader.c b/lib_util/render_config_reader.c index 58e0eee2860d4084a849324108e19a8b53615e65..27eed3bf147a4e933c2748dd587a2d9f1d961fd3 100644 --- a/lib_util/render_config_reader.c +++ b/lib_util/render_config_reader.c @@ -419,7 +419,7 @@ ivas_error RenderConfigReader_read( while ( sscanf( pParams + params_idx, "%64[^=]=%[^;];", item, pValue ) == 2 ) { hRenderConfig->room_acoustics.override = true; - params_idx += strlen( item ) + strlen( pValue ) + 2; + params_idx += (int32_t) ( strlen( item ) + strlen( pValue ) + 2 ); #ifdef DEBUGGING fprintf( stderr, " PARAM: %s -> %s\n", item, pValue ); #endif @@ -506,7 +506,7 @@ ivas_error RenderConfigReader_read( pValue = (char *) calloc( strlen( pParams ), sizeof( char ) ); while ( sscanf( pParams + params_idx, "%64[^=]=%[^;];", item, pValue ) == 2 ) { - params_idx += strlen( item ) + strlen( pValue ) + 2; + params_idx += (int32_t) ( strlen( item ) + strlen( pValue ) + 2 ); fprintf( stderr, " PARAM: %s -> %s\n", item, pValue ); if ( strcmp( item, "RENDERER" ) == 0 ) { diff --git a/pytest.ini b/pytest.ini index c4ed77a9f63d20671c78bf04aab431f1d9bceff6..bb4169677b91c1a0dacdc4c363d17630c9a23354 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,7 +1,8 @@ # pytest.ini # note: per convention, this file is placed in the root directory of the repository [pytest] -addopts = -ra --tb=short --basetemp=./tmp -n auto +# TODO remove ignore after tests are harmonized +addopts = -ra --tb=short --basetemp=./tmp -n auto -v --ignore=tests/renderer # Write captured system-out log messages to JUnit report. junit_logging = system-out # Do not capture log information for passing tests to JUnit report. diff --git a/scripts/find_unused_symbols.sh b/scripts/find_unused_symbols.sh index 1498fa172b1f14c0e290f596166b1fc803179e4d..68a6cdad8f66197671b1018439a795dadeea6938 100755 --- a/scripts/find_unused_symbols.sh +++ b/scripts/find_unused_symbols.sh @@ -32,7 +32,7 @@ options=$1 OBJDIR=obj -EXECUTABLES="../IVAS_cod ../IVAS_dec ./prerenderer/IVAS_prerenderer" +EXECUTABLES="../IVAS_cod ../IVAS_dec ../IVAS_rend" evaluateTables=1 evaluateFunctions=1 @@ -68,7 +68,6 @@ fi # build if [ $compile -ne 0 ]; then make -C .. DEBUG=0 STRIP=1 clean all 1>&2 - make -C prerenderer DEBUG=0 STRIP=1 clean all 1>&2 fi if [ $evaluateFunctions != 0 ]; then diff --git a/scripts/ivas_pytests/tests/unit_tests/crend/ivas_crend_io_parse.h b/scripts/ivas_pytests/tests/unit_tests/crend/ivas_crend_io_parse.h index abc8b1e463072a94e95c068b6f0a1d2d0dbaae69..bc719cd4a6912d96c07a7ba4edfad499b57c381e 100644 --- a/scripts/ivas_pytests/tests/unit_tests/crend/ivas_crend_io_parse.h +++ b/scripts/ivas_pytests/tests/unit_tests/crend/ivas_crend_io_parse.h @@ -46,6 +46,7 @@ #define IVAS_IN_FMT_510 "510" #define IVAS_IN_FMT_710 "710" #define IVAS_IN_FMT_512 "512" +#define IVAS_IN_FMT_514 "514" #define IVAS_IN_FMT_714 "714" #define IVAS_IN_FMT_FOA "HOA1S" @@ -62,6 +63,7 @@ typedef enum ivas_in_out_fmt_struct_t MULT_CH_5_1, MULT_CH_7_1, MULT_CH_5_1_2, + MULT_CH_5_1_4, MULT_CH_7_1_4, HOA_9, HOA_16, diff --git a/scripts/ivas_pytests/tests/unit_tests/crend/ivas_crend_unit_test.c b/scripts/ivas_pytests/tests/unit_tests/crend/ivas_crend_unit_test.c index b2a24534c02267d75acec5199a4880c192a9d3e8..baa953bc7dab37e0accf84b05e969f86b122a1cc 100644 --- a/scripts/ivas_pytests/tests/unit_tests/crend/ivas_crend_unit_test.c +++ b/scripts/ivas_pytests/tests/unit_tests/crend/ivas_crend_unit_test.c @@ -200,14 +200,14 @@ static ivas_result_t ivas_crend_reverb_test( ivas_crend_io_params_t *pIo_params #else AudioFileWriter_close( &pIo_params->fOut ); AudioFileReader *fOut; - AudioFileReader_open( &fOut , pIo_params->out_path, pIo_params->sample_rate); + AudioFileReader_open( &fOut , pIo_params->out_path, &pIo_params->sample_rate); int16_t numRead; /* Compare */ if ( pIo_params->fRef ) { AudioFileReader_close( &pIo_params->fRef ); AudioFileReader *fRef; - AudioFileReader_open( &fRef, pIo_params->ref_path, pIo_params->sample_rate ); + AudioFileReader_open( &fRef, pIo_params->ref_path, &pIo_params->sample_rate ); if ( test != FAIL ) { @@ -337,8 +337,8 @@ static ivas_result_t ivas_crend_proximity_test( ivas_crend_io_params_t *pIo_para AudioFileReader_close( &pIo_params->fRef ); AudioFileWriter_close( &pIo_params->fOut ); AudioFileReader *fRef, *fOut; - AudioFileReader_open( &fOut, pIo_params->out_path, pIo_params->sample_rate ); - AudioFileReader_open( &fRef, pIo_params->ref_path, pIo_params->sample_rate ); + AudioFileReader_open( &fOut, pIo_params->out_path, &pIo_params->sample_rate ); + AudioFileReader_open( &fRef, pIo_params->ref_path, &pIo_params->sample_rate ); while ( 1 ) { acc_0f = 0.0f; @@ -416,6 +416,10 @@ static ivas_result_t ivas_crend_binaural_test( ivas_crend_io_params_t *pIo_param { test_case = "CREND_512_TO_BIN"; } + else if ( pIo_params->in_fmt == MULT_CH_5_1_4 ) + { + test_case = "CREND_514_TO_BIN"; + } else if ( pIo_params->in_fmt == MULT_CH_7_1_4 ) { test_case = "CREND_714_TO_BIN"; @@ -496,8 +500,8 @@ static ivas_result_t ivas_crend_binaural_test( ivas_crend_io_params_t *pIo_param AudioFileReader_close( &pIo_params->fRef ); AudioFileWriter_close( &pIo_params->fOut ); AudioFileReader *fRef, *fOut; - AudioFileReader_open( &fOut, pIo_params->out_path, pIo_params->sample_rate ); - AudioFileReader_open( &fRef, pIo_params->ref_path, pIo_params->sample_rate ); + AudioFileReader_open( &fOut, pIo_params->out_path, &pIo_params->sample_rate ); + AudioFileReader_open( &fRef, pIo_params->ref_path, &pIo_params->sample_rate ); skip_samples = (int32_t) ( pIo_params->latency_s * pIo_params->sample_rate ); /* skip intial samples based on latency */ @@ -626,8 +630,8 @@ static ivas_result_t ivas_crend_no_diegetic_test( ivas_crend_io_params_t *pIo_pa AudioFileReader_close( &pIo_params->fRef ); AudioFileWriter_close( &pIo_params->fOut ); AudioFileReader *fRef, *fOut; - AudioFileReader_open( &fOut, pIo_params->out_path, pIo_params->sample_rate ); - AudioFileReader_open( &fRef, pIo_params->ref_path, pIo_params->sample_rate ); + AudioFileReader_open( &fOut, pIo_params->out_path, &pIo_params->sample_rate ); + AudioFileReader_open( &fRef, pIo_params->ref_path, &pIo_params->sample_rate ); if ( fRef ) { diff --git a/scripts/ivas_pytests/tests/unit_tests/crend/ivas_crend_utest_utils.c b/scripts/ivas_pytests/tests/unit_tests/crend/ivas_crend_utest_utils.c index d993405b1169dc591fe242b44fe3464af11683dd..be5e4839278b560a2abe35995e0d5c235378cf7b 100644 --- a/scripts/ivas_pytests/tests/unit_tests/crend/ivas_crend_utest_utils.c +++ b/scripts/ivas_pytests/tests/unit_tests/crend/ivas_crend_utest_utils.c @@ -64,7 +64,6 @@ #include "render_config_reader.h" - static ivas_result_t ivas_dec_default_io_params( ivas_dec_io_params_t *pIO_params ) { memset( pIO_params, 0, sizeof( ivas_dec_io_params_t ) ); @@ -103,7 +102,7 @@ static ivas_error ivas_hrtf_init( hHrtf->max_num_iterations = 0; hHrtf->gain_lfe = 0; hHrtf->index_frequency_max_diffuse = 0; - + for ( i = 0; i < IVAS_MAX_NUM_CH; i++ ) { hHrtf->inv_diffuse_weight[i] = 0; @@ -149,6 +148,8 @@ AUDIO_CONFIG ivas_crend_map_out_fmt( return AUDIO_CONFIG_7_1; case MULT_CH_5_1_2: return AUDIO_CONFIG_5_1_2; + case MULT_CH_5_1_4: + return AUDIO_CONFIG_5_1_4; case MULT_CH_7_1_4: return AUDIO_CONFIG_7_1_4; default: @@ -166,6 +167,7 @@ const char *ivas_crend_map_in_fmt( case MULT_CH_5_1: case MULT_CH_7_1: case MULT_CH_5_1_2: + case MULT_CH_5_1_4: case MULT_CH_7_1_4: return IVAS_IN_FMT_COMBINED; case FOA_4: @@ -213,6 +215,9 @@ int16_t ivas_get_num_channels( case MULT_CH_5_1_2: num_channels = 8; break; + case MULT_CH_5_1_4: + num_channels = 10; + break; case MULT_CH_7_1_4: num_channels = 12; break; @@ -279,9 +284,9 @@ static void ivas_crend_unit_test_usage( void ) fprintf( stdout, "\nOptional arguments:\n" ); fprintf( stdout, "---------------------\n" ); fprintf( stdout, "-ifmt : Input format index\n" ); - fprintf( stdout, " (0 - MONO, 1 - STEREO, 2 - BINAURAL, 3 - FOA, 4 - 5.1, 5 - 7.1, 6 - 5.1.2, 7 - 7.1.4, 8 - HOA2, 9 - HOA3)\n" ); + fprintf( stdout, " (0 - MONO, 1 - STEREO, 2 - BINAURAL, 3 - FOA, 4 - 5.1, 5 - 7.1, 6 - 5.1.2, 7 - 5.1.4, 8 - 7.1.4, 9 - HOA2, 10 - HOA3)\n" ); fprintf( stdout, "-ofmt : Output format index\n" ); - fprintf( stdout, " (0 - MONO, 1 - STEREO, 2 - BINAURAL, 3 - FOA, 4 - 5.1, 5 - 7.1, 6 - 5.1.2, 7 - 7.1.4, 8 - HOA2, 9 - HOA3)\n" ); + fprintf( stdout, " (0 - MONO, 1 - STEREO, 2 - BINAURAL, 3 - FOA, 4 - 5.1, 5 - 7.1, 6 - 5.1.2, 7 - 5.1.4, 8 - 7.1.4, 9 - HOA2, 10 - HOA3)\n" ); fprintf( stdout, "-i : Input path/file\n" ); fprintf( stdout, "-o : Output file\n" ); fprintf( stdout, "-r : Reference path/file\n" ); @@ -446,7 +451,7 @@ void ivas_open_files_crend( ivas_crend_io_params_t *pIo_params ) #ifdef USE_PCM_OUT if ( ( pIo_params->fIn[0] = fopen( pIo_params->in_path, "rb" ) ) == NULL ) #else - if ( AudioFileReader_open( &pIo_params->fIn[0] , pIo_params->in_path, pIo_params->sample_rate ) != IVAS_ERR_OK ) + if ( AudioFileReader_open( &pIo_params->fIn[0], pIo_params->in_path, &pIo_params->sample_rate ) != IVAS_ERR_OK ) #endif { fprintf( stderr, "Error: Input audio file %s could not be opened\n\n", pIo_params->in_path ); @@ -458,7 +463,7 @@ void ivas_open_files_crend( ivas_crend_io_params_t *pIo_params ) #ifdef USE_PCM_OUT if ( ( strlen( pIo_params->ref_path ) > 0 ) && ( ( pIo_params->fRef = fopen( pIo_params->ref_path, "rb" ) ) == NULL ) ) #else - if ( ( strlen( pIo_params->ref_path ) > 0 ) && ( AudioFileReader_open( &pIo_params->fRef, pIo_params->ref_path, pIo_params->sample_rate ) != IVAS_ERR_OK ) ) + if ( ( strlen( pIo_params->ref_path ) > 0 ) && ( AudioFileReader_open( &pIo_params->fRef, pIo_params->ref_path, &pIo_params->sample_rate ) != IVAS_ERR_OK ) ) #endif { fprintf( stderr, "Error: Reference audio file %s could not be opened\n\n", pIo_params->ref_path ); @@ -561,7 +566,7 @@ ivas_result_t ivas_crend_parse_io_params( int argc, char **argv, ivas_crend_io_p else if ( strcmp( to_upper( argv[i] ), "-IFMT" ) == 0 ) { pIo_params->in_fmt = atoi( argv[++i] ); - if ( ( pIo_params->in_fmt != MONO_1 ) && ( pIo_params->in_fmt != STEREO_2 ) && ( pIo_params->in_fmt != BIN_2 ) && ( pIo_params->in_fmt != FOA_4 ) && ( pIo_params->in_fmt != HOA_9 ) && ( pIo_params->in_fmt != HOA_16 ) && ( pIo_params->in_fmt != MULT_CH_5_1 ) && ( pIo_params->in_fmt != MULT_CH_7_1 ) && ( pIo_params->in_fmt != MULT_CH_5_1_2 ) && ( pIo_params->in_fmt != MULT_CH_7_1_4 ) && ( pIo_params->in_fmt != OBA ) ) + if ( ( pIo_params->in_fmt != MONO_1 ) && ( pIo_params->in_fmt != STEREO_2 ) && ( pIo_params->in_fmt != BIN_2 ) && ( pIo_params->in_fmt != FOA_4 ) && ( pIo_params->in_fmt != HOA_9 ) && ( pIo_params->in_fmt != HOA_16 ) && ( pIo_params->in_fmt != MULT_CH_5_1 ) && ( pIo_params->in_fmt != MULT_CH_7_1 ) && ( pIo_params->in_fmt != MULT_CH_5_1_2 ) && ( pIo_params->in_fmt != MULT_CH_5_1_4 ) && ( pIo_params->in_fmt != MULT_CH_7_1_4 ) && ( pIo_params->in_fmt != OBA ) ) { fprintf( stderr, "Error: Invalid input format\n\n" ); ivas_crend_unit_test_usage(); @@ -575,7 +580,7 @@ ivas_result_t ivas_crend_parse_io_params( int argc, char **argv, ivas_crend_io_p else if ( strcmp( to_upper( argv[i] ), "-OFMT" ) == 0 ) { pIo_params->out_fmt = atoi( argv[++i] ); - if ( ( pIo_params->out_fmt != MONO_1 ) && ( pIo_params->out_fmt != STEREO_2 ) && ( pIo_params->out_fmt != BIN_2 ) && ( pIo_params->out_fmt != FOA_4 ) && ( pIo_params->out_fmt != HOA_9 ) && ( pIo_params->out_fmt != HOA_16 ) && ( pIo_params->out_fmt != MULT_CH_5_1 ) && ( pIo_params->out_fmt != MULT_CH_7_1 ) && ( pIo_params->out_fmt != MULT_CH_5_1_2 ) && ( pIo_params->out_fmt != MULT_CH_7_1_4 ) ) + if ( ( pIo_params->out_fmt != MONO_1 ) && ( pIo_params->out_fmt != STEREO_2 ) && ( pIo_params->out_fmt != BIN_2 ) && ( pIo_params->out_fmt != FOA_4 ) && ( pIo_params->out_fmt != HOA_9 ) && ( pIo_params->out_fmt != HOA_16 ) && ( pIo_params->out_fmt != MULT_CH_5_1 ) && ( pIo_params->out_fmt != MULT_CH_7_1 ) && ( pIo_params->out_fmt != MULT_CH_5_1_2 ) && ( pIo_params->out_fmt != MULT_CH_5_1_4 ) && ( pIo_params->out_fmt != MULT_CH_7_1_4 ) ) { fprintf( stderr, "Error: Invalid output format\n\n" ); ivas_crend_unit_test_usage(); @@ -861,7 +866,7 @@ static ivas_result_t ivas_wrapper_get_in_buf( ivas_crend_io_params_t *pIo_params int16_t tmp, read = 0; num_in_ch = ivas_get_num_channels( pIo_params->in_fmt ); - if ( pIo_params->test == CREND_BIN_TEST || pIo_params->test == FASTCONV_BIN_TEST || pIo_params->test == PARAM_BIN_TEST || pIo_params->test == TD_BIN_TEST || pIo_params->test == CREND_ACOUSTIC_PROXIMITY) + if ( pIo_params->test == CREND_BIN_TEST || pIo_params->test == FASTCONV_BIN_TEST || pIo_params->test == PARAM_BIN_TEST || pIo_params->test == TD_BIN_TEST || pIo_params->test == CREND_ACOUSTIC_PROXIMITY ) { /* Read in PCM */ for ( j = 0; j < input_frame_len; j++ ) @@ -871,11 +876,11 @@ static ivas_result_t ivas_wrapper_get_in_buf( ivas_crend_io_params_t *pIo_params #ifdef USE_PCM_OUT if ( ( read = (int16_t) fread( &tmp, sizeof( int16_t ), 1, pIo_params->fIn[0] ) ) > 0 ) #else - if ( (AudioFileReader_read( pIo_params->fIn[0], &tmp, 1, &read ) == IVAS_ERR_OK) && (read > 0) ) + if ( ( AudioFileReader_read( pIo_params->fIn[0], &tmp, 1, &read ) == IVAS_ERR_OK ) && ( read > 0 ) ) #endif { ppPcm_in[i][j] = (float) tmp; - ppPcm_in[i][j] *= ( 1.0 / PCM16_TO_FLT_FAC ); + // ppPcm_in[i][j] *= ( 1.0 / PCM16_TO_FLT_FAC ); samples_read += 1; } else @@ -911,9 +916,9 @@ static ivas_result_t ivas_wrapper_get_in_buf( ivas_crend_io_params_t *pIo_params #else if ( ( AudioFileReader_read( pIo_params->fIn[i], &tmp, 1, &read ) == IVAS_ERR_OK ) && ( read > 0 ) ) #endif - { + { ppPcm_in[i][j] = (float) tmp; - ppPcm_in[i][j] *= ( 1.0 / PCM16_TO_FLT_FAC ); + // ppPcm_in[i][j] *= ( 1.0 / PCM16_TO_FLT_FAC ); samples_read += 1; } else @@ -943,14 +948,14 @@ static ivas_result_t ivas_wrapper_get_in_buf( ivas_crend_io_params_t *pIo_params *---------------------------------------------------------------------*/ static ivas_result_t ivas_feed_head_track_data( - Decoder_Struct *st_ivas, /* i/o: IVAS decoder handle */ + Decoder_Struct *st_ivas, /* i/o: IVAS decoder handle */ IVAS_QUATERNION *orientation /* i : head-tracking data */ ) { HEAD_TRACK_DATA_HANDLE hHeadTrackData; int16_t i; - + hHeadTrackData = st_ivas->hHeadTrackData; if ( hHeadTrackData == NULL ) @@ -999,6 +1004,7 @@ static void ivas_copy_io_params_to_dec_io_params( ivas_crend_io_params_t *pIo_pa case MULT_CH_5_1: case MULT_CH_7_1: case MULT_CH_5_1_2: + case MULT_CH_5_1_4: case MULT_CH_7_1_4: pDec_io_params->lfe_ch_idx = IVAS_DEFAULT_LFE_CH_IDX; break; @@ -1109,7 +1115,7 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl { st_ivas.ivas_format = SBA_FORMAT; } - + if ( ( pIo_params->out_fmt == BIN_2 ) && ( pIo_params->in_fmt != pIo_params->out_fmt ) ) { if ( pIo_params->test == FASTCONV_BIN_TEST ) @@ -1124,8 +1130,8 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl { st_ivas.renderer_type = RENDERER_BINAURAL_FASTCONV; st_ivas.hOutSetup.output_config = st_ivas.hDecoderConfig->output_config = AUDIO_CONFIG_BINAURAL; - if (st_ivas.ivas_format == MC_FORMAT) - pIo_params->latency_s = ( (float) frame_len ) / (float) dec_io_params.out_sample_rate + FASTCONV_HRIR_latency_s ; + if ( st_ivas.ivas_format == MC_FORMAT ) + pIo_params->latency_s = ( (float) frame_len ) / (float) dec_io_params.out_sample_rate + FASTCONV_HRIR_latency_s; else pIo_params->latency_s = ( (float) frame_len ) / (float) dec_io_params.out_sample_rate + FASTCONV_HOA3_latency_s; } @@ -1137,7 +1143,7 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl fprintf( stderr, "TD Renderer configuration wrong input format\n" ); exit( -1 ); } - + if ( st_ivas.hRenderConfig->roomAcoustics.use_brir ) { st_ivas.renderer_type = RENDERER_BINAURAL_OBJECTS_TD; @@ -1164,7 +1170,7 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl } pIo_params->latency_s = ( (float) frame_len ) / (float) dec_io_params.out_sample_rate; } - else if( pIo_params->test == CREND_BIN_TEST ) + else if ( pIo_params->test == CREND_BIN_TEST ) { if ( st_ivas.hRenderConfig->roomAcoustics.use_brir ) { @@ -1203,13 +1209,12 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl { if ( ( error = ivas_headTrack_open( &( st_ivas.hHeadTrackData ) ) ) != IVAS_ERR_OK ) { - return error; + return IVAS_FAILED; } - if ( ( error = HeadRotationFileReader_open( pIo_params->csv_path , &headRotReader ) ) != IVAS_ERR_OK ) + if ( ( error = HeadRotationFileReader_open( pIo_params->csv_path, &headRotReader ) ) != IVAS_ERR_OK ) { - return error; + return IVAS_FAILED; } - } st_ivas.nchan_transport = audioCfg2channels( st_ivas.transport_config ); @@ -1259,8 +1264,8 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl { if ( ( pIo_params->in_fmt != FOA_4 ) && ( pIo_params->in_fmt != HOA_9 ) && ( pIo_params->in_fmt != HOA_16 ) ) { - fprintf( stderr, "PARAM renderer configuration wrong format, must be FOA or HOA up to order 3\n" ); - exit( -1 ); + fprintf( stderr, "PARAM renderer configuration wrong format, must be FOA or HOA up to order 3\n" ); + exit( -1 ); } ivas_output_init( &st_ivas.hIntSetup, st_ivas.transport_config ); @@ -1303,7 +1308,7 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl if ( pIo_params->test == CREND_BIN_TEST ) { - pIo_params->latency_s = st_ivas.hHrtf->latency_s; + pIo_params->latency_s = st_ivas.hHrtf->latency_s; } if ( st_ivas.ivas_format == MC_FORMAT ) { @@ -1322,7 +1327,8 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl { if ( ( st_ivas.hLFE->lfe_delay_buf = (float *) count_malloc( st_ivas.hLFE->lfe_addl_delay * sizeof( float ) ) ) == NULL ) { - return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for LFE additional delay buffer\n" ) ); + fprintf( stderr, "Can not allocate memory for LFE additional delay buffer\n" ); + return IVAS_FAILED; } set_zero( (float *) st_ivas.hLFE->lfe_delay_buf, st_ivas.hLFE->lfe_addl_delay ); } @@ -1340,7 +1346,8 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl { if ( ( ppDelay_lines[i] = (float *) count_malloc( delay_lp * sizeof( float ) ) ) == NULL ) { - return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for LFE additional delay buffer\n" ) ); + fprintf( stderr, "Can not allocate memory for LFE additional delay buffer\n" ); + return IVAS_FAILED; } set_zero( (float *) ppDelay_lines[i], delay_lp ); } @@ -1353,7 +1360,7 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl delay_lp = 0; } } - + if ( pIo_params->limiter_enable ) { st_ivas.hLimiter = ivas_limiter_open( out_ch, pIo_params->sample_rate ); @@ -1380,54 +1387,42 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl { skip_samples = NS2SA( pIo_params->sample_rate, (int32_t) ( pIo_params->latency_s * 1000000000.f ) ); } - fprintf( stdout, "IVAS Common Renderer skip samples = %d\n", (int)skip_samples ); + fprintf( stdout, "IVAS Common Renderer skip samples = %d\n", (int) skip_samples ); frame_len = frame_len << 2; - int32_t frame_dec=0; - IVAS_QUATERNION Quaternions[IVAS_MAX_PARAM_SPATIAL_SUBFRAMES] = {0}; + int32_t frame_dec = 0; + IVAS_QUATERNION Quaternions[IVAS_MAX_PARAM_SPATIAL_SUBFRAMES] = { 0 }; /* process loop */ - while ( ( ( result = ivas_wrapper_get_in_buf( pIo_params, ppPcm_in ) ) == IVAS_SUCCESS ) && ( (st_ivas.hDecoderConfig->Opt_Headrotation == 0) || ( (st_ivas.hDecoderConfig->Opt_Headrotation == 1) && (HeadRotationFileReading( headRotReader, Quaternions, frame_dec ) == IVAS_ERR_OK ) ) ) ) + while ( ( ( result = ivas_wrapper_get_in_buf( pIo_params, ppPcm_in ) ) == IVAS_SUCCESS ) && ( ( st_ivas.hDecoderConfig->Opt_Headrotation == 0 ) || ( ( st_ivas.hDecoderConfig->Opt_Headrotation == 1 ) && ( HeadRotationFileReading( headRotReader, Quaternions, frame_dec ) == IVAS_ERR_OK ) ) ) ) { int16_t pcm[MAX_OUTPUT_CHANNELS]; frame_dec++; result = IVAS_SUCCESS; - if ( ( st_ivas.hDecoderConfig->Opt_Headrotation == 1 ) && (ivas_feed_head_track_data( &st_ivas, Quaternions ) != IVAS_SUCCESS )) + if ( ( st_ivas.hDecoderConfig->Opt_Headrotation == 1 ) && ( ivas_feed_head_track_data( &st_ivas, Quaternions ) != IVAS_SUCCESS ) ) { return IVAS_IO_ERROR; } - if ( pIo_params->lfe_lp_enable ) { - int32_t idx = 0; - for ( i = 0; i < in_ch; i++ ) + /* ADD delay to make overall max(block_offset, 11.5)*/ + if ( st_ivas.hLFE->lfe_addl_delay > 0 ) { - if ( i != lfe_ch_idx ) - { - delay_signal( ppPcm_in[idx], frame_len, ppDelay_lines[idx], delay_lp ); - idx++; - } + delay_signal( ppPcm_in[lfe_ch_idx], frame_len, st_ivas.hLFE->lfe_delay_buf, st_ivas.hLFE->lfe_addl_delay ); } + ivas_filter_process( &st_ivas.hLFE->filter_state, ppPcm_in[lfe_ch_idx], frame_len ); } if ( pIo_params->test == FASTCONV_BIN_TEST ) { ivas_binaural_cldfb( &st_ivas, ppPcm_in ); - for ( i = 0; i < out_ch; i++ ) - { - mvr2r( ppPcm_in[i], ppPcm_out[i], frame_len ); - } - } + } else if ( pIo_params->test == TD_BIN_TEST ) { ObjRenderIVASFrame( &st_ivas, ppPcm_in, frame_len ); - for ( i = 0; i < out_ch; i++ ) - { - mvr2r( ppPcm_in[i], ppPcm_out[i], frame_len ); - } } else if ( pIo_params->test == PARAM_BIN_TEST ) { @@ -1441,7 +1436,7 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl /* Implement a 5 msec loops */ maxBand = (int16_t) ( ( CLDFB_NO_CHANNELS_MAX * st_ivas.hDecoderConfig->output_Fs ) / 48000 ); - for ( slot_idx = 0; slot_idx < (int16_t)CLDFB_NO_COL_MAX; slot_idx++ ) + for ( slot_idx = 0; slot_idx < (int16_t) CLDFB_NO_COL_MAX; slot_idx++ ) { for ( ch = 0; ch < in_ch; ch++ ) @@ -1453,7 +1448,7 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl } } - for ( slot_idx = 0; slot_idx < (int16_t)CLDFB_NO_COL_MAX ; slot_idx++ ) + for ( slot_idx = 0; slot_idx < (int16_t) CLDFB_NO_COL_MAX; slot_idx++ ) { /* Implement binaural rendering */ for ( ch = 0; ch < BINAURAL_CHANNELS; ch++ ) @@ -1470,7 +1465,7 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl { filterTapsRealPtr = hrtfShCoeffsRe[ch][chIdx]; filterTapsImagPtr = hrtfShCoeffsIm[ch][chIdx]; - + for ( bandIdx = 0; bandIdx < maxBand; bandIdx++ ) { RealBuffer[bandIdx] += gain * ( Cldfb_RealBuffer[chIdx][slot_idx][bandIdx] * filterTapsRealPtr[bandIdx] ) - ( Cldfb_ImagBuffer[chIdx][slot_idx][bandIdx] * filterTapsImagPtr[bandIdx] ); @@ -1482,12 +1477,6 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl cldfbSynthesis( &outSlotRePr, &outSlotImPr, &( ppPcm_in[ch][slot_idx * maxBand] ), maxBand, st_ivas.cldfbSynDec[ch] ); } } - - for ( i = 0; i < out_ch; i++ ) - { - mvr2r( ppPcm_in[i], ppPcm_out[i], frame_len ); - } - } else { @@ -1500,32 +1489,26 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl ivas_crend_process( &st_ivas, ppPcm_in ); } } - + if ( mixer == NULL ) { - if ( st_ivas.hLFE ) + if ( pIo_params->lfe_lp_enable ) { - /* if ( st_ivas.hLFE->filter_state.order > 0 ) + for ( i = 0; i < out_ch; i++ ) { - - ivas_filter_process( &st_ivas.hLFE->filter_state, ppPcm_in[lfe_ch_idx], frame_len ); - }*/ - - /* ADD delay to make overall max(block_offset, 11.5)*/ - if ( st_ivas.hLFE->lfe_addl_delay > 0 ) - { - delay_signal( ppPcm_in[lfe_ch_idx], frame_len, st_ivas.hLFE->lfe_delay_buf, st_ivas.hLFE->lfe_addl_delay ); + delay_signal( ppPcm_in[i], frame_len, ppDelay_lines[i], delay_lp ); } - - ivas_binaural_add_LFE( &st_ivas, frame_len, ppPcm_in ); } + + ivas_binaural_add_LFE( &st_ivas, frame_len, ppPcm_in ); + for ( i = 0; i < out_ch; i++ ) { mvr2r( ppPcm_in[i], ppPcm_out[i], frame_len ); } if ( pIo_params->limiter_enable ) { - ivas_limiter_dec( st_ivas.hLimiter, ppPcm_out, out_ch, frame_len, FALSE ); + ivas_limiter_dec( st_ivas.hLimiter, ppPcm_out, out_ch, frame_len, 0 ); } } @@ -1540,7 +1523,8 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl } for ( i = 0; i < out_ch; i++ ) { - float temp = roundf( ppPcm_out[i][j] * PCM16_TO_FLT_FAC ); + // float temp = roundf( ppPcm_out[i][j] * PCM16_TO_FLT_FAC ); + float temp; #ifdef _FIND_MAX_ valMaxLoc = ( ppPcm_out[i][j] > valMaxLoc ) ? ppPcm_out[i][j] : ( ppPcm_out[i][j] < -valMaxLoc ) ? -ppPcm_out[i][j] : valMaxLoc; @@ -1549,15 +1533,14 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl valMax = valMaxLoc; valEner += ppPcm_out[i][j] * ppPcm_out[i][j]; #endif - pcm[i] = ( temp > MAX16B ) ? MAX16B : ( temp < MIN16B_FLT ) ? MIN16B - : (short) temp; - clip = max( clip, fabs( ppPcm_out[i][j] ) ); - - + temp = ( ppPcm_out[i][j] > MAX16B_FLT ) ? MAX16B_FLT : ( ppPcm_out[i][j] < MIN16B_FLT ) ? MIN16B_FLT + : ppPcm_out[i][j]; + pcm[i] = (short) roundf( temp ); + clip = max( clip, fabsf( ppPcm_out[i][j] ) ); } if ( write_flag == 1 ) { -#ifdef USE_PCM_OUT +#ifdef USE_PCM_OUT fwrite( pcm, sizeof( int16_t ), out_ch, pIo_params->fOut ); #else AudioFileWriter_write( pIo_params->fOut, pcm, out_ch ); @@ -1566,17 +1549,16 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl if ( write_flag == 0 ) skipped_samples++; } - if ( clip > 1.0f ) + if ( clip > MAX16B_FLT ) { fprintf( stdout, "IVAS Common Renderer Clipped: max gain = %f\n", clip ); } fprintf( stdout, "Processed frame: %ld\r", (long) frame_count ); frame_count++; - } - int16_t pcm[MAX_OUTPUT_CHANNELS] = {0}; + int16_t pcm[MAX_OUTPUT_CHANNELS] = { 0 }; while ( skipped_samples > 0 ) { AudioFileWriter_write( pIo_params->fOut, pcm, out_ch ); @@ -1594,7 +1576,7 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl ivas_lfe_dec_close( st_ivas.hLFE ); } - if ( st_ivas.hDirAC != NULL ) + if ( st_ivas.hDirAC != NULL ) ivas_dirac_dec_close( st_ivas.hDirAC ); if ( st_ivas.hBinRenderer != NULL ) ivas_binRenderer_close( &st_ivas.hBinRenderer ); @@ -1603,6 +1585,8 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl ivas_td_binaural_close( &st_ivas.hBinRendererTd ); if ( st_ivas.hLimiter != NULL ) ivas_limiter_close( &st_ivas.hLimiter ); + if ( st_ivas.hEFAPdata != NULL ) + efap_free_data( &st_ivas.hEFAPdata ); if ( pIo_params->lfe_lp_enable ) { @@ -1611,7 +1595,7 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl count_free( ppDelay_lines[i] ); } } - if ( st_ivas.hRenderConfig->roomAcoustics.late_reverb_on ) + if ( st_ivas.hRenderConfig != NULL ) { ivas_render_config_close( &st_ivas.hRenderConfig ); } @@ -1769,7 +1753,8 @@ ivas_result_t ivas_object_mixer_renderer( ivas_crend_io_params_t *pIo_params, in for ( i = 0; i < out_ch; i++ ) { - float temp = roundf( ppPcm_out[i][j] * PCM16_TO_FLT_FAC ); + // float temp = roundf( ppPcm_out[i][j] * PCM16_TO_FLT_FAC ); + float temp = roundf( ppPcm_out[i][j] ); #ifdef _FIND_MAX_ valMaxLoc = ( ppPcm_out[i][j] > valMaxLoc ) ? ppPcm_out[i][j] : ( ppPcm_out[i][j] < -valMaxLoc ) ? -ppPcm_out[i][j] @@ -1785,7 +1770,7 @@ ivas_result_t ivas_object_mixer_renderer( ivas_crend_io_params_t *pIo_params, in #ifdef USE_PCM_OUT pcm = ( temp > MAX16B_FLT ) ? MAX16B : ( temp < MIN16B_FLT ) ? MIN16B - : (short) temp; + : (short) temp; fwrite( &pcm, sizeof( int16_t ), 1, pIo_params->fOut ); #else pcm[i] = ( temp > MAX16B_FLT ) ? MAX16B : ( temp < MIN16B_FLT ) ? MIN16B diff --git a/scripts/ivas_pytests/tests/unit_tests/crend/ivas_dec_parse_io.h b/scripts/ivas_pytests/tests/unit_tests/crend/ivas_dec_parse_io.h index ed4071d8ca1a9f7127dbf4691dc9331fdc7c179e..97962a785b442aaa61f9e26ccf2c0d3290df75bd 100644 --- a/scripts/ivas_pytests/tests/unit_tests/crend/ivas_dec_parse_io.h +++ b/scripts/ivas_pytests/tests/unit_tests/crend/ivas_dec_parse_io.h @@ -83,6 +83,7 @@ #define IVAS_IN_FMT_510 "510" #define IVAS_IN_FMT_710 "710" #define IVAS_IN_FMT_512 "512" +#define IVAS_IN_FMT_514 "514" #define IVAS_IN_FMT_714 "714" #define IVAS_IN_FMT_FOA "HOA1S" #define IVAS_IN_FMT_HOA_2 "HOA2S" diff --git a/scripts/prepare_instrumentation.sh b/scripts/prepare_instrumentation.sh index 9463871f780007e33badac7e1771826bfe44eec9..d9fab685eab4ef6c8fed771d68e547608018b74c 100755 --- a/scripts/prepare_instrumentation.sh +++ b/scripts/prepare_instrumentation.sh @@ -90,7 +90,7 @@ fi # strip switches, to remove the macros if coan_exists; then - coan source --replace --no-transients -E -K --file $ifdef_list $targetdir/lib_{com,dec,enc,util,debug}/*.[hc] + coan source --replace --no-transients -E -K --file $ifdef_list $targetdir/lib_{com,dec,enc,rend,util,debug}/*.[hc] coan source --replace --no-transients -E -K --file $ifdef_list $targetdir/apps/*.[hc] else ./strip_defines_cppp.sh $targetdir $ifdef_list @@ -103,6 +103,7 @@ find $targetdir -name "*.[ch]" -exec sed -i.bak -e "s/\(0x[0-9a-fA-F]*\)UL/\(\(u ${emulator} ./wmc_tool.exe $targetdir/lib_enc/*.c /ic /op > /dev/null ${emulator} ./wmc_tool.exe $targetdir/lib_com/*.c /ic /op > /dev/null ${emulator} ./wmc_tool.exe $targetdir/lib_dec/*.c /ic /op > /dev/null +${emulator} ./wmc_tool.exe $targetdir/lib_rend/*.c /ic /op > /dev/null # automatically enable #define WMOPS in options.h sed -i.bak -e "s/\/\*\s*\(#define\s*WMOPS\)\s*\*\//\1/g" $targetdir/lib_com/options.h diff --git a/scripts/prerenderer/Makefile b/scripts/prerenderer/Makefile deleted file mode 100644 index 4201cd75e35fcddb78bb4f2a7532f018fc34c5b2..0000000000000000000000000000000000000000 --- a/scripts/prerenderer/Makefile +++ /dev/null @@ -1,172 +0,0 @@ -# GNU Makefile - -# Paths -SRC_LIBCOM = ../../lib_com -SRC_LIBDEBUG = ../../lib_debug -SRC_LIBDEC = ../../lib_dec -SRC_LIBENC = ../../lib_enc -SRC_LIBUTIL = ../../lib_util -SRC_APP = . -BUILD = build -OBJDIR = obj - -SRC_DIRS = $(sort -u $(SRC_LIBCOM) $(SRC_LIBDEBUG) $(SRC_LIBDEC) $(SRC_LIBENC) $(SRC_LIBUTIL) $(SRC_APP)) - -# Name of CLI binaries -CLI_PRD ?= IVAS_prerenderer -LIB_LIBCOM ?= libivascom.a -LIB_LIBDEBUG ?= libivasdebug.a -LIB_LIBDEC ?= libivasdec.a -LIB_LIBENC ?= libivasenc.a -LIB_LIBUTIL ?= libivasutil.a - -# Default tool settings -CC ?= gcc -RM ?= rm -f -AR ?= ar - -# Detect system -UNAME_S := $(shell uname -s) - -# Switches for cross-platform builds (i.e. build 32 bit code on 64 bit platforms) -ifneq "$(TARGET_PLATFORM)" "" -ifeq ("$(TARGET_PLATFORM)", "$(findstring $(TARGET_PLATFORM), i386 i586 i686)") - CFLAGS += -m32 - LDFLAGS += -m32 -endif - -ifeq ("$(TARGET_PLATFORM)", "$(findstring $(TARGET_PLATFORM), x86_64)") - CFLAGS += -m64 - LDFLAGS += -m64 -endif -endif - -ifndef VERBOSE -QUIET_CC = @echo ' ' Compiling $<; -QUIET_LINK= @echo ' ' Linking $@; -QUIET_AR = @echo ' ' Archiving $@; -QUIET = @ -endif - -# C compiler flags -CFLAGS += -std=c99 -pedantic -Wcast-qual -Wall -W -Wextra -Wno-long-long \ - -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes \ - -Werror-implicit-function-declaration -Wno-unused-parameter \ - -Wno-unused-function -Wno-implicit-fallthrough - -# libs to link -LDLIBS += -lm - -# Clang sanitizer compiler options -CCCLANG = clang -ifeq "$(CLANG)" "1" -CC = $(CCCLANG) -CFLAGS += -fsanitize=memory -LDFLAGS += -fsanitize=memory -endif -ifeq "$(CLANG)" "2" -CC = $(CCCLANG) -CFLAGS += -fsanitize=address -LDFLAGS += -fsanitize=address -endif -ifeq "$(CLANG)" "3" -CC = $(CCCLANG) -CFLAGS += -fsanitize=undefined -LDFLAGS += -fsanitize=undefined -endif - -ifeq "$(RELEASE)" "1" -CFLAGS += -DRELEASE -OPTIM ?= 2 -endif - -ifneq "$(DEBUG)" "0" -CFLAGS += -g3 -LDFLAGS += -g3 -endif - -ifeq "$(GCOV)" "1" -CFLAGS += -fprofile-arcs -ftest-coverage -LDFLAGS += -fprofile-arcs -ftest-coverage -endif - -ifeq "$(STRIP)" "1" -CFLAGS += -fdata-sections -ffunction-sections -ifneq ($(UNAME_S),Darwin) -LDFLAGS += -Wl,-gc-sections -static -else -LDFLAGS += -Wl,-dead_strip -endif -endif - -OPTIM ?= 0 -CFLAGS += -O$(OPTIM) - -CFLAGS += $(foreach DIR,$(SRC_DIRS),-I$(DIR)) - -# Source file search paths -VPATH = $(SRC_DIRS) - -############################################################################### - -SRCS_LIBCOM = $(foreach DIR,$(SRC_LIBCOM),$(patsubst $(DIR)/%,%,$(wildcard $(DIR)/*.c))) -SRCS_LIBDEBUG = $(foreach DIR,$(SRC_LIBDEBUG),$(patsubst $(DIR)/%,%,$(wildcard $(DIR)/*.c))) -SRCS_LIBDEC = $(foreach DIR,$(SRC_LIBDEC),$(patsubst $(DIR)/%,%,$(wildcard $(DIR)/*.c))) -SRCS_LIBENC = $(foreach DIR,$(SRC_LIBENC),$(patsubst $(DIR)/%,%,$(wildcard $(DIR)/*.c))) -SRCS_LIBUTIL = $(foreach DIR,$(SRC_LIBUTIL),$(patsubst $(DIR)/%,%,$(wildcard $(DIR)/*.c))) - -OBJS_LIBCOM = $(addprefix $(OBJDIR)/,$(SRCS_LIBCOM:.c=.o)) -OBJS_LIBDEBUG = $(addprefix $(OBJDIR)/,$(SRCS_LIBDEBUG:.c=.o)) -OBJS_LIBDEC = $(addprefix $(OBJDIR)/,$(SRCS_LIBDEC:.c=.o)) -OBJS_LIBENC = $(addprefix $(OBJDIR)/,$(SRCS_LIBENC:.c=.o)) -OBJS_LIBUTIL = $(addprefix $(OBJDIR)/,$(SRCS_LIBUTIL:.c=.o)) -OBJS_CLI_APPPRD = $(OBJDIR)/prerenderer.o - -DEPS = $(addprefix $(OBJDIR)/,$(SRCS_LIBCOM:.c=.P) $(SRCS_LIBDEBUG:.c=.P) $(SRCS_LIBDEC:.c=.P) \ - $(SRCS_LIBENC:.c=.P) $(SRCS_LIBUTIL:.c=.P)) - -############################################################################### - -.PHONY: all clean clean_all - -all: $(CLI_PRD) - -$(OBJDIR): - $(QUIET)mkdir -p $(OBJDIR) - -$(LIB_LIBCOM): $(OBJS_LIBCOM) - $(QUIET_AR)$(AR) rcs $@ $^ - -$(LIB_LIBDEBUG): $(OBJS_LIBDEBUG) - $(QUIET_AR)$(AR) rcs $@ $^ - -$(LIB_LIBDEC): $(OBJS_LIBDEC) - $(QUIET_AR)$(AR) rcs $@ $^ - -$(LIB_LIBENC): $(OBJS_LIBENC) - $(QUIET_AR)$(AR) rcs $@ $^ - -$(LIB_LIBUTIL): $(OBJS_LIBUTIL) - $(QUIET_AR)$(AR) rcs $@ $^ - -$(CLI_PRD): $(LIB_LIBENC) $(LIB_LIBDEC) $(LIB_LIBDEBUG) $(LIB_LIBCOM) $(LIB_LIBUTIL) $(OBJS_CLI_APPPRD) - $(QUIET_LINK)$(CC) $(LDFLAGS) $(OBJS_CLI_APPPRD) -L. -livasutil -livasenc -livasdec -livascom -livasdebug $(LDLIBS) -o $(CLI_PRD) - -libs: $(LIB_LIBENC) $(LIB_LIBDEBUG) $(LIB_LIBCOM) $(LIB_LIBDEC) $(LIB_LIBUTIL) - -clean: - $(QUIET)$(RM) $(OBJS_LIBENC) $(OBJS_LIBDEC) $(OBJS_CLI_APPPRD) $(DEPS) - $(QUIET)$(RM) $(DEPS:.P=.d) - $(QUIET)test ! -d $(OBJDIR) || rm -rf $(OBJDIR) - -clean_all: clean - $(QUIET)$(RM) $(CLI_PRD) $(LIB_LIBENC) $(LIB_LIBDEBUG) $(LIB_LIBCOM) $(LIB_LIBDEC) $(LIB_LIBUTIL) - -$(OBJDIR)/%.o : %.c | $(OBJDIR) - $(QUIET_CC)$(CC) $(CFLAGS) -c -MD -o $@ $< - @cp $(OBJDIR)/$*.d $(OBJDIR)/$*.P; \ - sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ - -e '/^$$/ d' -e 's/$$/ :/' < $(OBJDIR)/$*.d >> $(OBJDIR)/$*.P; \ - $(RM) $(OBJDIR)/$*.d - --include $(DEPS) diff --git a/scripts/prerenderer/Workspace_msvc/Workspace_msvc_prerenderer.sln b/scripts/prerenderer/Workspace_msvc/Workspace_msvc_prerenderer.sln deleted file mode 100644 index b36220e618a59a3e2434a60f264d518d9a7b5641..0000000000000000000000000000000000000000 --- a/scripts/prerenderer/Workspace_msvc/Workspace_msvc_prerenderer.sln +++ /dev/null @@ -1,48 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27428.2027 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "prerenderer", "prerenderer.vcxproj", "{12B4C8A5-1E06-4E30-B443-D1F916F52B47}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib_util", "..\..\..\Workspace_msvc\lib_util.vcxproj", "{2FA8F384-0775-F3B7-F8C3-85209222FC70}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib_debug", "..\..\..\Workspace_msvc\lib_debug.vcxproj", "{54509728-928B-44D9-A118-A6F92F08B34F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib_com", "..\..\..\Workspace_msvc\lib_com.vcxproj", "{39EC200D-7795-4FF8-B214-B24EDA5526AE}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib_dec", "..\..\..\Workspace_msvc\lib_dec.vcxproj", "{E822DDAF-0F5F-4CD0-A694-38AE69DE74D3}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {12B4C8A5-1E06-4E30-B443-D1F916F52B47}.Debug|Win32.ActiveCfg = Debug|Win32 - {12B4C8A5-1E06-4E30-B443-D1F916F52B47}.Debug|Win32.Build.0 = Debug|Win32 - {12B4C8A5-1E06-4E30-B443-D1F916F52B47}.Release|Win32.ActiveCfg = Release|Win32 - {12B4C8A5-1E06-4E30-B443-D1F916F52B47}.Release|Win32.Build.0 = Release|Win32 - {2FA8F384-0775-F3B7-F8C3-85209222FC70}.Debug|Win32.ActiveCfg = Debug|Win32 - {2FA8F384-0775-F3B7-F8C3-85209222FC70}.Debug|Win32.Build.0 = Debug|Win32 - {2FA8F384-0775-F3B7-F8C3-85209222FC70}.Release|Win32.ActiveCfg = Release|Win32 - {2FA8F384-0775-F3B7-F8C3-85209222FC70}.Release|Win32.Build.0 = Release|Win32 - {54509728-928B-44D9-A118-A6F92F08B34F}.Debug|Win32.ActiveCfg = Debug|Win32 - {54509728-928B-44D9-A118-A6F92F08B34F}.Debug|Win32.Build.0 = Debug|Win32 - {54509728-928B-44D9-A118-A6F92F08B34F}.Release|Win32.ActiveCfg = Release|Win32 - {54509728-928B-44D9-A118-A6F92F08B34F}.Release|Win32.Build.0 = Release|Win32 - {39EC200D-7795-4FF8-B214-B24EDA5526AE}.Debug|Win32.ActiveCfg = Debug|Win32 - {39EC200D-7795-4FF8-B214-B24EDA5526AE}.Debug|Win32.Build.0 = Debug|Win32 - {39EC200D-7795-4FF8-B214-B24EDA5526AE}.Release|Win32.ActiveCfg = Release|Win32 - {39EC200D-7795-4FF8-B214-B24EDA5526AE}.Release|Win32.Build.0 = Release|Win32 - {E822DDAF-0F5F-4CD0-A694-38AE69DE74D3}.Debug|Win32.ActiveCfg = Debug|Win32 - {E822DDAF-0F5F-4CD0-A694-38AE69DE74D3}.Debug|Win32.Build.0 = Debug|Win32 - {E822DDAF-0F5F-4CD0-A694-38AE69DE74D3}.Release|Win32.ActiveCfg = Release|Win32 - {E822DDAF-0F5F-4CD0-A694-38AE69DE74D3}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {06AC6878-76C6-4079-956D-B3FF3DB2C9A5} - EndGlobalSection -EndGlobal diff --git a/scripts/prerenderer/Workspace_msvc/prerenderer.vcxproj.filters b/scripts/prerenderer/Workspace_msvc/prerenderer.vcxproj.filters deleted file mode 100644 index ae26ad88ba209012fb3c594768642c35547ad218..0000000000000000000000000000000000000000 --- a/scripts/prerenderer/Workspace_msvc/prerenderer.vcxproj.filters +++ /dev/null @@ -1,50 +0,0 @@ - - - - - dec_ivas_c - - - dec_ivas_c - - - dec_ivas_c - - - dec_ivas_c - - - dec_ivas_c - - - dec_ivas_c - - - dec_ivas_c - - - enc_ivas_c - - - - - - - - {4fc737f1-c7a5-4376-a066-2a32d752a2ff} - - - {93995380-89bd-4b04-88eb-625fbe52ebfb} - - - {46364e80-1212-3600-1d53-58d3725f5bdc} - - - {67da6ab6-f800-4c08-8b7a-83bb121aad01} - - - - - - - \ No newline at end of file diff --git a/scripts/prerenderer/prerenderer.c b/scripts/prerenderer/prerenderer.c deleted file mode 100644 index 57515e5fd2fd6243dcd3baf9568126677d12698c..0000000000000000000000000000000000000000 --- a/scripts/prerenderer/prerenderer.c +++ /dev/null @@ -1,1553 +0,0 @@ -/****************************************************************************************************** - - (C) 2022 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, - Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., - Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, - Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other - contributors to this repository. All Rights Reserved. - - This software is protected by copyright law and by international treaties. - The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB, - Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., - Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, - Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other - contributors to this repository retain full ownership rights in their respective contributions in - the software. This notice grants no license of any kind, including but not limited to patent - license, nor is any license granted by implication, estoppel or otherwise. - - Contributors are required to enter into the IVAS codec Public Collaboration agreement before making - contributions. - - This software is provided "AS IS", without any express or implied warranties. The software is in the - development stage. It is intended exclusively for experts who have experience with such software and - solely for the purpose of inspection. All implied warranties of non-infringement, merchantability - and fitness for a particular purpose are hereby disclaimed and excluded. - - Any dispute, controversy or claim arising under or in relation to providing this software shall be - submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in - accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and - the United Nations Convention on Contracts on the International Sales of Goods. - -*******************************************************************************************************/ - -#include "options.h" -#include "audio_file_reader.h" -#include "audio_file_writer.h" -#include "common_api_types.h" -#include "ivas_prerenderer.h" -#include "ism_file_reader.h" -#include "ivas_stat_dec.h" -#include "prot.h" -#ifdef WMOPS -#include "wmops.h" -#endif -#ifdef RAM_COUNTING_TOOL -#include "mem_count.h" -#endif -#include -#include -#include -#include -#include -#include - -#ifndef count_malloc -#ifdef RAM_COUNTING_TOOL -#define count_malloc( n1 ) MALLOC_FCT_CALL( n1 ) -#define count_calloc( n1, n2 ) CALLOC_FCT_CALL( n1, n2 ) -#define count_free( ptr ) FREE_FCT_CALL( ptr ) -#else -#define count_malloc( n1 ) malloc( n1 ) -#define count_calloc( n1, n2 ) calloc( n1, n2 ) -#define count_free( ptr ) free( ptr ) -#endif -#endif - -#ifndef min -#define min( x, y ) ( ( x ) < ( y ) ? ( x ) : ( y ) ) -#endif - -#ifndef max -#define max( x, y ) ( ( x ) > ( y ) ? ( x ) : ( y ) ) -#endif - -#define PRERENDERER_MAX_METADATA_LENGTH 8192 -#define PRERENDERER_MAX_METADATA_LINE_LENGTH 1024 -#define PRERENDERER_MAX_CMDLN_ARG_LENGTH FILENAME_MAX - -#if !defined( DEBUGGING ) && !defined( WMOPS ) -static -#endif - int32_t frame = 0; - -#ifdef _WIN32 -#define SEP_FOLDER '\\' -#else -#define SEP_FOLDER '/' -#endif - -#ifdef WMOPS -/* void print_stack_call_tree( void ); -int Const_Data_Size_ivas_prerenderer_mai( void ); // Not a typo -int Const_Data_Size_ivas_rom_prerenderer( void ); -extern int16_t *ptr_base_stack; -extern int16_t *ptr_max_stack; -extern int32_t wc_frame; -extern char location_max_stack[256]; */ - -/* clang-format off */ -/*------------------------------------------------------------------------------------------* -* Function to print complexity & memory estimates -*------------------------------------------------------------------------------------------*/ -/* void print_mem_prerenderer(size_t SRAM_size) -{ - fprintf( stdout, "\n\n --- Prerenderer cmdln demo memory usage --- \n\n" ); - - fprintf( stdout, "PROM size (prerenderer): %d words (or instructions)\n", PROM_Size_prerenderer ); - fprintf( stdout, "Stack size: %d words in %s() in frame #%d\n", ( ptr_base_stack - ptr_max_stack ) * sizeof( int16_t ) / sizeof( float ), location_max_stack, wc_frame ); - fprintf( stdout, "Table ROM size(prerenderer_main): %d words\n", (Const_Data_Size_ivas_prerenderer_mai() ) / sizeof( float ) ); - fprintf( stdout, "Table ROM size(prerenderer): %d words\n", (Const_Data_Size_ivas_rom_prerenderer() ) / sizeof( float ) ); -#ifdef RAM_COUNTING_TOOL - fprintf( stdout, "Static RAM size: %d words\n\n", SRAM_size ); -#endif - print_stack_call_tree(); - - fprintf( stdout, "Note: this is an optimistic estimate of the memory consumption assuming\n" ); - fprintf( stdout, " that each variable (short, long or float) in the codec requires\n" ); - fprintf( stdout, " 32 bits of memory and may therefore be represented by 1 word.\n" ); - fprintf( stdout, " The following formula is used: sizeof('memory array')/sizeof(float)\n\n" ); -} */ -/* clang-format on */ -#endif - -static const char *optInputFile = "--inputFile"; -static const char *optInputFileShort = "-if"; -static const char *optInputAmbisonics = "--inputAmbisonics"; -static const char *optInputAmbisonicsShort = "-if-sba"; -static const char *optInputMultichannel = "--inputMultichannel"; -static const char *optInputMultichannelShort = "-if-mc"; -static const char *optOutputFile = "--outputFile"; -static const char *optOutputFileShort = "-of"; -static const char *optInputAudio = "--inputAudio"; -static const char *optInputAudioShort = "-ia"; -static const char *optFrameSize = "--frameSize"; -static const char *optFrameSizeShort = "-fr"; -static const char *optOutputConfig = "--outputConfig"; -static const char *optOutputConfigShort = "-oc"; -static const char *optSampleRate = "--sampleRate"; -static const char *optSampleRateShort = "-fs"; -static const char *optNeverDropLfe = "--neverDropLfe"; -static const char *optNeverDropLfeShort = "-ndl"; - -typedef struct Prndr_IsmPositionProvider -{ - uint32_t frameCounter; - uint32_t numObjects; - IsmFileReader *ismReaders[PRERENDERER_MAX_ISM_INPUTS]; - uint32_t numPositions[PRERENDERER_MAX_ISM_INPUTS]; - Prndr_AudioObjectPosition *positions[PRERENDERER_MAX_ISM_INPUTS]; /* size: [PRERENDERER_MAX_ISM_INPUTS][numPositions[object_index]] */ - uint16_t *positionDurations[PRERENDERER_MAX_ISM_INPUTS]; /* size: [PRERENDERER_MAX_ISM_INPUTS][numPositions[object_index]] */ - uint32_t currentPositionIdxs[PRERENDERER_MAX_ISM_INPUTS]; /* Index of current position as listed in the metadata file */ - uint16_t durationCounters[PRERENDERER_MAX_ISM_INPUTS]; /* Number of frames spent at current position */ -} Prndr_IsmPositionProvider; - -typedef enum Prndr_ForcedInputFormat -{ - prndr_forced_input_format_none = 0, - prndr_forced_input_format_sba, - prndr_forced_input_format_mc -} Prndr_ForcedInputFormat; - -typedef struct Prndr_CmdlnArgs -{ - char inputFilePath[FILENAME_MAX]; - char outputFilePath[FILENAME_MAX]; - int16_t frameSize; - int32_t sampleRate; - Prndr_OutputConfig outConfig; - Prndr_ForcedInputFormat forcedInputFormat; - char forcedAudioInputFile[FILENAME_MAX]; - int8_t neverDropLfe; /* flag */ -} Prndr_CmdlnArgs; - -static int8_t getAmbisonicsFromNumChannels( - uint32_t numChannels, - Prndr_Ambisonics *ambisonics ); - -static int8_t getSpeakerLayoutFromNumChannels( - uint32_t numChannels, - Prndr_SpeakerLayout *layout ); - -static int8_t setInConfigFromSbaNumChannels( - int16_t numChannels, - Prndr_InputConfig *inConfig ); - -static int8_t setInConfigFromMcNumChannels( - int16_t numChannels, - Prndr_InputConfig *inConfig ); - -static Prndr_Ambisonics ambisonicsOrderToEnum( - int32_t order ); - -static Prndr_SpeakerLayout speakerLayoutCicpToEnum( - int32_t cicpIndex ); - -static int8_t parseOutConfig( - char *configString, - Prndr_OutputConfig *outConfig ); - -static void printUsage( - void ); - -static void parseConfigFile( - char *path, - char *audioFilePath, - Prndr_InputConfig *inConfig, - Prndr_IsmPositionProvider *positionProvider ); - -static Prndr_CmdlnArgs parseCmdlnArgs( - int32_t argc, - char **argv ); - -static Prndr_IsmPositionProvider *Prndr_IsmPositionProvider_open( - void ); - -static void Prndr_IsmPositionProvider_getNextFrame( - Prndr_IsmPositionProvider *positionProvider, - Prndr_AudioObjectMetadataBuffer *objectMetadataBuffer ); - -static void Prndr_IsmPositionProvider_close( - Prndr_IsmPositionProvider *positionProvider ); - -static void readFromShorthandMetadata( - Prndr_IsmPositionProvider *positionProvider, - Prndr_AudioObjectMetadataBuffer *objectMetadataBuffer, - uint32_t objIdx ); - -void getMetadataFromFileReader( - IsmFileReader *ismReader, - Prndr_AudioObjectMetadataBuffer *objectMetadataBuffer, - uint32_t objIdx ); - -static void splitConfigFile( - const char *mdfFilePath, - char *metadataString, - uint32_t *metadataStringLength, - char *wavFileName, - uint32_t *wavFileNameLength ); - -static char *readNextMetadataChunk( - char *line, - const char *delimiter ); - -static void parseUint8( - char *line, - uint8_t *ret ); - -static int8_t parseUint32( - char *line, - uint32_t *ret ); - -static void parseObjectPosition( - char *line, - Prndr_AudioObjectPosition *position, - uint16_t *positionDuration ); - -static void parseIsm( - char *line, - char *inDir, - Prndr_InputConfig *inConfig, - Prndr_IsmPositionProvider *positionProvider, - int32_t idx ); - -static void parseSba( - char *line, - Prndr_InputConfig *inConfig, - int32_t idx ); - -static void parseMc( - char *line, - Prndr_InputConfig *inConfig, - int32_t idx ); - -static void parseMetadata( - char *metadataString, - char *inDir, - Prndr_InputConfig *inConfig, - Prndr_IsmPositionProvider *positionProvider ); - -static void convert_backslash( - char *str ); - -static void remove_cr( - char *str ); - - -/* ============================================================================ */ - -int32_t main( int32_t argc, char **argv ) -{ - Prndr_CmdlnArgs args; - Prndr_Prerenderer *prerenderer; - Prndr_IsmPositionProvider *positionProvider; - Prndr_InputConfig inConfig; - char audioFilePath[FILENAME_MAX]; - AudioFileReader *audioReader = NULL; - int16_t numInChannels; - AudioFileWriter *audioWriter; - int32_t inBufferSize; - int32_t outBufferSize; - int16_t *inpInt16Buffer; - float *inFloatBuffer; - int16_t *outInt16Buffer; - float *outFloatBuffer; - Prndr_AudioBuffer inPrndrBuffer; - Prndr_AudioBuffer outPrndrBuffer; - int16_t numSamplesRead; - int16_t i; - ivas_error error = IVAS_ERR_OK; -#ifdef WMOPS - size_t SRAM_size; -#endif - -#ifdef WMOPS - reset_wmops(); - reset_stack(); -#endif - -#ifdef RAM_COUNTING_TOOL - mem_count_init( 0, USE_32BITS ); -#endif - - args = parseCmdlnArgs( argc, argv ); - - /* === Open === */ - prerenderer = Prndr_Prerenderer_open(); - positionProvider = Prndr_IsmPositionProvider_open(); - - convert_backslash( args.inputFilePath ); - convert_backslash( args.outputFilePath ); - convert_backslash( args.forcedAudioInputFile ); - - /* === Parse === */ - if ( args.forcedInputFormat == prndr_forced_input_format_none ) - { - /* Only parse config file if input config is not forced at cmdln */ - parseConfigFile( args.inputFilePath, audioFilePath, &inConfig, positionProvider ); - } - else - { - /* If input config is forced, input file path is the input audio file, not config file */ - strncpy( audioFilePath, args.inputFilePath, FILENAME_MAX ); - - /* Initialize inConfig - this will be overwritten when applying forced parameters, - * but not initializing here causes a compiler warning on msvc */ - inConfig.numAmbisonicsBuses = 0; - inConfig.numMultiChannelBuses = 0; - inConfig.numAudioObjects = 0; - } - - /* === Apply forced parameters === */ - if ( strlen( args.forcedAudioInputFile ) != 0 ) - { - strncpy( audioFilePath, args.forcedAudioInputFile, FILENAME_MAX ); - } - - AudioFileReader_open( &audioReader, audioFilePath, args.sampleRate ); - if ( !audioReader ) - { - fprintf( stderr, "Error opening file: %s\n", audioFilePath ); - exit( -1 ); - } - - if ( args.forcedInputFormat != prndr_forced_input_format_none ) - { - numInChannels = AudioFileReader_getNumChannels( audioReader ); - if ( numInChannels == 0 ) - { - fprintf( stderr, "File does not contain number of channels metadata, probably a raw file: %s\n", audioFilePath ); - exit( -1 ); - } - - if ( args.forcedInputFormat == prndr_forced_input_format_sba ) - { - - if ( setInConfigFromSbaNumChannels( numInChannels, &inConfig ) != 0 ) - { - fprintf( stderr, "File cannot be used with forced sba input: %s\n", audioFilePath ); - exit( -1 ); - } - } - else if ( args.forcedInputFormat == prndr_forced_input_format_mc ) - { - if ( setInConfigFromMcNumChannels( numInChannels, &inConfig ) != 0 ) - { - fprintf( stderr, "File cannot be used with forced mc input: %s\n", audioFilePath ); - exit( -1 ); - } - } - } - - /* === Configure === */ - if ( ( error = Prndr_Prerenderer_configure( prerenderer, inConfig, args.outConfig, args.frameSize, args.sampleRate ) ) != IVAS_ERR_OK ) - { - exit( -1 ); - } - - if ( args.neverDropLfe ) - { - Prndr_Prerenderer_setNeverDropLfe( prerenderer, 1 ); - } - - if ( Prndr_Prerenderer_getInChannels( prerenderer ) != AudioFileReader_getNumChannels( audioReader ) ) - { - fprintf( stderr, "Number of channels in input file does not match that defined in the config file\n" ); - exit( -1 ); - } - - /* === Process === */ - AudioFileWriter_open( &audioWriter, args.outputFilePath, args.sampleRate, Prndr_Prerenderer_getOutChannels( prerenderer ) ); - - if ( audioWriter == NULL ) - { - exit( -1 ); - } - - inBufferSize = args.frameSize * Prndr_Prerenderer_getInChannels( prerenderer ); - outBufferSize = args.frameSize * Prndr_Prerenderer_getOutChannels( prerenderer ); - inpInt16Buffer = count_calloc( inBufferSize, sizeof( int16_t ) ); - inFloatBuffer = count_calloc( inBufferSize, sizeof( float ) ); - outInt16Buffer = count_calloc( outBufferSize, sizeof( int16_t ) ); - outFloatBuffer = count_calloc( outBufferSize, sizeof( float ) ); - - inPrndrBuffer.config.sampleRate = args.sampleRate; - inPrndrBuffer.config.bufferSize = args.frameSize; - inPrndrBuffer.config.numChannels = Prndr_Prerenderer_getInChannels( prerenderer ); - inPrndrBuffer.data = inFloatBuffer; - - outPrndrBuffer.config.sampleRate = args.sampleRate; - outPrndrBuffer.config.bufferSize = args.frameSize; - outPrndrBuffer.config.numChannels = Prndr_Prerenderer_getOutChannels( prerenderer ); - outPrndrBuffer.data = outFloatBuffer; - -#ifdef WMOPS - reset_wmops(); -#endif - - while ( 1 ) - { - int32_t chnl, smpl; - int32_t num_in_channels; - num_in_channels = inPrndrBuffer.config.numChannels; - i = 0; - - /* Read the input data */ - if ( ( error = AudioFileReader_read( audioReader, inpInt16Buffer, inBufferSize, &numSamplesRead ) ) != IVAS_ERR_OK ) - { - fprintf( stderr, "\nError reading from file %s\n", audioFilePath ); - break; - } - - if ( numSamplesRead == 0 ) - { - /* end of input data */ - break; - } - - /* Convert from int to float and from interleaved to packed */ - for ( smpl = 0; smpl < args.frameSize; ++smpl ) - { - for ( chnl = 0; chnl < num_in_channels; ++chnl ) - { - if ( i < numSamplesRead ) - { - inFloatBuffer[chnl * args.frameSize + smpl] = (float) inpInt16Buffer[i] / INT16_MAX; - } - else - { - inFloatBuffer[chnl * args.frameSize + smpl] = 0.f; - } - - ++i; - } - } - - Prndr_AudioObjectMetadataBuffer mtdBuffer; - Prndr_IsmPositionProvider_getNextFrame( positionProvider, &mtdBuffer ); - - Prndr_Prerenderer_render( prerenderer, inPrndrBuffer, mtdBuffer, outPrndrBuffer ); - - int32_t num_out_channels; - num_out_channels = outPrndrBuffer.config.numChannels; - i = 0; - - /* Convert from float to int and from packed to interleaved */ - for ( smpl = 0; smpl < args.frameSize; ++smpl ) - { - for ( chnl = 0; chnl < num_out_channels; ++chnl ) - { - outInt16Buffer[i] = (int16_t)(outFloatBuffer[chnl * args.frameSize + smpl] * INT16_MAX); - - ++i; - } - } - - if ( AudioFileWriter_write( audioWriter, outInt16Buffer, outBufferSize ) != 0 ) - { - fprintf( stderr, "Error writing audio file %s\n", args.outputFilePath ); - exit( -1 ); - } - - frame++; - fprintf( stdout, "%-8d\b\b\b\b\b\b\b\b", frame ); - -#ifdef WMOPS - update_wmops(); -#endif - } - fprintf( stdout, "\n" ); - - /* === Close === */ - count_free( inpInt16Buffer ); - count_free( inFloatBuffer ); - count_free( outInt16Buffer ); - count_free( outFloatBuffer ); - AudioFileReader_close( &audioReader ); - AudioFileWriter_close( &audioWriter ); - Prndr_Prerenderer_close( prerenderer ); - Prndr_IsmPositionProvider_close( positionProvider ); - -#ifdef RAM_COUNTING_TOOL -#ifdef WMOPS - SRAM_size = -#endif - mem_count_summary( USE_DEFAULT ); -#endif -#ifdef WMOPS - print_wmops(); - /* print_mem_prerenderer( SRAM_size ); */ -#endif - - return 0; -} - -static int8_t getAmbisonicsFromNumChannels( uint32_t numChannels, - Prndr_Ambisonics *ambisonics ) -{ - switch ( numChannels ) - { - case 1: - *ambisonics = prndr_ambisonics_mono; - break; - case 4: - *ambisonics = prndr_ambisonics_foa; - break; - case 9: - *ambisonics = prndr_ambisonics_soa; - break; - case 16: - *ambisonics = prndr_ambisonics_toa; - break; - default: - fprintf( stderr, "Unable to infer ambisonics order from number of channels: %d\n", numChannels ); - return -1; - } - - return 0; -} - -static int8_t getSpeakerLayoutFromNumChannels( uint32_t numChannels, - Prndr_SpeakerLayout *layout ) -{ - switch ( numChannels ) - { - case 1: - *layout = prndr_speaker_layout_mono; - break; - case 2: - *layout = prndr_speaker_layout_stereo; - break; - case 6: - *layout = prndr_speaker_layout_5_1; - break; - case 8: - *layout = prndr_speaker_layout_7_1; - break; - case 10: - *layout = prndr_speaker_layout_5_1_4; - break; - case 12: - *layout = prndr_speaker_layout_7_1_4; - break; - default: - fprintf( stderr, "Unable to infer speaker layout from number of channels: %d\n", numChannels ); - return -1; - } - - return 0; -} - -static int8_t setInConfigFromSbaNumChannels( int16_t numChannels, Prndr_InputConfig *inConfig ) -{ - inConfig->numAmbisonicsBuses = 1; - inConfig->numAudioObjects = 0; - inConfig->numMultiChannelBuses = 0; - - inConfig->ambisonicsBuses[0].inputChannelIndex = 0; - return getAmbisonicsFromNumChannels( numChannels, &inConfig->ambisonicsBuses[0].ambisonicsConfig ); -} - -static int8_t setInConfigFromMcNumChannels( int16_t numChannels, Prndr_InputConfig *inConfig ) -{ - inConfig->numAmbisonicsBuses = 0; - inConfig->numAudioObjects = 0; - inConfig->numMultiChannelBuses = 1; - - inConfig->multiChannelBuses[0].inputChannelIndex = 0; - return getSpeakerLayoutFromNumChannels( numChannels, &inConfig->multiChannelBuses[0].speakerLayout ); -} - -static Prndr_Ambisonics ambisonicsOrderToEnum( int32_t order ) -{ - switch ( order ) - { - case 0: - return prndr_ambisonics_mono; - case 1: - return prndr_ambisonics_foa; - case 2: - return prndr_ambisonics_soa; - case 3: - return prndr_ambisonics_toa; - } - - return prndr_ambisonics_none; -} - -static Prndr_SpeakerLayout speakerLayoutCicpToEnum( int32_t cicpIndex ) -{ - switch ( cicpIndex ) - { - case 0: - return prndr_speaker_layout_custom; - case 1: - return prndr_speaker_layout_mono; - case 2: - return prndr_speaker_layout_stereo; - case 6: - return prndr_speaker_layout_5_1; - case 16: - return prndr_speaker_layout_5_1_4; - case 12: - return prndr_speaker_layout_7_1; - case 19: - return prndr_speaker_layout_7_1_4; - } - - return prndr_speaker_layout_none; -} - -static int8_t parseOutConfig( char *configString, Prndr_OutputConfig *outConfig ) -{ - char outType[5]; - int32_t num; - int8_t success; /* flag */ - - outType[4] = '\0'; - strncpy( outType, configString, 4 ); - - success = 1; - - if ( ( outType[0] == 's' || outType[0] == 'S' ) && - ( outType[1] == 'b' || outType[1] == 'B' ) && - ( outType[2] == 'a' || outType[2] == 'A' ) ) - { - num = strtol( &configString[3], NULL, 10 ); - - /* If num is 0, ensure it's because user requested it and not because of strtol error */ - if ( num == 0 && configString[3] != 0 && configString[4] != '\0' ) - { - success = 0; - } - else - { - outConfig->ambisonics = ambisonicsOrderToEnum( num ); - outConfig->outSetupCustom = NULL; - } - } - else if ( ( outType[0] == 'c' || outType[0] == 'C' ) && - ( outType[1] == 'i' || outType[1] == 'I' ) && - ( outType[2] == 'c' || outType[2] == 'C' ) && - ( outType[3] == 'p' || outType[3] == 'P' ) ) - { - num = strtol( &configString[4], NULL, 10 ); - outConfig->speakerLayout = speakerLayoutCicpToEnum( num ); - outConfig->outSetupCustom = NULL; - } - else - { - int16_t i, is_planar; - LsCustomFileReader *hLsCustomReader = NULL; - IVAS_CUSTOM_LS_DATA hLsCustomData; - - /* Default to interpreting as a custom loudspeaker layout file */ - outConfig->speakerLayout = speakerLayoutCicpToEnum( 0 ); - - CustomLsReader_open( configString, &hLsCustomReader ); - success = ( CustomLsFileReading( hLsCustomReader, &hLsCustomData ) == LS_CUSTOM_FILEREADER_NO_ERROR ); - - outConfig->outSetupCustom->num_spk = hLsCustomData.num_spk; - mvr2r( hLsCustomData.azimuth, outConfig->outSetupCustom->ls_azimuth, hLsCustomData.num_spk ); - mvr2r( hLsCustomData.elevation, outConfig->outSetupCustom->ls_elevation, hLsCustomData.num_spk ); - - /* Set planar flag */ - is_planar = 1; - for ( i = 0; i < hLsCustomData.num_spk; i++ ) - { - if ( is_planar && outConfig->outSetupCustom->ls_elevation[i] != 0.0f ) - { - is_planar = 0; - } - } - outConfig->outSetupCustom->is_planar_setup = is_planar; - - /* Loudspeaker LFE */ - outConfig->outSetupCustom->num_lfe = hLsCustomData.num_lfe; - mvs2s( hLsCustomData.lfe_idx, outConfig->outSetupCustom->lfe_idx, hLsCustomData.num_lfe ); - - CustomLsReader_close( &hLsCustomReader ); - } - - return success ? 0 : -1; -} - -static void printUsage( void ) -{ - printf( "\n" ); - printf( "Usage: ./IVAS_prerenderer [options]\n" ); - printf( "\n" ); - printf( "Valid options:\n" ); - printf( "%s %s Path to the input file (txt config file).\n", optInputFileShort, optInputFile ); - printf( "%s %s Path to the ambisonics audio file. Use this optionally instead of the config file for single-item input configurations.\n", optInputAmbisonicsShort, optInputAmbisonics ); - printf( "%s %s Path to the multichannel audio file. Use this optionally instead of the config file for single-item input configurations.\n", optInputMultichannelShort, optInputMultichannel ); - printf( "%s %s Path to the output file.\n", optOutputFileShort, optOutputFile ); - printf( "%s %s Path to the input audio (overrides audio path from txt config file or -if-sba and -if-mc options) - for debugging/testing purposes.\n", optInputAudioShort, optInputAudio ); - printf( "%s %s Size of processing frame (in ms). Default value is 20ms.\n", optFrameSizeShort, optFrameSize ); - printf( "%s %s Input sampling rate in kHz.\n", optSampleRateShort, optSampleRate ); - printf( "%s %s Output configuration. For ambisonics of order n, select SBAn (e.g. SBA1 for order 1).\n", optOutputConfigShort, optOutputConfig ); - printf( " For multichannel, select CICP followed by CICP index (e.g. CICP6, CICP19).\n" ); - printf( " Alternatively, can be a custom loudspeaker layout file.\n" ); - printf( "%s %s [flag] If set, prerenderer tries to render LFE into other channels in an optimal way when rendering to configs w/o LFE.\n", optNeverDropLfeShort, optNeverDropLfe ); -} - -static int8_t checkOpt( uint32_t argc, char **argv, uint32_t idx, uint32_t numFollowingTokens ) -{ - uint32_t i; - - if ( idx + numFollowingTokens >= argc ) - { - fprintf( stderr, "Error: option %s requires %d following tokens\n", argv[idx], numFollowingTokens ); - return 0; - } - - for ( i = 1; i < numFollowingTokens; ++i ) - { - if ( strlen( argv[idx + i] ) > PRERENDERER_MAX_CMDLN_ARG_LENGTH ) - { - fprintf( stderr, "Too many characters in token: %s\n", argv[idx + i] ); - return 0; - } - } - - return 1; -} - -static Prndr_CmdlnArgs parseCmdlnArgs( int32_t argc, char **argv ) -{ - Prndr_CmdlnArgs parsedArgs; - int8_t success; /* flag */ - int32_t i; - uint32_t numFollowingTokens; - - success = 1; - - parsedArgs.inputFilePath[0] = '\0'; - parsedArgs.outputFilePath[0] = '\0'; - parsedArgs.frameSize = -1; - parsedArgs.sampleRate = -1; - parsedArgs.outConfig.ambisonics = prndr_ambisonics_none; - parsedArgs.outConfig.speakerLayout = prndr_speaker_layout_none; - parsedArgs.forcedInputFormat = prndr_forced_input_format_none; - parsedArgs.forcedAudioInputFile[0] = '\0'; - parsedArgs.neverDropLfe = false; - - i = 1; - while ( i < argc ) - { - numFollowingTokens = 1; /* Default number of expected tokens after an option */ - - if ( strcmp( argv[i], optInputFileShort ) == 0 || strcmp( argv[i], optInputFile ) == 0 ) - { - if ( ( success = checkOpt( argc, argv, i, numFollowingTokens ) ) == 1 ) - { - if ( strlen( parsedArgs.inputFilePath ) == 0 ) - { - strncpy( parsedArgs.inputFilePath, argv[i + 1], FILENAME_MAX ); - } - else - { - fprintf( stderr, "Duplicate option: %s (%s)\n", optInputFile, optInputFileShort ); - success = 0; - } - } - } - else if ( strcmp( argv[i], optInputAmbisonicsShort ) == 0 || strcmp( argv[i], optInputAmbisonics ) == 0 ) - { - if ( ( success = checkOpt( argc, argv, i, numFollowingTokens ) ) == 1 ) - { - if ( strlen( parsedArgs.inputFilePath ) == 0 ) - { - strncpy( parsedArgs.inputFilePath, argv[i + 1], FILENAME_MAX ); - parsedArgs.forcedInputFormat = prndr_forced_input_format_sba; - } - else - { - fprintf( stderr, "Duplicate option: %s (%s)\n", optInputAmbisonics, optInputAmbisonicsShort ); - success = 0; - } - } - } - else if ( strcmp( argv[i], optInputMultichannelShort ) == 0 || strcmp( argv[i], optInputMultichannel ) == 0 ) - { - if ( ( success = checkOpt( argc, argv, i, numFollowingTokens ) ) == 1 ) - { - if ( strlen( parsedArgs.inputFilePath ) == 0 ) - { - strncpy( parsedArgs.inputFilePath, argv[i + 1], FILENAME_MAX ); - parsedArgs.forcedInputFormat = prndr_forced_input_format_mc; - } - else - { - fprintf( stderr, "Duplicate option: %s (%s)\n", optInputMultichannel, optInputMultichannelShort ); - success = 0; - } - } - } - else if ( strcmp( argv[i], optOutputFileShort ) == 0 || strcmp( argv[i], optOutputFile ) == 0 ) - { - if ( ( success = checkOpt( argc, argv, i, numFollowingTokens ) ) == 1 ) - { - if ( strlen( parsedArgs.outputFilePath ) == 0 ) - { - strncpy( parsedArgs.outputFilePath, argv[i + 1], FILENAME_MAX ); - } - else - { - fprintf( stderr, "Duplicate option: %s (%s)\n", optOutputFile, optOutputFileShort ); - success = 0; - } - } - } - else if ( strcmp( argv[i], optInputAudioShort ) == 0 || strcmp( argv[i], optInputAudio ) == 0 ) - { - if ( ( success = checkOpt( argc, argv, i, numFollowingTokens ) ) == 1 ) - { - if ( strlen( parsedArgs.forcedAudioInputFile ) == 0 ) - { - strncpy( parsedArgs.forcedAudioInputFile, argv[i + 1], FILENAME_MAX ); - } - else - { - fprintf( stderr, "Duplicate option: %s (%s)\n", optInputAudio, optInputAudioShort ); - success = 0; - } - } - } - else if ( strcmp( argv[i], optFrameSizeShort ) == 0 || strcmp( argv[i], optFrameSize ) == 0 ) - { - if ( ( success = checkOpt( argc, argv, i, numFollowingTokens ) ) == 1 ) - { - if ( parsedArgs.frameSize <= 0 ) - { - /* Save value in ms for now - later converted to samples once sample rate is known */ - parsedArgs.frameSize = strtol( argv[i + 1], NULL, 10 ); - } - else - { - fprintf( stderr, "Duplicate option: %s (%s)\n", optFrameSize, optFrameSizeShort ); - success = 0; - } - } - } - else if ( strcmp( argv[i], optOutputConfigShort ) == 0 || strcmp( argv[i], optOutputConfig ) == 0 ) - { - if ( ( success = checkOpt( argc, argv, i, numFollowingTokens ) ) == 1 ) - { - if ( parsedArgs.outConfig.ambisonics == prndr_ambisonics_none && parsedArgs.outConfig.speakerLayout == prndr_speaker_layout_none ) - { - if ( parseOutConfig( argv[i + 1], &parsedArgs.outConfig ) != 0 ) - { - fprintf( stderr, "Unknown output or bad config: %s\n", argv[i + 1] ); - success = 0; - } - } - else - { - fprintf( stderr, "Duplicate option: %s (%s)\n", optOutputConfig, optOutputConfigShort ); - success = 0; - } - } - } - else if ( strcmp( argv[i], optSampleRateShort ) == 0 || strcmp( argv[i], optSampleRate ) == 0 ) - { - if ( ( success = checkOpt( argc, argv, i, numFollowingTokens ) ) == 1 ) - { - if ( parsedArgs.sampleRate <= 0 ) - { - parsedArgs.sampleRate = ( int32_t )( strtof( argv[i + 1], NULL ) * 1000 ); - } - else - { - fprintf( stderr, "Duplicate option: %s (%s)\n", optSampleRate, optSampleRateShort ); - success = 0; - } - } - } - else if ( strcmp( argv[i], optNeverDropLfeShort ) == 0 || strcmp( argv[i], optNeverDropLfe ) == 0 ) - { - numFollowingTokens = 0; - - if ( ( success = checkOpt( argc, argv, i, numFollowingTokens ) ) == 1 ) - { - parsedArgs.neverDropLfe = true; - } - } - else - { - fprintf( stderr, "Unknown option: %s\n", argv[i] ); - success = 0; - } - - i += 1 + numFollowingTokens; - } - - /* Check if any of required args is missing */ - if ( strlen( parsedArgs.inputFilePath ) == 0 ) - { - fprintf( stderr, "Required option missing or invalid value: %s (%s) or %s (%s) or %s (%s)\n", - optInputFile, - optInputFileShort, - optInputAmbisonics, - optInputAmbisonicsShort, - optInputMultichannel, - optInputMultichannelShort ); - success = 0; - } - if ( strlen( parsedArgs.outputFilePath ) == 0 ) - { - fprintf( stderr, "Required option missing or invalid value: %s (%s)\n", optOutputFile, optOutputFileShort ); - success = 0; - } - if ( parsedArgs.outConfig.ambisonics == prndr_ambisonics_none && parsedArgs.outConfig.speakerLayout == prndr_speaker_layout_none ) - { - fprintf( stderr, "Required option missing or invalid value: %s (%s)\n", optOutputConfig, optOutputConfigShort ); - success = 0; - } - if ( parsedArgs.sampleRate <= 0 ) - { - fprintf( stderr, "Required option missing or invalid value: %s (%s)\n", optSampleRate, optSampleRateShort ); - success = 0; - } - - /* Set default values if not specified */ - if ( parsedArgs.frameSize <= 0 ) - { - parsedArgs.frameSize = parsedArgs.sampleRate / 50; - } - else - { - /* Convert from ms to samples */ - parsedArgs.frameSize = parsedArgs.sampleRate * parsedArgs.frameSize / 1000; - } - - if ( !success ) - { - printUsage(); - exit( -1 ); - } - - return parsedArgs; -} - - -Prndr_IsmPositionProvider *Prndr_IsmPositionProvider_open( void ) -{ - Prndr_IsmPositionProvider *ipp; - uint32_t i; - - ipp = (Prndr_IsmPositionProvider *) count_malloc( sizeof( Prndr_IsmPositionProvider ) ); - ipp->frameCounter = 0; - ipp->numObjects = 0; - - for ( i = 0; i < PRERENDERER_MAX_ISM_INPUTS; ++i ) - { - ipp->ismReaders[i] = NULL; - ipp->positions[i] = NULL; - ipp->positionDurations[i] = NULL; - ipp->currentPositionIdxs[i] = 0; - ipp->durationCounters[i] = 0; - } - - return ipp; -} - -void getMetadataFromFileReader( - IsmFileReader *ismReader, - Prndr_AudioObjectMetadataBuffer *objectMetadataBuffer, - uint32_t objIdx ) -{ - IVAS_ISM_METADATA ismMetadata; - ivas_error error; - - if ( ( error = IsmFileReader_readNextFrame( ismReader, &ismMetadata ) ) != IVAS_ERR_OK ) - { - fprintf( stderr, "\nError (%s) while reading ism metadata from: %s\n\n", ivas_error_to_string( error ), IsmFileReader_getFilePath( ismReader ) ); - exit( -1 ); - } - - objectMetadataBuffer->positions[objIdx].azimuth = ismMetadata.azimuth; - objectMetadataBuffer->positions[objIdx].elevation = ismMetadata.elevation; -} - -void readFromShorthandMetadata( Prndr_IsmPositionProvider *positionProvider, - Prndr_AudioObjectMetadataBuffer *objectMetadataBuffer, - uint32_t objIdx ) -{ - uint32_t preUpdatePositionIdx; - uint32_t postUpdatePositionIdx; - - preUpdatePositionIdx = positionProvider->currentPositionIdxs[objIdx]; - - if ( positionProvider->durationCounters[objIdx] == positionProvider->positionDurations[objIdx][preUpdatePositionIdx] ) - { - positionProvider->durationCounters[objIdx] = 0; - positionProvider->currentPositionIdxs[objIdx] = ( positionProvider->currentPositionIdxs[objIdx] + 1 ) % positionProvider->numPositions[objIdx]; - } - - ++positionProvider->durationCounters[objIdx]; - - postUpdatePositionIdx = positionProvider->currentPositionIdxs[objIdx]; - - objectMetadataBuffer->positions[objIdx] = positionProvider->positions[objIdx][postUpdatePositionIdx]; -} - -void Prndr_IsmPositionProvider_getNextFrame( - Prndr_IsmPositionProvider *positionProvider, - Prndr_AudioObjectMetadataBuffer *objectMetadataBuffer ) -{ - uint32_t objIdx; - int16_t aziShifted; - - objectMetadataBuffer->numObjects = positionProvider->numObjects; - - for ( objIdx = 0; objIdx < positionProvider->numObjects; ++objIdx ) - { - if ( positionProvider->ismReaders[objIdx] != NULL ) - { - getMetadataFromFileReader( positionProvider->ismReaders[objIdx], objectMetadataBuffer, objIdx ); - } - else - { - readFromShorthandMetadata( positionProvider, objectMetadataBuffer, objIdx ); - } - - /* Wrap azimuth to lie within (-180, 180] range */ - aziShifted = objectMetadataBuffer->positions[objIdx].azimuth + 180; - objectMetadataBuffer->positions[objIdx].azimuth = aziShifted <= 0 || aziShifted % 360 == 0 ? ( aziShifted % 360 ) + 180 : ( aziShifted % 360 ) - 180; - - /* Clamp elevation to lie within [-90, 90] range (can't be wrapped easily) */ - objectMetadataBuffer->positions[objIdx].elevation = min( max( objectMetadataBuffer->positions[objIdx].elevation, -90 ), 90 ); - } - - ++positionProvider->frameCounter; -} - -void Prndr_IsmPositionProvider_close( Prndr_IsmPositionProvider *positionProvider ) -{ - uint32_t i; - - if ( positionProvider == NULL ) - { - assert( !"Can't close Prndr_IsmPositionProvider - pointer is NULL" ); - } - - for ( i = 0; i < PRERENDERER_MAX_ISM_INPUTS; ++i ) - { - if ( positionProvider->ismReaders[i] != NULL ) - { - IsmFileReader_close( &positionProvider->ismReaders[i] ); - } - - if ( positionProvider->positions[i] != NULL ) - { - count_free( positionProvider->positions[i] ); - } - - if ( positionProvider->positionDurations[i] != NULL ) - { - count_free( positionProvider->positionDurations[i] ); - } - } - - count_free( positionProvider ); -} - -static void splitConfigFile( const char *mdfFilePath, - char *metadataString, - uint32_t *metadataStringLength, - char *wavFileName, - uint32_t *wavFileNameLength ) -{ - FILE *file; - uint32_t bufferlength; - char wavLine[FILENAME_MAX]; - uint32_t currentPositionIdxs; - uint32_t mdlength; - - memset( metadataString, 0, *metadataStringLength ); - memset( wavFileName, 0, (int16_t) *wavFileNameLength ); - - file = fopen( mdfFilePath, "rb" ); - if ( !file ) - { - fprintf( stderr, "Couldn't open metadata file %s\n", mdfFilePath ); - exit( -1 ); - } - - fseek( file, 0, SEEK_END ); - bufferlength = ftell( file ); - fseek( file, 0, SEEK_SET ); - - if ( fgets( wavLine, (int) *wavFileNameLength, file ) == NULL ) - { - fprintf( stderr, "Error reading metadata\n" ); - exit( -1 ); - } - currentPositionIdxs = ftell( file ); - if ( *wavFileNameLength < currentPositionIdxs ) - { - assert( !"Couldn't read wavFileName, string buffer too small" ); - } - if ( !sscanf( wavLine, "%s", wavFileName ) ) - { - fprintf( stderr, "Error reading metadata\n" ); - exit( -1 ); - } - *wavFileNameLength = strlen( wavFileName ); - - mdlength = bufferlength - currentPositionIdxs; - /* "+1" for null termination */ - if ( *metadataStringLength + 1 < mdlength ) - { - assert( !"Couldn't read metadata string, string buffer too small" ); - } - - fread( metadataString, 1, mdlength, file ); - metadataString[mdlength] = '\0'; - *metadataStringLength = mdlength + 1; - - fclose( file ); -} - -/* r: pointer to character following last found delimiter */ -static char *readNextMetadataChunkFrom( char *start_char, char *line, const char *delimiter ) -{ - char *token; - - /* start_char can be NULL - it's used to continue parsing with strtok */ - assert( line != NULL && delimiter != NULL && "unexpected NULL ptr given to readNextMetadataChunkFrom()" ); - - token = strtok( start_char, delimiter ); - - /* End of string reached */ - if ( token == NULL ) - { - /* Clear `line` from previous contents and return NULL */ - line[0] = '\0'; - return NULL; - } - - strcpy( line, token ); - - return token + strlen( token ) + 1; -} - -/* r: pointer to character following last found delimiter */ -static char *readNextMetadataChunk( char *line, const char *delimiter ) -{ - return readNextMetadataChunkFrom( NULL, line, delimiter ); -} - -static void parseUint8( char *line, uint8_t *ret ) -{ - char *ptr; - ptr = NULL; - - *ret = strtol( line, &ptr, 10 ); - if ( *ptr != '\0' ) - { - fprintf( stderr, "Cannot parse string \"%s\" as an integer value\n", line ); - exit( -1 ); - } -} - -static int8_t parseUint32( char *line, uint32_t *ret ) -{ - char *ptr; - ptr = " "; - - *ret = strtol( line, &ptr, 10 ); - if ( *ptr != '\0' ) - { - return -1; - } - - return 0; -} - -static void parseOptionalInputValues( - char *line, - float *gain_dB ) -{ - char *parse_pos; - char *key; - char *value; - char *endptr; - - endptr = NULL; - - /* Set default values, in case some values are not specified */ - *gain_dB = 0.f; - - /* Save parsing position - will have to be passed to strtok to resume parsing after using strtok with non-NULL value below */ - parse_pos = readNextMetadataChunk( line, "\n" ); - - /* Look for optional metadata until end of string or next input identifier is found */ - while ( parse_pos != NULL && strcmp( line, "MC" ) != 0 && strcmp( line, "SBA" ) != 0 && strcmp( line, "ISM" ) != 0 ) - { - key = strtok( line, ":" ); - value = strtok( NULL, "\n" ); - - if ( strcmp( key, "gain_dB" ) == 0 ) - { - *gain_dB = (float) strtod( value, &endptr ); - - if ( *endptr != '\0' ) - { - fprintf( stderr, "Cannot parse string string \"%s\" as a float value\n", value ); - exit( -1 ); - } - } - else - { - fprintf( stderr, "Unsupported optional key: %s\n", key ); - exit( -1 ); - } - - parse_pos = readNextMetadataChunkFrom( parse_pos, line, "\n" ); - } -} - -static void parseObjectPosition( char *line, - Prndr_AudioObjectPosition *position, - uint16_t *positionDuration ) -{ - char *endptr; - - readNextMetadataChunk( line, "," ); - *positionDuration = strtol( line, &endptr, 10 ); - - if ( *endptr != '\0' ) - { - fprintf( stderr, "Error reading metadata\n" ); - exit( -1 ); - } - - readNextMetadataChunk( line, "," ); - position->azimuth = strtol( line, &endptr, 10 ); - - if ( *endptr != '\0' ) - { - fprintf( stderr, "Error reading metadata\n" ); - exit( -1 ); - } - - readNextMetadataChunk( line, "\n" ); - position->elevation = strtol( line, &endptr, 10 ); - if ( *endptr != '\0' ) - { - fprintf( stderr, "Error reading metadata\n" ); - exit( -1 ); - } -} - -static void parseIsm( - char *line, - char *inDir, - Prndr_InputConfig *inConfig, - Prndr_IsmPositionProvider *positionProvider, - int32_t idx ) -{ - uint32_t numberOfObjectPositionsToRead; - uint32_t i; - - readNextMetadataChunk( line, "\n" ); - parseUint8( line, &inConfig->audioObjects[idx].inputChannelIndex ); - --inConfig->audioObjects[idx].inputChannelIndex; /* Convert from 1-indexing */ - - readNextMetadataChunk( line, "\n" ); - - /* Try to interpret line as number of positions to read */ - if ( parseUint32( line, &numberOfObjectPositionsToRead ) == 0 ) - { - positionProvider->numPositions[idx] = numberOfObjectPositionsToRead; - positionProvider->positions[idx] = count_calloc( numberOfObjectPositionsToRead, sizeof( Prndr_AudioObjectPosition ) ); - positionProvider->positionDurations[idx] = count_calloc( numberOfObjectPositionsToRead, sizeof( uint16_t ) ); - - for ( i = 0; i < numberOfObjectPositionsToRead; ++i ) - { - parseObjectPosition( line, &positionProvider->positions[idx][i], &positionProvider->positionDurations[idx][i] ); - } - } - else /* If not a number, it is a relative path from main metadata file to a metadata file */ - { - char fullpath[FILENAME_MAX]; - *fullpath = '\0'; - strncat( fullpath, inDir, strlen( inDir ) ); - strncat( fullpath, line, sizeof( fullpath ) - strlen( fullpath ) - 1 ); - if ( ( positionProvider->ismReaders[idx] = IsmFileReader_open( fullpath ) ) == NULL ) - { - fprintf( stderr, "Error: ISM input metadata file %s could not be opened\n", line ); - exit( -1 ); - } - } - - /* Read optional values */ - parseOptionalInputValues( line, &inConfig->audioObjects[idx].gain_dB ); -} - -static void parseSba( char *line, - Prndr_InputConfig *inConfig, - int32_t idx ) -{ - uint8_t ambiOrder; - - readNextMetadataChunk( line, "\n" ); - parseUint8( line, &inConfig->ambisonicsBuses[idx].inputChannelIndex ); - --inConfig->ambisonicsBuses[idx].inputChannelIndex; /* Convert from 1-indexing */ - - readNextMetadataChunk( line, "\n" ); - parseUint8( line, &ambiOrder ); - inConfig->ambisonicsBuses[idx].ambisonicsConfig = ambisonicsOrderToEnum( ambiOrder ); - - /* Read optional values */ - parseOptionalInputValues( line, &inConfig->ambisonicsBuses[idx].gain_dB ); -} - -static void parseMc( char *line, - Prndr_InputConfig *inConfig, - int32_t idx ) -{ - uint8_t cicpIndex; - - readNextMetadataChunk( line, "\n" ); - parseUint8( line, &inConfig->multiChannelBuses[idx].inputChannelIndex ); - --inConfig->multiChannelBuses[idx].inputChannelIndex; /* Convert from 1-indexing */ - - readNextMetadataChunk( line, "\n" ); - parseUint8( line, &cicpIndex ); - inConfig->multiChannelBuses[idx].speakerLayout = speakerLayoutCicpToEnum( cicpIndex ); - - /* Read optional values */ - parseOptionalInputValues( line, &inConfig->multiChannelBuses[idx].gain_dB ); -} - -static void parseMetadata( - char *metadataString, - char *inDir, - Prndr_InputConfig *inConfig, - Prndr_IsmPositionProvider *positionProvider ) -{ - char line[PRERENDERER_MAX_METADATA_LINE_LENGTH]; - char *delimiter; - char *token; - uint8_t totalNumberOfAudioObjects; - uint8_t counterChannelAudioObjects; - uint8_t counterAmbisonicsAudioObjects; - uint8_t counterMonoAudioObjects; - uint8_t num_parsed_inputs; - - delimiter = "\n"; - - token = strtok( metadataString, delimiter ); - if ( token == NULL ) - { - fprintf( stderr, "Unexpected metadata format\n" ); - exit( -1 ); - } - if ( !sscanf( token, "%s", line ) ) - { - fprintf( stderr, "Unexpected metadata format\n" ); - exit( -1 ); - } - - parseUint8( line, &totalNumberOfAudioObjects ); - if ( totalNumberOfAudioObjects <= 0 ) - { - fprintf( stderr, "Invalid metadata: number of inputs should be > 0\n" ); - exit( -1 ); - } - - num_parsed_inputs = 0; - counterChannelAudioObjects = 0; - counterAmbisonicsAudioObjects = 0; - counterMonoAudioObjects = 0; - - readNextMetadataChunk( line, delimiter ); - - while ( num_parsed_inputs < totalNumberOfAudioObjects ) - { - /* `line` will already contain the identifier ("MC", "SBA" or "ISM") after previous iteration */ - if ( strcmp( line, "MC" ) == 0 ) - { - ++counterChannelAudioObjects; - if ( counterChannelAudioObjects > PRERENDERER_MAX_MC_INPUTS ) - { - fprintf( stderr, "Metadata exceeds the supported number of MC inputs\n" ); - exit( -1 ); - } - parseMc( line, inConfig, counterChannelAudioObjects - 1 ); - } - else if ( strcmp( line, "SBA" ) == 0 ) - { - ++counterAmbisonicsAudioObjects; - if ( counterAmbisonicsAudioObjects > PRERENDERER_MAX_SBA_INPUTS ) - { - fprintf( stderr, "Metadata exceeds the supported number of SBA inputs\n" ); - exit( -1 ); - } - parseSba( line, inConfig, counterAmbisonicsAudioObjects - 1 ); - } - else if ( strcmp( line, "ISM" ) == 0 ) - { - ++counterMonoAudioObjects; - if ( counterMonoAudioObjects > PRERENDERER_MAX_ISM_INPUTS ) - { - fprintf( stderr, "Metadata exceeds the supported number of ISM inputs\n" ); - exit( -1 ); - } - parseIsm( line, inDir, inConfig, positionProvider, counterMonoAudioObjects - 1 ); - } - else if ( line[0] == '\0' ) - { - fprintf( stderr, "Metadata string too short - expected %d inputs, found %d.\n", totalNumberOfAudioObjects, num_parsed_inputs ); - exit( -1 ); - } - else - { - fprintf( stderr, "Unexpected metadata identifier\n" ); - exit( -1 ); - } - - ++num_parsed_inputs; - } - - inConfig->numAudioObjects = counterMonoAudioObjects; - inConfig->numAmbisonicsBuses = counterAmbisonicsAudioObjects; - inConfig->numMultiChannelBuses = counterChannelAudioObjects; - positionProvider->numObjects = counterMonoAudioObjects; - - /* check for trailing text */ - token = strtok( NULL, delimiter ); - if ( token != NULL && sscanf( token, "%s", line ) ) - { - fprintf( stderr, "Trailing text in metadata file\n" ); - exit( -1 ); - } -} - -void parseConfigFile( char *path, char *audioFilePath, Prndr_InputConfig *inConfig, Prndr_IsmPositionProvider *positionProvider ) -{ - uint32_t inAudioFilePathLen; - char inAudioFilePath[FILENAME_MAX]; - uint32_t mtdStrLen; - char mtdStr[PRERENDERER_MAX_METADATA_LENGTH]; - char inDir[FILENAME_MAX]; - char *lastSlash = NULL; - - inAudioFilePathLen = FILENAME_MAX; - mtdStrLen = PRERENDERER_MAX_METADATA_LENGTH; - splitConfigFile( path, - mtdStr, - &mtdStrLen, - inAudioFilePath, - &inAudioFilePathLen ); - - remove_cr( mtdStr ); - convert_backslash( inAudioFilePath ); - - /* Trim config file path to get path to the dir containing it */ - lastSlash = strrchr( path, SEP_FOLDER ); - *inDir = '\0'; - if ( lastSlash != NULL ) - { - strncat( inDir, path, ( lastSlash - path + 1 ) ); - } - - /* Append audio file path (relative to config file location) - * to config file location path to get full absolute path */ - strcpy( audioFilePath, inDir ); - strncat( audioFilePath, inAudioFilePath, inAudioFilePathLen ); - - parseMetadata( mtdStr, inDir, inConfig, positionProvider ); -} - -static void convert_backslash( char *str ) -{ - int i, len; - - /* check that all backslashes are correct on the given platform */ - len = strlen( str ); - - for ( i = 0; i < len; i++ ) - { -#ifdef _WIN32 - if ( str[i] == '/' ) - { - str[i] = '\\'; - } -#else - if ( str[i] == '\\' ) - { - str[i] = '/'; - } -#endif - } - - return; -} - -static void remove_cr( char *str ) -{ - char *pos; - - /* remove all \r characters from the string */ - pos = strchr( str, '\r' ); - while ( pos != NULL ) - { - strcpy( pos, pos + 1 ); - pos = strchr( pos, '\r' ); - } - - return; -} diff --git a/scripts/prerenderer_configs/IVAS_ISM_metadata_-30_0.csv b/scripts/prerenderer_configs/IVAS_ISM_metadata_-30_0.csv deleted file mode 100644 index c3f47f72204b3c54e36dad69a765cb0e29988793..0000000000000000000000000000000000000000 --- a/scripts/prerenderer_configs/IVAS_ISM_metadata_-30_0.csv +++ /dev/null @@ -1,50 +0,0 @@ --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 --030.00,+00.00,01.00,000.00,1.00 diff --git a/scripts/prerenderer_configs/IVAS_ISM_metadata_0_0.csv b/scripts/prerenderer_configs/IVAS_ISM_metadata_0_0.csv deleted file mode 100644 index 887782fd4e5ceffdbeef724425398260b17688d8..0000000000000000000000000000000000000000 --- a/scripts/prerenderer_configs/IVAS_ISM_metadata_0_0.csv +++ /dev/null @@ -1,50 +0,0 @@ -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 -+000.00,+00.00,01.00,000.00,1.00 diff --git a/scripts/prerenderer_configs/IVAS_ISM_metadata_30_0.csv b/scripts/prerenderer_configs/IVAS_ISM_metadata_30_0.csv deleted file mode 100644 index 8ed2a4df12acb7b1ddf1d29c5ad8665aeb3b81bc..0000000000000000000000000000000000000000 --- a/scripts/prerenderer_configs/IVAS_ISM_metadata_30_0.csv +++ /dev/null @@ -1,50 +0,0 @@ -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 -+030.00,+00.00,01.00,000.00,1.00 diff --git a/scripts/prerenderer_configs/IVAS_ISM_metadata_circle.csv b/scripts/prerenderer_configs/IVAS_ISM_metadata_circle.csv deleted file mode 100644 index 9323b776fe27df31dee36f48c471cb8462369d28..0000000000000000000000000000000000000000 --- a/scripts/prerenderer_configs/IVAS_ISM_metadata_circle.csv +++ /dev/null @@ -1,50 +0,0 @@ -+000.00,+00.00,01.00,000.00,1.00 -+007.00,+00.00,01.00,000.00,1.00 -+014.00,+00.00,01.00,000.00,1.00 -+022.00,+00.00,01.00,000.00,1.00 -+029.00,+00.00,01.00,000.00,1.00 -+036.00,+00.00,01.00,000.00,1.00 -+043.00,+00.00,01.00,000.00,1.00 -+050.00,+00.00,01.00,000.00,1.00 -+058.00,+00.00,01.00,000.00,1.00 -+065.00,+00.00,01.00,000.00,1.00 -+072.00,+00.00,01.00,000.00,1.00 -+079.00,+00.00,01.00,000.00,1.00 -+086.00,+00.00,01.00,000.00,1.00 -+094.00,+00.00,01.00,000.00,1.00 -+101.00,+00.00,01.00,000.00,1.00 -+108.00,+00.00,01.00,000.00,1.00 -+115.00,+00.00,01.00,000.00,1.00 -+122.00,+00.00,01.00,000.00,1.00 -+130.00,+00.00,01.00,000.00,1.00 -+137.00,+00.00,01.00,000.00,1.00 -+144.00,+00.00,01.00,000.00,1.00 -+151.00,+00.00,01.00,000.00,1.00 -+158.00,+00.00,01.00,000.00,1.00 -+166.00,+00.00,01.00,000.00,1.00 -+173.00,+00.00,01.00,000.00,1.00 -+180.00,+00.00,01.00,000.00,1.00 -+187.00,+00.00,01.00,000.00,1.00 -+194.00,+00.00,01.00,000.00,1.00 -+202.00,+00.00,01.00,000.00,1.00 -+209.00,+00.00,01.00,000.00,1.00 -+216.00,+00.00,01.00,000.00,1.00 -+223.00,+00.00,01.00,000.00,1.00 -+230.00,+00.00,01.00,000.00,1.00 -+238.00,+00.00,01.00,000.00,1.00 -+245.00,+00.00,01.00,000.00,1.00 -+252.00,+00.00,01.00,000.00,1.00 -+259.00,+00.00,01.00,000.00,1.00 -+266.00,+00.00,01.00,000.00,1.00 -+274.00,+00.00,01.00,000.00,1.00 -+281.00,+00.00,01.00,000.00,1.00 -+288.00,+00.00,01.00,000.00,1.00 -+295.00,+00.00,01.00,000.00,1.00 -+302.00,+00.00,01.00,000.00,1.00 -+310.00,+00.00,01.00,000.00,1.00 -+317.00,+00.00,01.00,000.00,1.00 -+324.00,+00.00,01.00,000.00,1.00 -+331.00,+00.00,01.00,000.00,1.00 -+338.00,+00.00,01.00,000.00,1.00 -+346.00,+00.00,01.00,000.00,1.00 -+353.00,+00.00,01.00,000.00,1.00 diff --git a/scripts/pyaudio3dtools/EFAP.py b/scripts/pyaudio3dtools/EFAP.py index 5014cd168cc9a2a7f4e0df05d8124ad2578ff5c0..dcc615355ed32dcbc06990d090d08cffaf9a29c0 100644 --- a/scripts/pyaudio3dtools/EFAP.py +++ b/scripts/pyaudio3dtools/EFAP.py @@ -28,6 +28,7 @@ import argparse import os +from enum import Enum from itertools import combinations from typing import Optional, Tuple, Union @@ -75,6 +76,12 @@ def wrap_angles( return azi, ele +class EfapDmxType(Enum): + NONE = 0 + AMPLITUDE = 1 + INTENSITY = 2 + + class EfapVertex: """ Vertex data structure for EFAP @@ -89,9 +96,17 @@ class EfapVertex: Elevation of vertex is_ghost : bool Whether the vertex is a ghost, default is False + dmx_type : EfapDmxType + Downmix type for ghost vertices """ - def __init__(self, azi: float, ele: float, is_ghost: Optional[bool] = False): + def __init__( + self, + azi: float, + ele: float, + is_ghost: Optional[bool] = False, + dmx_type: Optional[EfapDmxType] = EfapDmxType.INTENSITY, + ): self.azi, self.ele = wrap_angles(azi, ele) self.pos = np.array( [ @@ -108,6 +123,7 @@ class EfapVertex: ) # vertices on the median plane have lowest index self.is_ghost = is_ghost + self.dmx_type = dmx_type def __str__(self): str_ = f"a{self.azi}e{self.ele}" @@ -132,13 +148,15 @@ class EFAP: Azimuth positions of the loudspeaker array elevations : npndarray Elevation postions of the loudspeaker array + intensity_panning : bool + Whether intensity panning is enabled or not Examples -------- >>> from EFAP import EFAP - >>> panner = EFAP([30, -30, 0, 110, -110], [0, 0, 0, 0, 0]]) - >>> panner.pan(15, 45, intensity_panning=False) + >>> panner = EFAP([30, -30, 0, 110, -110], [0, 0, 0, 0, 0], False) + >>> panner.pan(15, 45) array([0.66742381, 0.19069252, 0.66742381, 0.19069252, 0.19069252]) """ @@ -152,7 +170,10 @@ class EFAP: _EFAP_THRESH_TRI = 1e-10 # tolerance for a point to be inside a triangle def __init__( - self, azimuths: Union[list, np.ndarray], elevations: Union[list, np.ndarray] + self, + azimuths: Union[list, np.ndarray], + elevations: Union[list, np.ndarray], + intensity_panning: Optional[bool] = False, ): # validation azimuths = np.array(azimuths) @@ -164,6 +185,9 @@ class EFAP: if azimuths.shape != elevations.shape: raise ValueError("Mismatch between loudspeaker azimuths and elevations") + # set EFIP flag + self.intensity_panning = intensity_panning + # initialise vertices and add ghost loudspeakers if needed self.verts = np.array( [EfapVertex(azi, ele) for azi, ele in zip(azimuths, elevations)] @@ -198,11 +222,28 @@ class EFAP: """ ele = [v.ele for v in self.verts] + dmx_type = EfapDmxType.INTENSITY + # add ghost loudspeakers at the poles if necessary if max(ele) < self._EFAP_POLAR_ELE: - self.verts = np.append(self.verts, EfapVertex(0, 90, True)) + + if self.intensity_panning: + if max(ele) > self._EFAP_THRESH_MID_LAYER: + dmx_type = EfapDmxType.NONE + else: + dmx_type = EfapDmxType.AMPLITUDE + + self.verts = np.append(self.verts, EfapVertex(0, 90, True, dmx_type)) + if min(ele) > -self._EFAP_POLAR_ELE: - self.verts = np.append(self.verts, EfapVertex(0, -90, True)) + + if self.intensity_panning: + if min(ele) < -self._EFAP_THRESH_MID_LAYER: + dmx_type = EfapDmxType.NONE + else: + dmx_type = EfapDmxType.AMPLITUDE + + self.verts = np.append(self.verts, EfapVertex(0, -90, True, dmx_type)) # check for large gaps in the middle horizontal layer mid_spkrs = [ @@ -390,10 +431,21 @@ class EFAP: vec = M @ vec M2[:, i] = vec - # energy distribution for real LS and amplitude distribution for ghost LS self.dmx_mat = M2[:-n_ghost, :] + + # amplitude downmix for real loudspeakers self.dmx_mat[:, :-n_ghost] = np.sqrt(self.dmx_mat[:, :-n_ghost]) + # distribute ghosts according to downmix type + for i, v in enumerate(self.verts): + if v.is_ghost: + if v.dmx_type == EfapDmxType.NONE: + self.dmx_mat[:, i] = 0 + elif v.dmx_type == EfapDmxType.AMPLITUDE: + pass + else: + self.dmx_mat[:, i] = np.sqrt(self.dmx_mat[:, i]) + def _tri2poly(self) -> None: """ Merge hull triangles into polygons if they are coplanar @@ -719,9 +771,7 @@ class EFAP: return surface - def _compute_gains_point( - self, azimuth: float, elevation: float, intensity_panning: bool = False - ) -> np.ndarray: + def _compute_gains_point(self, azimuth: float, elevation: float) -> np.ndarray: """ Compute gains for the requested panning position @@ -732,8 +782,6 @@ class EFAP: Azimuth of requested panning position elevation : float Elevation of requested panning position - intensity_panning : bool - Flag whether to use intensity panning (Default is False == amplitude panning) Returns ------- @@ -787,7 +835,7 @@ class EFAP: gains = gains @ self.dmx_mat.T gains = gains / np.linalg.norm(gains) - if intensity_panning: + if self.intensity_panning: gains = np.sqrt(gains / np.sum(gains)) return gains @@ -818,11 +866,11 @@ class EFAP: azimuths = np.array(azimuths) elevations = np.array(elevations) if azimuths.size == 1 and elevations.size == 1: - return self._compute_gains_point(azimuths, elevations, intensity_panning) + return self._compute_gains_point(azimuths, elevations) elif np.squeeze(azimuths).ndim == 1 and np.squeeze(elevations).ndim == 1: gains = [] for a, e in zip(azimuths, elevations): - gains.append(self._compute_gains_point(a, e, intensity_panning)) + gains.append(self._compute_gains_point(a, e)) return np.vstack(gains) else: raise ValueError( @@ -846,8 +894,8 @@ def main(args): speaker_positions = np.loadtxt( os.path.abspath(args.input), delimiter=",", max_rows=2 ) - panner = EFAP(speaker_positions[0, :], speaker_positions[1, :]) - print(panner.pan(args.azimuth, args.elevation, args.efip)) + panner = EFAP(speaker_positions[0, :], speaker_positions[1, :], args.efip) + print(panner.pan(args.azimuth, args.elevation)) if __name__ == "__main__": diff --git a/scripts/pyaudio3dtools/audio3dtools.py b/scripts/pyaudio3dtools/audio3dtools.py index 99ae71351b861fc59e939ed65aff33febe5054dd..c2b1ff46d129d7a7d8d8ebb318d60aeb010f5903 100644 --- a/scripts/pyaudio3dtools/audio3dtools.py +++ b/scripts/pyaudio3dtools/audio3dtools.py @@ -84,7 +84,7 @@ def main(): "--outformat", type=str, metavar="OUTFORMAT", - help="Output format (default = %(default)s, same as input format)", + help="Output format (default = %(default)s, same as input format). Can be a custom loudspeaker layout file.", default=None, ) parser.add_argument( @@ -116,13 +116,6 @@ def main(): help="list of input metadata files (only relevant for ISM and MASA input)", default=None, ) - parser.add_argument( - "-y", - "--layoutfile", - help="File describing a custom LS layout", - type=str, - default=None, - ) parser.add_argument( "-fc", "--outfc", @@ -249,7 +242,6 @@ def main(): in_fs=args.infs, in_nchans=args.inchan, in_meta_files=args.metadata, - in_ls_layout_file=args.layoutfile, out_format=args.outformat, out_fs=args.outfs, out_fc=args.outfc, @@ -277,7 +269,6 @@ def main(): in_format=args.outformat, in_fs=args.outfs, in_meta_files=args.metadata, - in_ls_layout_file=args.layoutfile, out_format="BINAURAL", output_loudness=args.normalize, loudness_tool=args.loudness_tool, diff --git a/scripts/pyaudio3dtools/audioarray.py b/scripts/pyaudio3dtools/audioarray.py index 16569e1ec7fe0d7f27435d37794a1b7a3d537d82..dd2c5fca5f7ff61da8a8c485567eeafe77d660fc 100644 --- a/scripts/pyaudio3dtools/audioarray.py +++ b/scripts/pyaudio3dtools/audioarray.py @@ -286,7 +286,7 @@ def compare(ref: np.ndarray, test: np.ndarray, fs: int) -> dict: def getdelay(x: np.ndarray, y: np.ndarray) -> int: - """Get the delay between two audios signals + """Get the delay between two audio signals Parameters ---------- diff --git a/scripts/pyaudio3dtools/binauralrenderer.py b/scripts/pyaudio3dtools/binauralrenderer.py index ca56618a012399930627b023400d932da082c77f..d5004c06f38787fbff9a81898721ce2749b36e51 100644 --- a/scripts/pyaudio3dtools/binauralrenderer.py +++ b/scripts/pyaudio3dtools/binauralrenderer.py @@ -39,12 +39,10 @@ import numpy as np import scipy.interpolate as interp import scipy.io as sio import scipy.signal as sig +from pyaudio3dtools.rotation import rotateHOA, rotateISM, rotateMC -from pyaudio3dtools import audioarray, spatialaudioformat +from pyaudio3dtools import audioarray, spatialaudioformat, spatialaudioconvert from pyaudio3dtools.constants import * -from pyaudio3dtools.rotateHOA import rotateHOA -from pyaudio3dtools.rotateISM import rotateISM -from pyaudio3dtools.rotateMC import rotateMC main_logger = logging.getLogger("__main__") logger = main_logger.getChild(__name__) @@ -53,6 +51,10 @@ logger.setLevel(logging.DEBUG) """" Helper functions """ +def NS2SA(fs, x): + return int(int(fs / 100) * ((x) / 100) / 100000) + + def read_hrirs_from_mat( hrirs_path: str = "/HRIRs_mat/ORANGE_HRIR_53_48000_combined.mat", ) -> np.ndarray: @@ -87,17 +89,17 @@ def read_hrirs_from_mat( def get_IR( - in_format: spatialaudioformat.Format, - out_format: spatialaudioformat.Format, + in_spfmt: spatialaudioformat.Format, + out_spfmt: spatialaudioformat.Format, dataset: str, -) -> Tuple[np.ndarray, np.ndarray]: +) -> Tuple[np.ndarray, np.ndarray, float]: """get_IR Parameters ---------- - in_format: spatialaudioformat + in_spfmt: spatialaudioformat input spatial audio format - out_format: spatialaudioformat + out_spfmt: spatialaudioformat output spatial audio format dataset: str name of the HRIRs or BRIRs dataset @@ -111,7 +113,7 @@ def get_IR( """ # override for BRIRs, currently only one option - if out_format.name == "BINAURAL_ROOM": + if out_spfmt.name == "BINAURAL_ROOM": dataset = "mozart_iis" # dataset file prefix @@ -125,41 +127,43 @@ def get_IR( raise ValueError(f"Unsupported dataset '{dataset}' for HRIRs") # dataset file suffix - if in_format.name.startswith("ISM") or in_format.name.startswith("CUSTOM_LS"): + if in_spfmt.name.startswith("ISM") or in_spfmt.altname.startswith("CUSTOM_LS"): suffix = "full.mat" - elif in_format.isloudspeaker and in_format.nchannels > 1: + elif in_spfmt.isloudspeaker and in_spfmt.nchannels > 1: suffix = "combined.mat" - elif in_format.ambi_order > 0 or in_format.name.upper() == "MONO": + elif in_spfmt.ambi_order > 0 or in_spfmt.name.upper() == "MONO": suffix = "SBA3.mat" else: raise ValueError( - f"Unsupported format '{in_format.name}' for dataset '{dataset}' for HRIRs" + f"Unsupported format '{in_spfmt.name}' for dataset '{dataset}' for HRIRs" ) IR, SourcePosition = read_hrirs_from_mat("_".join([prefix, suffix])) - if in_format.name.startswith("MONO"): + latency_smp = float(np.min(np.argmax(np.sum(np.abs(IR), axis=(1)), axis=(0)))) + + if in_spfmt.name.startswith("MONO"): IR = IR[:, :, :1] # use omni/W from SBA - elif in_format.name.startswith("STEREO"): + elif in_spfmt.name.startswith("STEREO"): IR = IR[:, :, :2] # use L and R channels - elif in_format.isloudspeaker and not in_format.name.startswith("CUSTOM_LS"): + elif in_spfmt.isloudspeaker and not in_spfmt.altname.startswith("CUSTOM_LS"): # extract positions from the combined file - tmpformat = spatialaudioformat.Format("COMBINED") + tmp_spfmt = spatialaudioformat.Format("COMBINED") IR_tmp = IR.copy() - IR = np.zeros([IR_tmp.shape[0], IR_tmp.shape[1], in_format.nchannels]) + IR = np.zeros([IR_tmp.shape[0], IR_tmp.shape[1], in_spfmt.nchannels]) ir_index = 0 - for i in range(tmpformat.nchannels): - for j in range(in_format.nchannels): + for i in range(tmp_spfmt.nchannels): + for j in range(in_spfmt.nchannels): if ( - tmpformat.ls_azi[i] == in_format.ls_azi[j] - and tmpformat.ls_ele[i] == in_format.ls_ele[j] + tmp_spfmt.ls_azi[i] == in_spfmt.ls_azi[j] + and tmp_spfmt.ls_ele[i] == in_spfmt.ls_ele[j] ): - if j != in_format.lfe_index[0]: + if j != in_spfmt.lfe_index[0]: IR[:, :, ir_index] = IR_tmp[:, :, i] ir_index += 1 - return IR, SourcePosition + return IR, SourcePosition, latency_smp def FindFilter(SourcePosition: np.ndarray, azi: float, ele: float) -> int: @@ -256,7 +260,7 @@ def binaural_fftconv_framewise( SourcePosition: np.ndarray, azi: np.ndarray = None, ele: np.ndarray = None, - frame_len: int = (IVAS_FRAME_LEN_MS // 4) * 48000, + frame_len: int = (IVAS_FRAME_LEN_MS // 4) * 48, interp_method="linear", verbose=False, ) -> np.ndarray: @@ -290,7 +294,7 @@ def binaural_fftconv_framewise( """ sig_len = x.shape[0] - frame_len = (IVAS_FRAME_LEN_MS // 4) * 48000 + frame_len = (IVAS_FRAME_LEN_MS // 4) * 48 N_frames = int(sig_len / frame_len) N_HRIR_taps = IR.shape[2] @@ -306,8 +310,8 @@ def binaural_fftconv_framewise( [np.repeat(ele, N_frames // len(ele)), ele[: N_frames % len(ele)]] ) - iGs = np.zeros([N_frames], dtype=int) - mGs = np.zeros([N_frames], dtype=int) + iGs = np.zeros([N_frames + 1], dtype=int) + mGs = np.zeros([N_frames + 1], dtype=int) # store trajectory as a sequence of indices of source positions # on the HRTF database in a compressed format such that, for @@ -396,8 +400,9 @@ def binaural_fftconv_framewise( y = np.zeros([sig_len + T_rev, 2]) y0 = np.zeros([N_rev, sig_len + T_rev, 2]) - b = np.linspace(0.0, 1.0, frame_len, endpoint=False) - a = 1.0 - b + fade_in = np.arange(frame_len) / (frame_len - 1) + fade_in = fade_in[:, np.newaxis] + fade_out = 1.0 - fade_in for i_ear in [0, 1]: @@ -419,8 +424,8 @@ def binaural_fftconv_framewise( i2 = (i_frame + 1) * frame_len i2p = i1 + T_rev - a = np.linspace(0.0, 1.0, T_rev, endpoint=False) - b = 1.0 - a + fade_out = np.linspace(0.0, 1.0, T_rev, endpoint=False) + fade_in = 1.0 - fade_out for j_frame in [0, 1]: G_n_m = G[min(j_frame + i_frame, N_frames - 1), :] @@ -428,7 +433,10 @@ def binaural_fftconv_framewise( np.squeeze(x[i1:i2]), G_n_m ) - y[i1:i2p, i_ear] += a * y0[0, i1:i2p, i_ear] + b * y0[1, i1:i2p, i_ear] + y[i1:i2p, i_ear] = ( + np.squeeze(fade_out) * y0[0, i1:i2p, i_ear] + + np.squeeze(fade_in) * y0[1, i1:i2p, i_ear] + ) t1 = timeit.default_timer() fps = (i_frame + 1) / (t1 - t0) @@ -464,7 +472,10 @@ def binaural_fftconv_framewise( y0[0, j1:j2p, i_ear] += sig.oaconvolve(np.squeeze(x[j1:j2]), G0) y0[1, j1:j2p, i_ear] += sig.oaconvolve(np.squeeze(x[j1:j2]), G1) - y[i1:i2, i_ear] = a * y0[0, i1:i2, i_ear] + b * y0[1, i1:i2, i_ear] + y[i1:i2, i_ear] = ( + np.squeeze(fade_out) * y0[0, i1:i2, i_ear] + + np.squeeze(fade_in) * y0[1, i1:i2, i_ear] + ) t1 = timeit.default_timer() fps = (i_frame + 1) / (t1 - t0) @@ -489,8 +500,12 @@ def binaural_render_LFE( fs: int = 48000, lfe_index: list = [3], LFE_gain: float = 10 ** (5.5 / 20), - hrir_latency_smp: float = 0, + latency_smp: int = 0, ) -> np.ndarray: + """ + Extract LFE from the given input and render + it binaurally, accounting for delay of the + """ lfe = x[:, lfe_index].copy() @@ -498,6 +513,9 @@ def binaural_render_LFE( if lfe.shape[1] > 1: lfe = np.sum(lfe, axis=1) + # TODO tmu - disabled temporarily here, disabled in C + lfe_delay_ns = 0 + """ # 120 Hz low-pass filtering for LFE using IVAS filter coefficients if fs == 48000: lfe = sig.sosfilt(IVAS_LPF_4_BUTTER_48K_SOS, lfe, axis=0) @@ -505,11 +523,13 @@ def binaural_render_LFE( raise NotImplementedError("Only 48 kHz supported at the moment!") # 3.5ms LP filter delay from IVAS ROM - filter_delay = int(3.5 * fs / 1000) + lfe_delay_ns = 0.0035 * 1e9 + lfe_delay_smp = round(lfe_delay_ns * fs / 1e9) - # delay adjustment - lfe = np.roll(lfe, filter_delay, axis=0) - lfe[:filter_delay, :] = 0 + # Delay LFE by the same amount as the HRTF delay + lfe = np.roll(lfe, round(latency_smp), axis=0) + lfe[0 : round(latency_smp), :] = 0 + """ # apply gain lfe *= LFE_gain @@ -517,7 +537,7 @@ def binaural_render_LFE( # duplicate for each binaural channel lfe = np.hstack([lfe, lfe]) - return lfe + return lfe, lfe_delay_ns """ Format specific wrapper functions """ @@ -526,41 +546,46 @@ def binaural_render_LFE( def render_custom_ls_binaural( x: np.ndarray, fs: int, - in_format: spatialaudioformat.Format, + in_spfmt: spatialaudioformat.Format, + out_spfmt: spatialaudioformat.Format, IR: np.ndarray, SourcePosition: np.ndarray, trajectory: np.ndarray, ) -> np.ndarray: - if in_format.name != "CUSTOM_LS": - raise ValueError( - f"Unsupported format {in_format.name} for CUSTOM_LS binaural rendering!" - ) - - ls_azi_all = in_format.ls_azi - ls_ele_all = in_format.ls_ele - lfe_index_all = in_format.lfe_index - logger.info(" Processing channels on custom LS layout") - logger.info("azi: {}".format(ls_azi_all)) - logger.info("ele: {}".format(ls_ele_all)) - logger.info("lfe_index: {}".format(lfe_index_all)) - - y = audioarray.resample(x, fs, 48000) + ls_azi_all = in_spfmt.ls_azi + ls_ele_all = in_spfmt.ls_ele + lfe_index_all = in_spfmt.lfe_index + + logger.info(" Processing channels on custom LS layout") + azis = ", ".join([f"{a:7.2f}" for a in ls_azi_all]) + eles = ", ".join([f"{e:7.2f}" for e in ls_ele_all]) + logger.info(f" azi: {azis}") + logger.info(f" ele: {eles}") + logger.info(f" lfe_index: {lfe_index_all}") + + if out_spfmt.name == "BINAURAL_ROOM": + tmp_spfmt = spatialaudioformat.Format("7_1_4") + x = spatialaudioconvert.convert_mc(x, in_spfmt, tmp_spfmt) + ls_azi_all = tmp_spfmt.ls_azi + ls_ele_all = tmp_spfmt.ls_ele + lfe_index_all = tmp_spfmt.lfe_index + logger.info(f" {in_spfmt.name} -> {tmp_spfmt.name} -> {out_spfmt.name}") frame_len = (IVAS_FRAME_LEN_MS // 4) * (fs // 1000) - sig_len = y.shape[0] + sig_len = x.shape[0] N_frames = int(sig_len / frame_len) i_ls = 0 - y_all = np.zeros([sig_len, 2]) - for i_chan in range(y.shape[1]): + y = np.zeros([sig_len, 2]) + for i_chan in range(x.shape[1]): # skip LFE if i_chan in lfe_index_all: continue # skip silent (or very low volume) channels - if np.allclose(y[:, i_chan], 0.0, atol=32.0): + if np.allclose(x[:, i_chan], 0.0, atol=32.0): continue ls_azi = np.repeat(ls_azi_all[i_ls], N_frames) @@ -568,8 +593,8 @@ def render_custom_ls_binaural( azi, ele = rotateISM(ls_azi, ls_ele, trajectory=trajectory) - y_all += binaural_fftconv_framewise( - y[:, i_chan], + y += binaural_fftconv_framewise( + x[:, i_chan], IR, SourcePosition, frame_len=frame_len, @@ -579,24 +604,20 @@ def render_custom_ls_binaural( ) i_ls += 1 - y = audioarray.resample(y_all, 48000, fs) - - return y_all + return y def render_ism_binaural( x: np.ndarray, fs: int, - in_format: spatialaudioformat.Format, IR: np.ndarray, SourcePosition: np.ndarray, trajectory: np.ndarray, in_pos: np.ndarray, ) -> np.ndarray: - y = audioarray.resample(x, fs, 48000) frame_len = (IVAS_FRAME_LEN_MS // 4) * (fs // 1000) - sig_len = y.shape[0] + sig_len = x.shape[0] N_frames = int(sig_len / frame_len) # get ISM metadata and repeat it nsubframe times @@ -606,17 +627,16 @@ def render_ism_binaural( [pos["azimuth"], pos["elevation"]] for _ in range(pos["use_for_frames"]) ) pos_data = np.array(pos_data) - pos_data = np.tile(pos_data, (4, 1)) + pos_data = np.repeat(pos_data, 4, axis=0) # extract positions only according to the audio duration pos_data = pos_data[:N_frames, :] - y_all = np.zeros([sig_len, 2]) - azi, ele = rotateISM(pos_data[:, 0], pos_data[:, 1], trajectory=trajectory) - y_all += binaural_fftconv_framewise( - y, + y = np.zeros([sig_len, 2]) + y += binaural_fftconv_framewise( + x, IR, SourcePosition, frame_len=frame_len, @@ -625,9 +645,40 @@ def render_ism_binaural( verbose=False, ) - y = audioarray.resample(y_all, 48000, fs) + return y - return y_all + +def render_masa_binaural( + x: np.ndarray, + fs: int, + in_spfmt: spatialaudioformat.Format, + IR: np.ndarray, + SourcePosition: np.ndarray, + trajectory: np.ndarray, +): + y = x[:, :2] + # TODO + return y + + +def render_ambi_ls_binaural( + x: np.ndarray, + fs: int, + in_spfmt: spatialaudioformat.Format, + IR: np.ndarray, + trajectory: np.ndarray, +) -> np.ndarray: + + y = x[:] + if trajectory is not None: + if in_spfmt.ambi_order > 0: + y = rotateHOA(y, trajectory) + if in_spfmt.isloudspeaker: + y = rotateMC(y, trajectory, in_spfmt) + + y = binaural_fftconv(y, IR, in_spfmt.nchannels, in_spfmt.lfe_index) + + return y """ Wrapper function for generic binaural rendering """ @@ -635,14 +686,13 @@ def render_ism_binaural( def binaural_rendering( x: np.ndarray, - in_format: spatialaudioformat.Format, - out_format: spatialaudioformat.Format, + in_spfmt: spatialaudioformat.Format, + out_spfmt: spatialaudioformat.Format, dataset: str = "orange53", fs: int = 48000, trajectory: str = None, include_LFE: bool = False, LFE_gain: float = 10 ** (5.5 / 20), - in_ls_layout_file: str = None, in_pos: dict = None, ): """Binaural rendering @@ -651,7 +701,7 @@ def binaural_rendering( ---------- x: np array input multi-channel array - in_format_name: str + in_spfmt_name: str name of input spatial format dataset: str name of the HRIRs or BRIRs dataset @@ -672,57 +722,61 @@ def binaural_rendering( " performing rotation along trajectory from file {}".format(trajectory) ) + # resample to 48 kHz + y = audioarray.resample(x, fs, 48000) + delay_total_ns = 0 + # get IR corresponding to the input and output formats - IR, SourcePosition = get_IR(in_format, out_format, dataset) + IR, SourcePosition, latency_smp = get_IR(in_spfmt, out_spfmt, dataset) + delay_total_ns += latency_smp / float(fs) * 1e9 + + # prepare LFE signal to be added to output + if include_LFE and in_spfmt.isloudspeaker and in_spfmt.lfe_index: + lfe, lfe_delay_ns = binaural_render_LFE( + x, 48000, in_spfmt.lfe_index, LFE_gain, latency_smp + ) + delay_total_ns += lfe_delay_ns - if in_format.name.startswith("CUSTOM_LS"): - return render_custom_ls_binaural( - x, fs, in_format, IR, SourcePosition, trajectory + # get binauralized signal based on format + if in_spfmt.altname.startswith("CUSTOM_LS"): + y = render_custom_ls_binaural( + x, fs, in_spfmt, out_spfmt, IR, SourcePosition, trajectory ) - elif in_format.name.startswith("ISM"): + elif in_spfmt.name.startswith("ISM"): if not in_pos: raise ValueError("ISM metadata empty!") - return render_ism_binaural( + y = render_ism_binaural( x, fs, - in_format, IR, SourcePosition, trajectory, in_pos, ) - elif in_format.name.startswith("MASA"): - pass # TODO - # return render_masa_binaural() - elif in_format.ambi_order > 0 or in_format.isloudspeaker: - y = audioarray.resample(x, fs, 48000) - - if trajectory is not None: - if in_format.ambi_order > 0: - y = rotateHOA(y, trajectory) - if in_format.isloudspeaker: - y = rotateMC(y, trajectory, in_format) - - if include_LFE: - lfe = binaural_render_LFE( - y, 48000, in_format.lfe_index, LFE_gain, latency_smp - ) - - y = binaural_fftconv(y, IR, in_format.nchannels, in_format.lfe_index) + elif in_spfmt.name.startswith("MASA"): + y = render_masa_binaural(x, fs, in_spfmt, IR, SourcePosition, trajectory) + elif in_spfmt.ambi_order > 0 or in_spfmt.isloudspeaker: + y = render_ambi_ls_binaural(x, fs, in_spfmt, IR, trajectory) + else: + raise NotImplementedError( + f"{in_spfmt.name} -> {out_spfmt.name}: format conversion not implemented" + ) - if include_LFE: - y += lfe + # add LFE signal to output + if include_LFE and in_spfmt.isloudspeaker and in_spfmt.lfe_index: + # delay the binauralized signal by the LFE delay + lfe_delay_smp = NS2SA(fs, int(lfe_delay_ns)) + y = np.roll(y, lfe_delay_smp, axis=0) + y[0:lfe_delay_smp, :] = 0 + y += lfe - # HRTF delay compensation - latency_smp = np.argmax(np.sum(np.abs(IR), axis=(1, 2))) - y = np.roll(y, latency_smp, axis=0) - if latency_smp > 0: - y[:latency_smp, :] = 0 + # delay compensation + delay_total_smp = NS2SA(fs, delay_total_ns) + y = np.roll(y, -delay_total_smp, axis=0) + if delay_total_smp > 0: + y[-delay_total_smp:, :] = 0 - y = audioarray.resample(y, 48000, fs) + # resample back to original rate + y = audioarray.resample(y, 48000, fs) - return y - else: - raise NotImplementedError( - f"{in_format.name} -> {out_format.name}: format conversion not implemented" - ) + return y diff --git a/scripts/pyaudio3dtools/constants.py b/scripts/pyaudio3dtools/constants.py index 24c8b7aaba76b9da7ff113db7d481eb491469126..b9523fc4ae28ff2eacb9a9c15ee409652f1cd2d9 100644 --- a/scripts/pyaudio3dtools/constants.py +++ b/scripts/pyaudio3dtools/constants.py @@ -35,16 +35,16 @@ IVAS_CICPX_TO_MONO = np.array( [ 1, 1, - np.sqrt(0.5), - np.sqrt(0.5), - 0.79999995, - 0.79999995, - 0.79999995, - 0.79999995, - 0.849999964, - 0.849999964, - 0.849999964, - 0.849999964, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, ] ] ).T @@ -198,8 +198,10 @@ IVAS_MC_CONVERSION = { }, "5_1_4": { # downmix - "MONO": IVAS_CICPX_TO_MONO[:10, :], - "STEREO": IVAS_CICPX_TO_STEREO[:10, :], + "MONO": np.vstack([IVAS_CICPX_TO_MONO[:6, :], IVAS_CICPX_TO_MONO[-4:, :]]), + "STEREO": np.vstack( + [IVAS_CICPX_TO_STEREO[:6, :], IVAS_CICPX_TO_STEREO[-4:, :]] + ), "5_1": IVAS_CICP16_TO_6, "7_1": IVAS_CICP16_TO_12, "5_1_2": IVAS_CICP16_TO_14, diff --git a/scripts/pyaudio3dtools/hoadecoder.py b/scripts/pyaudio3dtools/hoadecoder.py index 331e86f52fc0fc904e6d6d8b256318bcc91d40d2..b774c0a9ecec39cde988cb0da93675c6137b9bbb 100644 --- a/scripts/pyaudio3dtools/hoadecoder.py +++ b/scripts/pyaudio3dtools/hoadecoder.py @@ -65,15 +65,21 @@ def get_hoa_mtx( mtx_hoa_dec[1, 0] = 0.5 mtx_hoa_dec[1, 1] = -0.5 elif spkrlayout.isloudspeaker: - Y_td = getRSH(T_DESIGN_11_AZI, T_DESIGN_11_ELE, ambi_order, norm="ortho") + # TODO getRSH in IVAS casts to int16_t , decide on final behaviour for scripts + Y_td = getRSH( + T_DESIGN_11_AZI.astype(np.int16), + T_DESIGN_11_ELE.astype(np.int16), + ambi_order, + norm="ortho", + ) Y_td *= np.sqrt(4 * np.pi) n_ls_woLFE = spkrlayout.nchannels - len(spkrlayout.lfe_index) ls_azi_woLFE = np.delete(spkrlayout.ls_azi, spkrlayout.lfe_index).astype(float) ls_ele_woLFE = np.delete(spkrlayout.ls_ele, spkrlayout.lfe_index).astype(float) - panner = EFAP(ls_azi_woLFE, ls_ele_woLFE) - G_td = panner.pan(T_DESIGN_11_AZI, T_DESIGN_11_ELE, intensity_panning) + panner = EFAP(ls_azi_woLFE, ls_ele_woLFE, intensity_panning) + G_td = panner.pan(T_DESIGN_11_AZI, T_DESIGN_11_ELE) mtx_hoa_dec = (G_td.T @ Y_td.T) / T_DESIGN_11_AZI.size diff --git a/scripts/pyaudio3dtools/rotateISM.py b/scripts/pyaudio3dtools/masarenderer.py similarity index 51% rename from scripts/pyaudio3dtools/rotateISM.py rename to scripts/pyaudio3dtools/masarenderer.py index bea8e65068d7a18b74de444c06b422563f6875dc..4ae7a9da3bd97cfdd10c4f62267054b14a605e68 100644 --- a/scripts/pyaudio3dtools/rotateISM.py +++ b/scripts/pyaudio3dtools/masarenderer.py @@ -30,34 +30,83 @@ the United Nations Convention on Contracts on the International Sales of Goods. """ + +import os +import shutil +import subprocess as sp +from tempfile import TemporaryDirectory + import numpy as np -from pyaudio3dtools.quaternions.functions import Quat2RotMat, rotateAziEle +from pyaudio3dtools.audiofile import readfile, writefile +from pyaudio3dtools.spatialaudioformat import Format + + +def render_masa( + in_sig: str, + in_meta: str, + in_spfmt: Format, + out_spfmt: Format, +) -> np.ndarray: + """Python wrapper for masaRenderer binaray + + Parameters + ---------- + in_sig: np.ndarray + Input signal with MASA transport channels + in_meta: str + Input MASA metadata file + in_spfmt: Format + Input spatial audio format + out_spfmt: Format + Output spatial audio format + + Returns + ------- + out_sig: np.ndarray + Rendered signal + fs : int + Sampling frequency (always 48 kHz for masaRenderer) + """ + + if shutil.which("masaRenderer") is None: + raise FileNotFoundError("The masaRenderer binary was not found in path!") + with TemporaryDirectory() as tmp_dir: + MASA_RENDERER_CMD = [ + "masaRenderer", + "", # outputMode -LS51, -LS714 or BINAURAL + "", # input PCM + in_meta[0], + "", # output PCM + ] -def rotateISM( - azi: np.ndarray, - ele: np.ndarray, - trajectory: str = None, -) -> tuple: + cmd = MASA_RENDERER_CMD[:] + if out_spfmt.name.startswith("BINAURAL"): + cmd[1] = "-BINAURAL" + out_nchan = 2 + elif out_spfmt.name == "5_1": + cmd[1] = "-LS51" + out_nchan = 6 + else: + cmd[1] = "-LS714" + out_nchan = 12 - if trajectory is None: - return azi, ele + tmp_in = os.path.join(tmp_dir, "tmp_masa_in.pcm") + tmp_out = os.path.join(tmp_dir, "tmp_masa_out.pcm") - trj_data = np.genfromtxt(trajectory, delimiter=",") - trj_frames = trj_data.shape[0] + cmd[2] = tmp_in + cmd[4] = tmp_out - N_frames = azi.shape[0] - if ele.shape[0] != azi.shape[0]: - raise ValueError("Inconsistent input in azi and ele") + writefile(tmp_in, in_sig, 48000) - azi_rot = np.zeros([N_frames]) - ele_rot = np.zeros([N_frames]) + try: + result = sp.run(cmd, check=True, capture_output=True, text=True) + except sp.CalledProcessError as e: + raise SystemError( + f"Command returned non-zero exit status ({e.returncode}): {' '.join(e.cmd)}\n{e.stderr}\n{e.stdout}" + ) - for i_frame in range(N_frames): - q = trj_data[i_frame % trj_frames, :] - azi_rot[i_frame], ele_rot[i_frame] = rotateAziEle( - azi[i_frame], ele[i_frame], Quat2RotMat(q) - ) + out_sig, _ = readfile(tmp_out, out_nchan, 48000) - return azi_rot, ele_rot + return out_sig diff --git a/scripts/pyaudio3dtools/rotateHOA.py b/scripts/pyaudio3dtools/rotation.py similarity index 74% rename from scripts/pyaudio3dtools/rotateHOA.py rename to scripts/pyaudio3dtools/rotation.py index c672491fc90bb7f992d537206e461e7937d214ae..09f90a9ccf49d6d76440df4bc585a18e6233f95d 100644 --- a/scripts/pyaudio3dtools/rotateHOA.py +++ b/scripts/pyaudio3dtools/rotation.py @@ -32,8 +32,9 @@ import numpy as np +from pyaudio3dtools import EFAP, spatialaudioformat from pyaudio3dtools.constants import * -from pyaudio3dtools.quaternions.functions import Quat2RotMat +from pyaudio3dtools.quaternions.functions import Quat2RotMat, rotateAziEle ######################################################################### # Helper functions used by Ruedenberg, @@ -219,7 +220,7 @@ def rotateHOA(x: np.ndarray, trajectory: str) -> np.ndarray: sig_len = x.shape[0] sig_dim = x.shape[1] - frame_len = (IVAS_FRAME_LEN_MS // 4) * 48000 + frame_len = (IVAS_FRAME_LEN_MS // 4) * 48 N_frames = int(sig_len / frame_len) if sig_dim not in [4, 9, 16]: @@ -227,8 +228,9 @@ def rotateHOA(x: np.ndarray, trajectory: str) -> np.ndarray: y = np.zeros([sig_len, sig_dim]) - a = np.linspace(0, 1.0, frame_len, endpoint=False)[:, np.newaxis] - b = 1.0 - a + fade_in = np.arange(frame_len) / (frame_len - 1) + fade_in = fade_in[:, np.newaxis] + fade_out = 1.0 - fade_in R = np.eye(sig_dim) R_old = np.eye(sig_dim) @@ -244,8 +246,103 @@ def rotateHOA(x: np.ndarray, trajectory: str) -> np.ndarray: frame_in = x[i1:i2, :] frame_out = y[i1:i2, :] - frame_out[:, :] = (frame_in @ R_old.T) * b + (frame_in @ R.T) * a + frame_out[:, :] = (fade_in * frame_in @ R.T) + (fade_out * frame_in @ R_old.T) R_old[:, :] = R.copy() return y + + +def rotateISM( + azi: np.ndarray, + ele: np.ndarray, + trajectory: str = None, +) -> tuple: + + if trajectory is None: + return azi, ele + + trj_data = np.genfromtxt(trajectory, delimiter=",") + trj_frames = trj_data.shape[0] + + N_frames = azi.shape[0] + if ele.shape[0] != azi.shape[0]: + raise ValueError("Inconsistent input in azi and ele") + + azi_rot = np.zeros([N_frames]) + ele_rot = np.zeros([N_frames]) + + for i_frame in range(N_frames): + q = trj_data[i_frame % trj_frames, :] + azi_rot[i_frame], ele_rot[i_frame] = rotateAziEle( + azi[i_frame], ele[i_frame], Quat2RotMat(q) + ) + + return azi_rot, ele_rot + + +def rotateMC(x: np.ndarray, trajectory: str, layout: spatialaudioformat) -> np.ndarray: + """Rotate MC signal by applying a rotation matrix calculated from the current quaternion + in each subframe + + Parameters: + ---------- + x: np.ndarray + input multichannel signal + trajectory: str + path to trajectory file + + Returns: + ---------- + y: np.ndarray + rotated multichannel signal + """ + + # TODO needs optimization, currently slow + trj_data = np.genfromtxt(trajectory, delimiter=",") + trj_frames = trj_data.shape[0] + + sig_len = x.shape[0] + sig_dim = x.shape[1] + frame_len = (IVAS_FRAME_LEN_MS // 4) * 48 + N_frames = int(sig_len / frame_len) + + y = np.zeros([sig_len, sig_dim]) + + # TODO LFE handling here + panner = EFAP.EFAP(layout.ls_azi, layout.ls_ele) + + fade_in = np.arange(frame_len) / (frame_len - 1) + fade_in = fade_in[:, np.newaxis] + fade_out = 1.0 - fade_in + + R = np.eye(layout.nchannels) + R_old = np.eye(layout.nchannels) + + for i_frame in range(N_frames): + + start = i_frame * frame_len + end = (i_frame + 1) * frame_len + + q = trj_data[i_frame % trj_frames, :] + + rotated_pos = np.array( + [ + rotateAziEle(a, e, Quat2RotMat(q)) + for a, e in zip(layout.ls_azi, layout.ls_ele) + ] + ).astype( + np.int16 + ) # TODO tmu for alignment with IVAS + R = panner.pan(rotated_pos[:, 0], rotated_pos[:, 1]) + R[:, layout.lfe_index] = np.zeros([layout.nchannels, 1]) + R[layout.lfe_index, layout.lfe_index] = 1 + + frame_in = x[start:end, :] + frame_out = y[start:end, :] + + frame_out[:, :] = (fade_in * frame_in @ R) + (fade_out * frame_in @ R_old) + + R_old = R.copy() + + return y diff --git a/scripts/pyaudio3dtools/spatialaudioconvert.py b/scripts/pyaudio3dtools/spatialaudioconvert.py index 3d71dc0e6fb361da57b4b7ff7b683e91dfdc6559..2dc1fc073772796a43912059864d4c95cd136006 100644 --- a/scripts/pyaudio3dtools/spatialaudioconvert.py +++ b/scripts/pyaudio3dtools/spatialaudioconvert.py @@ -32,6 +32,7 @@ import logging import os +import warnings from typing import Optional, Tuple import numpy as np @@ -42,6 +43,7 @@ from pyaudio3dtools import ( audiofile, binauralrenderer, hoadecoder, + masarenderer, spatialaudioformat, spatialmetadata, ) @@ -59,7 +61,6 @@ def spatial_audio_convert( in_fs: Optional[int] = None, in_nchans: Optional[int] = None, in_meta_files: Optional[list] = None, - in_ls_layout_file: Optional[str] = None, out_format: Optional[str] = None, out_fs: Optional[int] = None, out_fc: Optional[int] = None, @@ -68,7 +69,7 @@ def spatial_audio_convert( limit_output: Optional[bool] = False, cut_preamble_s: Optional[int] = None, trajectory: Optional[str] = None, - bin_rend_include_LFE: Optional[bool] = False, + bin_rend_include_LFE: Optional[bool] = True, bin_rend_LFE_gain: Optional[float] = 10 ** (5.5 / 20), binaural_dataset: Optional[str] = "orange53", ) -> Tuple[np.ndarray, int]: @@ -88,8 +89,6 @@ def spatial_audio_convert( input sampling frequency in_nchans: Optional[int] input number of channels (deduced for .wav) - in_ls_layout_file: Optional[str] - input loudspeaker layout file out_format: Optional[str] output spatial audio format @@ -122,99 +121,98 @@ def spatial_audio_convert( Returns ------- - out_spfmt.name: str - output spatial audio format name - out_fs: int + out_sig : np.ndarray + output signal + out_fs : int output sampling frequency """ + + """ get spatial input and audio format configurations """ + if in_format is None: + if in_nchans is not None: + in_format = spatialaudioformat.Format.detect_format(in_nchans) + in_spfmt = spatialaudioformat.Format(in_format) + logger.info(f" Input spatial audio format detected: {in_format}") + else: + logger.info(f" Input spatial audio format: {in_format}") + in_spfmt = spatialaudioformat.Format(in_format) + + if out_format is None: + out_format = in_format + logger.info( + f" Output spatial audio format not specified, defaulting to pass-through: {out_format}" + ) + out_spfmt = spatialaudioformat.Format(out_format) + + """ read input file """ # Input is either waveform file (.pcm or .wav) or iis metadata (.txt) _, input_ext = os.path.splitext(os.path.basename(in_file)) - """ read input file """ if input_ext == ".pcm": if in_fs is None: - if out_fs is not None: + if out_fs: in_fs = out_fs else: - raise Exception("Input and output fs not defined.") + raise ValueError("Input and output fs not defined.") if in_nchans is None: - if in_format is not None: - in_spfmt = spatialaudioformat.Format( - in_format=in_format, ls_layout_file=in_ls_layout_file - ) + if in_spfmt is not None: in_nchans = in_spfmt.nchannels - elif out_format is not None: - out_spfmt = spatialaudioformat.Format(in_format=out_format) - in_nchans = out_spfmt.nchannels else: - raise Exception( - "Number if input channels not defined and can't be deduced." + raise ValueError( + "Number of input channels not defined and can't be deduced." ) in_sig, in_fs = audiofile.readfile(in_file, fs=in_fs, nchannels=in_nchans) elif input_ext == ".wav": in_sig, in_fs = audiofile.readfile(in_file) - if in_format is not None: - in_spfmt = spatialaudioformat.Format( - in_format=in_format, ls_layout_file=in_ls_layout_file - ) - # Adjust number of channels if case of HOA, zeroed vert channels if planar - if in_spfmt.ambi_order > 0: - in_sig = audioarray.convert(in_sig, out_nchans=in_spfmt.nchannels) + if in_format is None: + in_format = spatialaudioformat.Format.detect_format(in_sig.shape[1]) + in_spfmt = spatialaudioformat.Format(in_format) + + # Adjust number of channels if case of HOA, zeroed vert channels if planar + if in_spfmt.ambi_order > 0: + in_sig = audioarray.convert(in_sig, out_nchans=in_spfmt.nchannels) elif input_ext == ".txt": metadata_obj = spatialmetadata.Metadata(in_file, audio_fs=in_fs) in_sig, in_fs = metadata_obj.get_audio_array() - if in_format != "META": + if in_spfmt.name != "META": logger.info( - f" {in_format} specified with .txt input file: overriding to META format" + f" {in_spfmt.name} specified with .txt input file: overriding to META format" ) in_format = "META" + in_spfmt = spatialaudioformat.Format(in_format) else: raise Exception(f"Not supported file {input_ext}") _, in_nchans = in_sig.shape - """ attempt to detect input format if not specified """ - if in_format is None: - in_format = spatialaudioformat.Format.detect_format(in_nchans) - logger.info(f" Input spatial audio format detected: {in_format}") - else: - logger.info(f" Input spatial audio format: {in_format}") - - """ convert metadata based formats directly to output format """ - if in_format.startswith("META") or in_format.startswith("ISM"): - if out_format is None: + """ convert metadata based formats (ISM / META) directly to output format """ + if in_spfmt.name.startswith("META") or in_spfmt.name.startswith("ISM"): + if out_spfmt.name.startswith("META"): raise Exception("out format must be specified for META (.txt) or ISM input") - if in_format.startswith("ISM"): + if in_spfmt.name.startswith("ISM"): if in_meta_files is None: raise ValueError( - f"Please specify a list of metadata files for {in_format}" + f"Please specify a list of metadata files for {in_spfmt.name}" ) - if len(in_meta_files) != int(in_format[-1]): + if len(in_meta_files) != int(in_spfmt.name[-1]): raise ValueError( - f"Mismatch between number of streams and number of specified metadata files for {in_format}" + f"Mismatch between number of streams and number of specified metadata files for {in_spfmt.name}" ) # initialise metadata object for ISM metadata_obj = spatialmetadata.Metadata() metadata_obj.init_for_ism(in_file, in_fs, in_meta_files) - # TODO alternative paths for binaural rendering for now - if out_format.startswith("BINAURAL_ROOM"): + # TODO decide on reference path for BINAURAL_ROOM + if out_spfmt.name.startswith("BINAURAL_ROOM"): in_format = "7_1_4" - elif out_format.startswith("BINAURAL"): - in_format = "HOA3" else: in_format = out_format - in_spfmt = spatialaudioformat.Format(in_format) - out_spfmt = spatialaudioformat.Format(out_format) else: # set input format to output format # render_meta() handles all conversions - in_spfmt = spatialaudioformat.Format(in_format) - out_spfmt = spatialaudioformat.Format(out_format) - in_format = out_format in_spfmt = out_spfmt @@ -224,12 +222,9 @@ def spatial_audio_convert( dataset=binaural_dataset, fs=in_fs, trajectory=trajectory, - in_ls_layout_file=in_ls_layout_file, include_LFE=bin_rend_include_LFE, LFE_gain=bin_rend_LFE_gain, ) - elif in_format.startswith("MASA"): - raise NotImplementedError(f"Rendering of MASA is not yet supported!") """ cut preamble """ if cut_preamble_s is not None: @@ -238,27 +233,21 @@ def spatial_audio_convert( logger.info(f" Cut preample by {samples_to_cut} samples") in_sig = audioarray.cut(in_sig, (samples_to_cut, -1)) - """ get spatial input and audio format configurations """ - in_spfmt = spatialaudioformat.Format( - in_format=in_format, ls_layout_file=in_ls_layout_file - ) - if out_format is None: - out_format = in_format - out_spfmt = spatialaudioformat.Format(in_format=out_format) - """ zero non-planar input ambisonics channels """ if in_spfmt.ambi_order > 0 and in_spfmt.isplanar: in_sig = spatialaudioformat.Format.zero_vert_hoa_channels(in_sig) """ Spatial audio format conversion """ out_sig = in_sig - if (out_format != in_format) and not ( + if (in_spfmt.name != out_spfmt.name) and not ( in_spfmt.isheadphones and out_spfmt.isheadphones ): logger.info(f" {in_spfmt.name} -> {out_spfmt.name}") - # binaural output - if out_spfmt.name.startswith("BINAURAL"): + # binaural output (except MASA) + if out_spfmt.name.startswith("BINAURAL") and not in_spfmt.name.startswith( + "MASA" + ): out_sig = binauralrenderer.binaural_rendering( in_sig, in_spfmt, @@ -266,7 +255,6 @@ def spatial_audio_convert( dataset=binaural_dataset, fs=in_fs, trajectory=trajectory, - in_ls_layout_file=in_ls_layout_file, include_LFE=bin_rend_include_LFE, LFE_gain=bin_rend_LFE_gain, ) @@ -279,14 +267,9 @@ def spatial_audio_convert( elif in_spfmt.isloudspeaker: out_sig = convert_mc(in_sig, in_spfmt, out_spfmt) - # ISM conversion - elif in_spfmt.name.startswith("ISM"): - out_sig = convert_ism(in_sig, in_fs, in_spfmt, out_spfmt) - # MASA conversion elif in_spfmt.name.startswith("MASA"): - raise AssertionError(f"Shouldn't execute!") # TODO remove - out_sig = convert_masa(in_sig, in_spfmt, out_spfmt) + out_sig = convert_masa(in_sig, in_fs, in_meta_files, in_spfmt, out_spfmt) else: raise NotImplementedError( f"{in_spfmt.name} -> {out_spfmt.name}: format conversion not implemented" @@ -358,22 +341,39 @@ def convert_mc( try: MC2LS = IVAS_MC_CONVERSION[in_spfmt.name][out_spfmt.name] except KeyError: - panner = EFAP.EFAP(in_spfmt.ls_azi, in_spfmt.ls_ele) - MC2LS = np.vstack( - [panner.pan(a, e) for a, e in zip(out_spfmt.ls_azi, out_spfmt.ls_ele)] - ).T + ls_azi_woLFE = np.delete(out_spfmt.ls_azi, out_spfmt.lfe_index).astype( + float + ) + ls_ele_woLFE = np.delete(out_spfmt.ls_ele, out_spfmt.lfe_index).astype( + float + ) + + panner = EFAP.EFAP(ls_azi_woLFE, ls_ele_woLFE) + MC2LS = np.vstack( + [ + panner.pan(a, e).T + for i, (a, e) in enumerate(zip(in_spfmt.ls_azi, in_spfmt.ls_ele)) + if i not in in_spfmt.lfe_index + ] + ) + # TODO tmu : implement configurable LFE handling # pass-through for LFE - MC2LS[in_spfmt.lfe_index, :] = 0 + MC2LS = np.insert(MC2LS, in_spfmt.lfe_index, 0, axis=0) + MC2LS = np.insert(MC2LS, out_spfmt.lfe_index, 0, axis=1) MC2LS[in_spfmt.lfe_index, out_spfmt.lfe_index] = 1 + # TODO tmu temporarily disable LFE rendering to MONO/STEREO + if out_spfmt.name == "MONO" or out_spfmt.name == "STEREO": + MC2LS[in_spfmt.lfe_index, :] = 0 return in_sig @ MC2LS # MC -> HOA elif out_spfmt.ambi_order > 0: # SH response for loudspeaker positions MC2HOA = np.hstack( [ - hoadecoder.getRSH([a], [e], out_spfmt.ambi_order) + # TODO getRSH in IVAS casts to int16_t , decide on final behaviour for scripts + hoadecoder.getRSH([int(a)], [int(e)], out_spfmt.ambi_order) for a, e in zip(in_spfmt.ls_azi, in_spfmt.ls_ele) ] ).T @@ -409,7 +409,8 @@ def convert_ism( out_sig = np.zeros([sig_len, out_spfmt.nchannels]) - fade_in = np.linspace(0, 1.0, frame_len, endpoint=False)[:, np.newaxis] + fade_in = np.arange(frame_len) / (frame_len - 1) + fade_in = fade_in[:, np.newaxis] fade_out = 1.0 - fade_in if out_spfmt.isloudspeaker: @@ -434,7 +435,10 @@ def convert_ism( gains = gains[:, np.newaxis] # ISM -> HOA elif out_spfmt.ambi_order > 0: - gains = hoadecoder.getRSH([pos[0]], [pos[1]], out_spfmt.ambi_order) + # TODO getRSH in IVAS casts to int16_t , decide on final behaviour for scripts + gains = hoadecoder.getRSH( + [int(pos[0])], [int(pos[1])], out_spfmt.ambi_order + ) else: raise NotImplementedError( f"{in_spfmt.name} -> {out_spfmt.name}: format conversion not implemented" @@ -443,9 +447,9 @@ def convert_ism( if gains_old is None: gains_old = gains.copy() - out_frame[:] = (in_frame @ gains.T) * fade_in + ( - in_frame @ gains_old.T - ) * fade_out + out_frame[:] = (fade_in * in_frame @ gains.T) + ( + fade_out * in_frame @ gains_old.T + ) gains_old = gains.copy() @@ -454,28 +458,48 @@ def convert_ism( def convert_masa( in_sig: np.ndarray, + in_fs: int, + in_meta: str, in_spfmt: spatialaudioformat.Format, out_spfmt: spatialaudioformat.Format, ) -> np.ndarray: """Convert a MASA signal to the requested output format""" + + if in_fs != 48000: + raise ValueError(f"{in_spfmt.name} rendering only support for 48kHz!") + + tmp_spfmt = out_spfmt + # MASA -> LS if out_spfmt.isloudspeaker: - # TODO - raise NotImplementedError( - f"{in_spfmt.name} -> {out_spfmt.name}: format conversion not implemented" - ) + if not (out_spfmt.name == "5_1" or out_spfmt.name == "7_1_4"): + tmp_spfmt = spatialaudioformat.Format("7_1_4") + warnings.warn( + f"{out_spfmt.name} not natively supported by masaRenderer, using {tmp_spfmt.name} as intermediate format" + ) # MASA -> HOA elif out_spfmt.ambi_order > 0: - # TODO - raise NotImplementedError( - f"{in_spfmt.name} -> {out_spfmt.name}: format conversion not implemented" + tmp_spfmt = spatialaudioformat.Format("7_1_4") + warnings.warn( + f"{out_spfmt.name} not natively supported by masaRenderer, using {tmp_spfmt.name} as intermediate format" + ) + elif out_spfmt.name == "BINAURAL": + warnings.warn( + f"Using masaRenderer for rendering; any binaural_dataset setting will be ignored!" ) else: raise NotImplementedError( f"{in_spfmt.name} -> {out_spfmt.name}: format conversion not implemented" ) - return out_sig + out_sig = masarenderer.render_masa(in_sig, in_meta, in_spfmt, tmp_spfmt) + + # conversion done + if tmp_spfmt.name == out_spfmt.name: + return out_sig + # only rendered an intermediate format, more conversion needed + else: + return convert_mc(out_sig, tmp_spfmt, out_spfmt) def render_meta( @@ -484,7 +508,6 @@ def render_meta( dataset: str, fs: int, trajectory: str, - in_ls_layout_file: str, include_LFE: bool = False, LFE_gain: float = 10 ** (5.5 / 20), ) -> np.ndarray: @@ -499,6 +522,9 @@ def render_meta( start = object["track_index"] stop = start + object["nb_tracks"] obj_sig = metadata_obj.audio_array[:, start:stop] + # apply gain + if hasattr(object, "gain"): + obj_sig *= object["gain"] if dest_fmt.name.startswith("BINAURAL"): if object["input_type"] == "ism": @@ -520,7 +546,6 @@ def render_meta( trajectory=trajectory, include_LFE=include_LFE, LFE_gain=LFE_gain, - in_ls_layout_file=in_ls_layout_file, in_pos=positions, ) else: @@ -530,10 +555,10 @@ def render_meta( obj_sig, fs, object["positions"], src_format, dest_fmt ) elif object["input_type"] == "sba": - src_format = spatialaudioformat.Format(f"SBA{object['order']}") + src_format = object["format"] out_sig += convert_sba(obj_sig, src_format, dest_fmt) elif object["input_type"] == "mc": - src_format = spatialaudioformat.Format(f"CICP{object['cicp_index']}") + src_format = object["format"] out_sig += convert_mc(obj_sig, src_format, dest_fmt) return out_sig diff --git a/scripts/pyaudio3dtools/spatialaudioformat.py b/scripts/pyaudio3dtools/spatialaudioformat.py index 83eb4b6802d249ec889e1b8e0143069cb3b980b8..40f08800c4ffb5c81da00eedd1030d276b7d9f26 100644 --- a/scripts/pyaudio3dtools/spatialaudioformat.py +++ b/scripts/pyaudio3dtools/spatialaudioformat.py @@ -30,6 +30,8 @@ the United Nations Convention on Contracts on the International Sales of Goods. """ +import os + import numpy as np _format_configs = { @@ -197,7 +199,7 @@ _format_configs = { "ls_azi": None, "ls_ele": None, "lfe_index": None, - "altname": "custom_ls", + "altname": "CUSTOM_LS", "config_file": "layout.txt", }, # ambisonics @@ -363,28 +365,18 @@ _vert_hoa_channels = np.array([2, 5, 6, 7, 10, 11, 12, 13, 14]) class Format: - def __init__(self, in_format: str = "FOA", ls_layout_file: str = None): + def __init__(self, in_format: str = "FOA"): self.name = None + self.altname = None self.ambi_order = -1 self.nchannels = None self.isloudspeaker = False + self.isheadphones = False self.lfe_index = [] - for config_name, config_dict in _format_configs.items(): - if ( - in_format.upper() == config_name - or in_format.upper() == config_dict["altname"].upper() - ): - for k, v in _format_configs[config_name].items(): - setattr(self, k, v) - - if not self.name: - raise SystemExit( - "Spatial audio format not supported. If 'EXT' is used, please change to ISM or MASA. Ensure it is same as 'in_format'" - ) - - if self.name == "CUSTOM_LS" and ls_layout_file is not None: - with open(ls_layout_file, "r") as f_ls: + # if it is a path, then treat as custom layout + if not isinstance(in_format, str) or in_format[-4:].lower() == ".txt": + with open(in_format, "r") as f_ls: self.ls_azi = [ float(x.strip()) for x in f_ls.readline().strip().split(",") ] @@ -402,6 +394,27 @@ class Format: [self.ls_azi.insert(i, 0.0) for i in self.lfe_index] [self.ls_ele.insert(i, 0.0) for i in self.lfe_index] + self.name = os.path.basename(in_format).replace(".txt", "") + self.altname = "CUSTOM_LS" + self.config_file = str(in_format) + self.isloudspeaker = True + self.nchannels = len(self.ls_azi) + self.isplanar = np.all([e == 0.0 for e in self.ls_ele]) + # search in predefined dictionary + else: + for config_name, config_dict in _format_configs.items(): + if ( + in_format.upper() == config_name + or in_format.upper() == config_dict["altname"].upper() + ): + for k, v in _format_configs[config_name].items(): + setattr(self, k, v) + + if not self.name: + raise SystemExit( + f"Spatial audio format '{in_format}' not supported. If 'EXT' is used, please change to ISM or MASA. Ensure it is same as 'in_format'" + ) + def get_nchannels(self): return self.nchannels @@ -439,10 +452,9 @@ class Format: def detect_format(nchannels: int) -> str: config_name = None - for config_name in _format_configs: - dictionary = _format_configs[config_name] - if dictionary["nchannels"] == nchannels: - config_name = dictionary["name"] + for k, v in _format_configs.items(): + if v["nchannels"] == nchannels: + config_name = v["name"] break if config_name is None: diff --git a/scripts/pyaudio3dtools/spatialmetadata.py b/scripts/pyaudio3dtools/spatialmetadata.py index f081278fa008fd570e1b1443faa350658bdc5905..d1933d0f5781a0927c601f7e0716e1ce200ba21e 100644 --- a/scripts/pyaudio3dtools/spatialmetadata.py +++ b/scripts/pyaudio3dtools/spatialmetadata.py @@ -294,6 +294,7 @@ def read_ism_input(file_handle: TextIO, dirname: str) -> dict: ism["track_index"] = int(file_handle.readline()) - 1 ism["nb_tracks"] = 1 ism["positions"] = [] + ism["gain"] = 1 line = file_handle.readline() try: @@ -307,6 +308,7 @@ def read_ism_input(file_handle: TextIO, dirname: str) -> dict: pos["azimuth"] = int(azimuth) pos["elevation"] = int(elevation) ism["positions"].append(pos) + ism["gain"] = read_gain_value(file_handle) except: meta_csv = os.path.join(dirname, line.strip()) pos_idx = 0 @@ -380,8 +382,10 @@ def write_ism_input( def read_sba_input(file_handle: TextIO) -> dict: sba = {"input_type": "sba"} sba["track_index"] = int(file_handle.readline()) - 1 - sba["order"] = int(file_handle.readline()) + sba["format"] = spatialaudioformat.Format(f"SBA{int(file_handle.readline())}") + sba["order"] = sba["format"].ambi_order sba["nb_tracks"] = (sba["order"] + 1) ** 2 + sba["gain"] = read_gain_value(file_handle) return sba @@ -396,10 +400,9 @@ def write_sba_input(file_handle: TextIO, sba_dict: dict) -> None: def read_mc_input(file_handle: TextIO) -> dict: mc = {"input_type": "mc"} mc["track_index"] = int(file_handle.readline()) - 1 - mc["cicp_index"] = int( - file_handle.readline() - ) # TODO try to support custom LS files? - mc["nb_tracks"] = spatialaudioformat.Format(f"CICP{mc['cicp_index']}").nchannels + mc["format"] = spatialaudioformat.Format(file_handle.readline().strip()) + mc["nb_tracks"] = mc["format"].nchannels + mc["gain"] = read_gain_value(file_handle) return mc @@ -407,8 +410,19 @@ def write_mc_input(file_handle: TextIO, mc_dict: dict) -> None: file_handle.write("MC\n") track_index = mc_dict["track_index"] file_handle.write(f"{str(track_index + 1)}\n") - order = mc_dict["order"] - file_handle.write(f"{str(order)}\n") + name = mc_dict["format"].name + file_handle.write(f"{name}\n") + + +def read_gain_value(file_handle: TextIO) -> float: + original_pos = file_handle.tell() + gain = file_handle.readline().lower() + if gain.startswith("gain_db"): + gain = float(gain.replace("gain_db", "")) + return 10 ** (gain / 20) + else: + file_handle.seek(original_pos) + return 1 ################################################## diff --git a/scripts/pyprocessing/prepost_processing.py b/scripts/pyprocessing/prepost_processing.py index dee95b77a64b5bd33407adc20e32312cbe2fd464..a2c014b6a9adb731b5f207440e38cc28763cdf82 100644 --- a/scripts/pyprocessing/prepost_processing.py +++ b/scripts/pyprocessing/prepost_processing.py @@ -161,7 +161,8 @@ class PostProcessing(Processing): out_sig, fs = audiofile.readfile(output_path) bin_sig = binauralrenderer.binaural_rendering( out_sig, - self.out_spfmt.name, + self.out_spfmt, + spatialaudioformat.Format("BINAURAL"), fs=fs, include_LFE=self.bin_rend_include_LFE, LFE_gain=self.bin_rend_LFE_gain, diff --git a/scripts/td_object_renderer/object_renderer_standalone/Makefile b/scripts/td_object_renderer/object_renderer_standalone/Makefile index 1f7571b867e318bdaad6dff6aaff15ae2deb69b6..c8a43fc6f13cbe11cfc278f35a4eca67a2a562e8 100644 --- a/scripts/td_object_renderer/object_renderer_standalone/Makefile +++ b/scripts/td_object_renderer/object_renderer_standalone/Makefile @@ -5,12 +5,13 @@ SRC_LIBCOM = ../../../lib_com SRC_LIBDEBUG = ../../../lib_debug SRC_LIBDEC = ../../../lib_dec SRC_LIBENC = ../../../lib_enc +SRC_LIBREND = ../../../lib_rend SRC_LIBUTIL = ../../../lib_util SRC_APP = ./object_renderer_standalone BUILD = build OBJDIR = obj -SRC_DIRS = $(sort -u $(SRC_LIBCOM) $(SRC_LIBDEBUG) $(SRC_LIBDEC) $(SRC_LIBENC) $(SRC_LIBUTIL) $(SRC_APP)) +SRC_DIRS = $(sort -u $(SRC_LIBCOM) $(SRC_LIBDEBUG) $(SRC_LIBDEC) $(SRC_LIBENC) $(SRC_LIBREND) $(SRC_LIBUTIL) $(SRC_APP)) # Name of CLI binaries CLI_REN ?= renderer_standalone @@ -18,6 +19,7 @@ LIB_LIBCOM ?= libivascom.a LIB_LIBDEBUG ?= libivasdebug.a LIB_LIBDEC ?= libivasdec.a LIB_LIBENC ?= libivasenc.a +LIB_LIBREND ?= libivasrend.a LIB_LIBUTIL ?= libivasutil.a # Default tool settings @@ -117,12 +119,14 @@ SRCS_LIBCOM = $(foreach DIR,$(SRC_LIBCOM),$(patsubst $(DIR)/%,%,$(wildcard $(D SRCS_LIBDEBUG = $(foreach DIR,$(SRC_LIBDEBUG),$(patsubst $(DIR)/%,%,$(wildcard $(DIR)/*.c))) SRCS_LIBDEC = $(foreach DIR,$(SRC_LIBDEC),$(patsubst $(DIR)/%,%,$(wildcard $(DIR)/*.c))) SRCS_LIBENC = $(foreach DIR,$(SRC_LIBENC),$(patsubst $(DIR)/%,%,$(wildcard $(DIR)/*.c))) +SRCS_LIBREND = $(foreach DIR,$(SRC_LIBREND),$(patsubst $(DIR)/%,%,$(wildcard $(DIR)/*.c))) SRCS_LIBUTIL = $(foreach DIR,$(SRC_LIBUTIL),$(patsubst $(DIR)/%,%,$(wildcard $(DIR)/*.c))) OBJS_LIBCOM = $(addprefix $(OBJDIR)/,$(SRCS_LIBCOM:.c=.o)) OBJS_LIBDEBUG = $(addprefix $(OBJDIR)/,$(SRCS_LIBDEBUG:.c=.o)) OBJS_LIBDEC = $(addprefix $(OBJDIR)/,$(SRCS_LIBDEC:.c=.o)) OBJS_LIBENC = $(addprefix $(OBJDIR)/,$(SRCS_LIBENC:.c=.o)) +OBJS_LIBREND = $(addprefix $(OBJDIR)/,$(SRCS_LIBREND:.c=.o)) OBJS_LIBUTIL = $(addprefix $(OBJDIR)/,$(SRCS_LIBUTIL:.c=.o)) OBJS_REN = $(OBJDIR)/renderer_standalone.o @@ -145,19 +149,22 @@ $(LIB_LIBCOM): $(OBJS_LIBCOM) $(LIB_LIBDEBUG): $(OBJS_LIBDEBUG) $(QUIET_AR)$(AR) rcs $@ $^ -$(LIB_LIBDEC): $(OBJS_LIBDEC) +$(LIB_LIBDEC): $(OBJS_LIBDEC) $(OBJS_LIBREND) $(QUIET_AR)$(AR) rcs $@ $^ $(LIB_LIBENC): $(OBJS_LIBENC) $(QUIET_AR)$(AR) rcs $@ $^ +$(LIB_LIBREND): $(OBJS_LIBREND) + $(QUIET_AR)$(AR) rcs $@ $^ + $(LIB_LIBUTIL): $(OBJS_LIBUTIL) $(QUIET_AR)$(AR) rcs $@ $^ $(CLI_REN): $(LIB_LIBENC) $(LIB_LIBDEC) $(LIB_LIBDEBUG) $(LIB_LIBCOM) $(LIB_LIBUTIL) $(OBJS_REN) $(QUIET_LINK)$(CC) $(LDFLAGS) $(OBJS_REN) -L. -livasdebug -livasutil -livasenc -livasdec -livascom $(LDLIBS) -o $(CLI_REN) -libs: $(LIB_LIBENC) $(LIB_LIBDEBUG) $(LIB_LIBCOM) $(LIB_LIBDEC) $(LIB_LIBUTIL) +libs: $(LIB_LIBENC) $(LIB_LIBDEBUG) $(LIB_LIBCOM) $(LIB_LIBDEC) $(LIB_LIBREND) $(LIB_LIBUTIL) clean: $(QUIET)$(RM) $(OBJS_LIBENC) $(OBJS_LIBDEC) $(OBJS_REN) $(DEPS) @@ -165,7 +172,7 @@ clean: $(QUIET)test ! -d $(OBJDIR) || rm -rf $(OBJDIR) clean_all: clean - $(QUIET)$(RM) $(CLI_REN) $(LIB_LIBENC) $(LIB_LIBDEBUG) $(LIB_LIBCOM) $(LIB_LIBDEC) $(LIB_LIBUTIL) + $(QUIET)$(RM) $(CLI_REN) $(LIB_LIBENC) $(LIB_LIBDEBUG) $(LIB_LIBCOM) $(LIB_LIBDEC) $(LIB_LIBUTIL) $(LIB_LIBREND) $(OBJDIR)/%.o : %.c | $(OBJDIR) $(QUIET_CC)$(CC) $(CFLAGS) -c -MD -o $@ $< diff --git a/scripts/td_object_renderer/object_renderer_standalone/object_renderer_standalone.sln b/scripts/td_object_renderer/object_renderer_standalone/object_renderer_standalone.sln index 18c18b0acebc09eb09ac3df1f41ec8ad77da88e4..72b93504062711e923ce58c9cdf47f3eac127050 100644 --- a/scripts/td_object_renderer/object_renderer_standalone/object_renderer_standalone.sln +++ b/scripts/td_object_renderer/object_renderer_standalone/object_renderer_standalone.sln @@ -3,16 +3,18 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.28307.136 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "object_renderer_standalone", "object_renderer_standalone.vcxproj", "{75AE3898-3FDF-4AE2-86A1-838D0E78545E}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib_util", "..\..\..\Workspace_msvc\lib_util.vcxproj", "{2FA8F384-0775-F3B7-F8C3-85209222FC70}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib_dec", "..\..\..\Workspace_msvc\lib_dec.vcxproj", "{E822DDAF-0F5F-4CD0-A694-38AE69DE74D3}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib_com", "..\..\..\Workspace_msvc\lib_com.vcxproj", "{39EC200D-7795-4FF8-B214-B24EDA5526AE}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib_rend", "..\..\..\Workspace_msvc\lib_rend.vcxproj", "{718DE063-A18B-BB72-9150-62B892E6FFA6}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib_util", "..\..\..\Workspace_msvc\lib_util.vcxproj", "{2FA8F384-0775-F3B7-F8C3-85209222FC70}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib_debug", "..\..\..\Workspace_msvc\lib_debug.vcxproj", "{54509728-928B-44D9-A118-A6F92F08B34F}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "object_renderer_standalone", "object_renderer_standalone.vcxproj", "{75AE3898-3FDF-4AE2-86A1-838D0E78545E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -50,6 +52,12 @@ Global {54509728-928B-44D9-A118-A6F92F08B34F}.Release|Win32.Build.0 = Release|Win32 {54509728-928B-44D9-A118-A6F92F08B34F}.Unittests|Win32.ActiveCfg = Unittests|Win32 {54509728-928B-44D9-A118-A6F92F08B34F}.Unittests|Win32.Build.0 = Unittests|Win32 + {718DE063-A18B-BB72-9150-62B892E6FFA6}.Debug|Win32.ActiveCfg = Debug|Win32 + {718DE063-A18B-BB72-9150-62B892E6FFA6}.Debug|Win32.Build.0 = Debug|Win32 + {718DE063-A18B-BB72-9150-62B892E6FFA6}.Release|Win32.ActiveCfg = Release|Win32 + {718DE063-A18B-BB72-9150-62B892E6FFA6}.Release|Win32.Build.0 = Release|Win32 + {718DE063-A18B-BB72-9150-62B892E6FFA6}.Unittests|Win32.ActiveCfg = Unittests|Win32 + {718DE063-A18B-BB72-9150-62B892E6FFA6}.Unittests|Win32.Build.0 = Unittests|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/scripts/td_object_renderer/object_renderer_standalone/object_renderer_standalone/renderer_standalone.c b/scripts/td_object_renderer/object_renderer_standalone/object_renderer_standalone/renderer_standalone.c index 831192a0913c0d78a67cf11c6e11dbc4420c692b..b2963093a245b8e4c544a2e575da2059e9896828 100644 --- a/scripts/td_object_renderer/object_renderer_standalone/object_renderer_standalone/renderer_standalone.c +++ b/scripts/td_object_renderer/object_renderer_standalone/object_renderer_standalone/renderer_standalone.c @@ -52,7 +52,7 @@ *------------------------------------------------------------------------------------------*/ #define META_LINE_LENGTH 200 /* max number of characters at one line of metadata input/output file */ -#define NUM_ISM_METADATA_PER_LINE 5 /* Number of ISM metadata per line in a metadata file */ +#define NUM_ISM_METADATA_PER_LINE 4 /* Number of ISM metadata per line in a metadata file */ /*---------------------------------------------------------------------* * Local function prototypes @@ -109,7 +109,7 @@ int32_t frame = 0; /* Counter of frames */ /*------------------------------------------------------------------------------------------* * Standalone Renderer program * - * + * *------------------------------------------------------------------------------------------*/ int main( int argc, char *argv[] ) { @@ -126,7 +126,6 @@ int main( int argc, char *argv[] ) FILE *f_input; FILE *f_output; FILE *f_quat_traj; - int32_t tmp; float x, y, z, w; FILE *f_metadata[MAX_NUM_OBJECTS]; Decoder_Struct st_ivas_static; @@ -283,7 +282,7 @@ int main( int argc, char *argv[] ) /* Fs and 20ms frame length */ st_ivas->hDecoderConfig->output_Fs = atoi( argv[i] ) * 1000; - nFrameLength = ( ( int16_t )( st_ivas->hDecoderConfig->output_Fs / 1000 ) ) * 20; /* 20 ms frame */ + nFrameLength = ( (int16_t) ( st_ivas->hDecoderConfig->output_Fs / 1000 ) ) * 20; /* 20 ms frame */ i++; /* Input, n-channel audio */ @@ -311,6 +310,7 @@ int main( int argc, char *argv[] ) if ( f_quat_traj != NULL ) { + st_ivas->hDecoderConfig->Opt_Headrotation = 1; if ( ( st_ivas->hHeadTrackData = (HEAD_TRACK_DATA_HANDLE) count_malloc( sizeof( HEAD_TRACK_DATA ) ) ) == NULL ) { fprintf( stderr, "Can not allocate memory for head-tracking\n" ); @@ -318,6 +318,12 @@ int main( int argc, char *argv[] ) } st_ivas->hDecoderConfig->Opt_Headrotation = TRUE; } +#ifdef EXT_RENDERER + else + { + st_ivas->hDecoderConfig->Opt_Headrotation = 0; + } +#endif /* Init limiter */ st_ivas->hLimiter = ivas_limiter_open( nChannels, st_ivas->hDecoderConfig->output_Fs ); @@ -392,7 +398,7 @@ int main( int argc, char *argv[] ) { for ( i = 0; i < 4; i++ ) /* MAX_PARAM_SPATIAL_SUBFRAMES = 4 */ { - if ( 5 == fscanf( f_quat_traj, "%d,%f,%f,%f,%f", &tmp, &w, &x, &y, &z ) ) + if ( 4 == fscanf( f_quat_traj, "%f,%f,%f,%f", &w, &x, &y, &z ) ) { st_ivas->hHeadTrackData->num_quaternions = -1; @@ -430,7 +436,7 @@ int main( int argc, char *argv[] ) /* Trim first frame to compensate for delay */ if ( nFrameCount == 0 ) { - offset = ( int16_t )( st_ivas->hDecoderConfig->output_Fs / 200 ); /* 240 samples for 48kHz etc */ + offset = (int16_t) ( st_ivas->hDecoderConfig->output_Fs / 200 ); /* 240 samples for 48kHz etc */ } else { @@ -442,7 +448,7 @@ int main( int argc, char *argv[] ) { for ( nS = 0; nS < NumLdspks; nS++ ) { - MixFrameWav[n * NumLdspks + nS] = ( int16_t )( output[nS][n + offset] + 0.5f * sign( output[nS][n + offset] ) ); + MixFrameWav[n * NumLdspks + nS] = (int16_t) ( output[nS][n + offset] + 0.5f * sign( output[nS][n + offset] ) ); } } fwrite( MixFrameWav, sizeof( int16_t ), ( currFrameLength - offset ) * NumLdspks, f_output ); diff --git a/scripts/pyaudio3dtools/rotateMC.py b/tests/renderer/__init__.py similarity index 50% rename from scripts/pyaudio3dtools/rotateMC.py rename to tests/renderer/__init__.py index a956db633abc6d8f2208794d512f2cacb14cfeb4..c2c14754b4940b8f4ab39ef87a35a6356e5cab1a 100644 --- a/scripts/pyaudio3dtools/rotateMC.py +++ b/tests/renderer/__init__.py @@ -25,72 +25,3 @@ accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and the United Nations Convention on Contracts on the International Sales of Goods. """ - -import numpy as np - -from pyaudio3dtools import EFAP, spatialaudioformat -from pyaudio3dtools.constants import * -from pyaudio3dtools.quaternions.functions import Quat2RotMat, rotateAziEle - - -def rotateMC(x: np.ndarray, trajectory: str, layout: spatialaudioformat) -> np.ndarray: - """Rotate MC signal by applying a rotation matrix calculated from the current quaternion - in each subframe - - Parameters: - ---------- - x: np.ndarray - input multichannel signal - trajectory: str - path to trajectory file - - Returns: - ---------- - y: np.ndarray - rotated multichannel signal - """ - - # TODO needs optimization, currently slow - trj_data = np.genfromtxt(trajectory, delimiter=",") - trj_frames = trj_data.shape[0] - - sig_len = x.shape[0] - sig_dim = x.shape[1] - frame_len = (IVAS_FRAME_LEN_MS // 4) * 48000 - N_frames = int(sig_len / frame_len) - - y = np.zeros([sig_len, sig_dim]) - - panner = EFAP.EFAP(layout.ls_azi, layout.ls_ele) - - a = np.linspace(0, 1.0, frame_len, endpoint=False)[:, np.newaxis] - b = 1.0 - a - - R = np.eye(layout.nchannels) - R_old = np.eye(layout.nchannels) - - for i_frame in range(N_frames): - - start = i_frame * frame_len - end = (i_frame + 1) * frame_len - - q = trj_data[i_frame % trj_frames, :] - - rotated_pos = np.array( - [ - rotateAziEle(a, e, Quat2RotMat(q)) - for a, e in zip(layout.ls_azi, layout.ls_ele) - ] - ) - R = panner.pan(rotated_pos[:, 0], rotated_pos[:, 1]) - R[:, layout.lfe_index] = np.zeros([layout.nchannels, 1]) - R[layout.lfe_index, layout.lfe_index] = 1 - - frame_in = x[start:end, :] - frame_out = y[start:end, :] - - frame_out[:, :] = (frame_in @ R_old) * b + (frame_in @ R) * a - - R_old = R.copy() - - return y diff --git a/tests/renderer/compare_audio.py b/tests/renderer/compare_audio.py new file mode 100644 index 0000000000000000000000000000000000000000..bf3ce26c93f968ca9bf11d2b17fe7279b4d3d15c --- /dev/null +++ b/tests/renderer/compare_audio.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 + +""" + (C) 2022 Baseline Development Group with portions copyright Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies OY, Orange, + Panasonic Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation. All Rights Reserved. + + This software is protected by copyright law and by international treaties. + The Baseline Development Group consisting of Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies OY, Orange, + Panasonic Corporation, Qualcomm Technologies, Inc., and VoiceAge Corporation retain full ownership + rights in their respective contributions in the software. No license of any kind, including but not + limited to patent license, of any foregoing parties is hereby granted by implication, estoppel or + otherwise. + + This software is provided "AS IS", without any express or implied warranties. The software is in the + development stage. It is intended exclusively for experts who have experience with such software and + solely for the purpose of inspection. All implied warranties of non-infringement, merchantability + and/or fitness for a particular purpose are hereby disclaimed and excluded. + + Any dispute, controversy or claim arising under or in relation to providing this software shall be + submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in + accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and + the United Nations Convention on Contracts on the International Sales of Goods. +""" + +import sys +import warnings +from typing import Tuple + +import numpy as np + +from .constants import SCRIPTS_DIR + +sys.path.append(str(SCRIPTS_DIR)) +from pyaudio3dtools.audioarray import getdelay + + +def compare_audio_arrays( + left: np.ndarray, left_fs: int, right: np.ndarray, right_fs: int +) -> Tuple[float, float]: + + if left_fs != right_fs: + return ValueError(f"Differing samplerates: {left_fs} vs {right_fs}!") + + if left.shape[1] != right.shape[1]: + cmp_ch = min(left.shape[1], right.shape[1]) + warnings.warn( + f"Differing number of channels: {left.shape[1]} vs {right.shape[1]}! Comparing first {cmp_ch} channel(s)", + category=RuntimeWarning, + ) + left = left[:, :cmp_ch] + right = right[:, :cmp_ch] + + if left.shape[0] != right.shape[0]: + cmp_smp = min(left.shape[0], right.shape[0]) + warnings.warn( + f"Warning - different durations: {left.shape[0] / left_fs:.2f}s vs {right.shape[0] / right_fs:.2f}s! Comparing first {cmp_smp / left_fs : .2f} sample(s)", + category=RuntimeWarning, + ) + left = left[:cmp_smp, :] + right = right[:cmp_smp, :] + + if not np.array_equal(left, right): + delay = getdelay(left, right) + delay_abs = np.abs(delay) + # getdelay can return large values if signals are quite different + # limit any delay compensation to 20 ms + if delay != 0 and (delay_abs < left_fs / 50): + warnings.warn( + f"File B is delayed by {delay} samples ({delay*1000 / left_fs : .2f}ms)!", + category=RuntimeWarning, + ) + + # shift array + left = np.roll(left, delay, axis=0) + + # zero shifted out samples + if delay < 0: + left[-np.abs(delay) :, :] = 0 + elif delay > 0: + left[: np.abs(delay), :] = 0 + """ + http://www-mmsp.ece.mcgill.ca/Documents/Software/Packages/AFsp/AFsp/CompAudio.html + """ + num = np.sum(left * right) + den = np.sqrt(np.sum(left**2) * np.sum(right**2)) + if den > 0: + r = num / den + else: + r = np.inf + snr = 10 * np.log10(1 / (1 - (r**2))) + gain_b = num / np.sum(right**2) + max_diff = np.abs(np.max(left - right)) + else: + snr = np.inf + gain_b = 1 + max_diff = 0 + + return snr, gain_b, max_diff diff --git a/tests/renderer/constants.py b/tests/renderer/constants.py new file mode 100644 index 0000000000000000000000000000000000000000..cb2d580ec3ecc9342019975a424245feae2653e0 --- /dev/null +++ b/tests/renderer/constants.py @@ -0,0 +1,540 @@ +#!/usr/bin/env python3 + +""" + (C) 2022 Baseline Development Group with portions copyright Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies OY, Orange, + Panasonic Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation. All Rights Reserved. + + This software is protected by copyright law and by international treaties. + The Baseline Development Group consisting of Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies OY, Orange, + Panasonic Corporation, Qualcomm Technologies, Inc., and VoiceAge Corporation retain full ownership + rights in their respective contributions in the software. No license of any kind, including but not + limited to patent license, of any foregoing parties is hereby granted by implication, estoppel or + otherwise. + + This software is provided "AS IS", without any express or implied warranties. The software is in the + development stage. It is intended exclusively for experts who have experience with such software and + solely for the purpose of inspection. All implied warranties of non-infringement, merchantability + and/or fitness for a particular purpose are hereby disclaimed and excluded. + + Any dispute, controversy or claim arising under or in relation to providing this software shall be + submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in + accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and + the United Nations Convention on Contracts on the International Sales of Goods. +""" + +from pathlib import Path + +""" Set up paths """ +TESTS_DIR = Path(__file__).parent +SCRIPTS_DIR = TESTS_DIR.parents[1].joinpath("scripts").resolve() +TEST_VECTOR_DIR = TESTS_DIR.joinpath("data") + +OUTPUT_PATH_REF = TESTS_DIR.joinpath("ref") +OUTPUT_PATH_CUT = TESTS_DIR.joinpath("cut") + +CUSTOM_LAYOUT_DIR = SCRIPTS_DIR.joinpath("ls_layouts") +HR_TRAJECTORY_DIR = SCRIPTS_DIR.joinpath("trajectories") +TESTV_DIR = SCRIPTS_DIR.joinpath("testv") + +BIN_SUFFIX_MERGETARGET = "_ref" + +""" Encoder commandline template """ +IVAS_COD_CMD = [ + str(TESTS_DIR.parent.parent.joinpath("IVAS_cod")), + "", # 1 -> mode + "", # 2 -> options for mode + "", # 3 -> bitrate + "48", # 4 -> input fs + "", # 5 -> input file + "", # 6 -> bitstream file +] + +""" Decoder commandline template """ +IVAS_DEC_CMD = [ + str(TESTS_DIR.parent.parent.joinpath("IVAS_dec")), + "-no_delay_cmp", + "", # 2 -> output format, + "48", # 3 -> output fs + "", # 4 -> bitstream file + "", # 5 -> output file +] + +""" Renderer commandline template """ +RENDERER_CMD = [ + str(TESTS_DIR.parent.parent.joinpath("IVAS_rend")), + "-i", + "", # 2 -> input file + "-if", + "", # 4 -> input format + "-o", + "/dev/null", # 6 -> output file + "-of", + "", # 8 -> output format + "-fs", + "48", # 10 -> input fs + "--no_delay_cmp", + # "-ndl", + "-q", +] + +""" TD Object Renderer commandline template """ +TDRENDERER_CMD = [ + str( + SCRIPTS_DIR.joinpath("td_object_renderer") + .joinpath("object_renderer_standalone") + .joinpath("renderer_standalone") + ), + "48", + "", # 4 -> input file + "", # 5 -> output file +] + +""" CREND commandline template """ +CREND_CMD = [ + str( + SCRIPTS_DIR.joinpath("ivas_pytests/tests/unit_tests/crend/IVAS_crend_unit_test") + ), + "-test", + "1", + "-sr", + "48", + "-ifmt", + "", # 4 -> input format + "-ofmt", + "", # 8 -> output format + "-i", + "", # 2 -> input file + "-o", + "/dev/null", # 6 -> output file + # "-lp_lfe", + # "-limiter" + # "-no_delay_cmp" +] + +""" Format to file mappings """ +NCHAN_TO_FILE = { + 1: TEST_VECTOR_DIR.joinpath("spectral_test_1ch_48kHz.wav"), + 2: TEST_VECTOR_DIR.joinpath("spectral_test_2ch_48kHz.wav"), + 3: TEST_VECTOR_DIR.joinpath("spectral_test_3ch_48kHz.wav"), + 4: TEST_VECTOR_DIR.joinpath("spectral_test_4ch_48kHz.wav"), + 5: TEST_VECTOR_DIR.joinpath("spectral_test_5ch_48kHz.wav"), + 6: TEST_VECTOR_DIR.joinpath("spectral_test_6ch_48kHz.wav"), + 8: TEST_VECTOR_DIR.joinpath("spectral_test_8ch_48kHz.wav"), + 9: TEST_VECTOR_DIR.joinpath("spectral_test_9ch_48kHz.wav"), + 10: TEST_VECTOR_DIR.joinpath("spectral_test_10ch_48kHz.wav"), + 11: TEST_VECTOR_DIR.joinpath("spectral_test_11ch_48kHz.wav"), + 12: TEST_VECTOR_DIR.joinpath("spectral_test_12ch_48kHz.wav"), + 15: TEST_VECTOR_DIR.joinpath("spectral_test_15ch_48kHz.wav"), + 16: TEST_VECTOR_DIR.joinpath("spectral_test_16ch_48kHz.wav"), +} + +FORMAT_TO_FILE = { + "MONO": NCHAN_TO_FILE[1], + "STEREO": NCHAN_TO_FILE[2], + "5_1": NCHAN_TO_FILE[6], + "7_1": NCHAN_TO_FILE[8], + "5_1_2": NCHAN_TO_FILE[8], + "5_1_4": NCHAN_TO_FILE[10], + "7_1_4": NCHAN_TO_FILE[12], + "FOA": NCHAN_TO_FILE[4], + "HOA2": NCHAN_TO_FILE[9], + "HOA3": NCHAN_TO_FILE[16], + "ISM1": NCHAN_TO_FILE[1], + "ISM2": NCHAN_TO_FILE[2], + "ISM3": NCHAN_TO_FILE[3], + "ISM4": NCHAN_TO_FILE[4], + # "ISM1": TEST_VECTOR_DIR.joinpath("spectral_test_ism1.txt"), + # "ISM2": TEST_VECTOR_DIR.joinpath("spectral_test_ism2.txt"), + # "ISM3": TEST_VECTOR_DIR.joinpath("spectral_test_ism3.txt"), + # "ISM4": TEST_VECTOR_DIR.joinpath("spectral_test_ism4.txt"), + # "MASA1": NCHAN_TO_FILE[1], + # "MASA2": NCHAN_TO_FILE[2], + "MASA1": TESTV_DIR.joinpath("stv_IVASMASA_1dir1TC.pcm"), + "MASA2": TESTV_DIR.joinpath("stv_IVASMASA_2dir2TC.pcm"), + "META": TEST_VECTOR_DIR.joinpath("mixed_scene.txt"), + "16ch_8+4+4": NCHAN_TO_FILE[16], + "4d0": NCHAN_TO_FILE[4], + "4d4": NCHAN_TO_FILE[8], + "cicp1": NCHAN_TO_FILE[1], + "cicp20": NCHAN_TO_FILE[15], + "cicp2": NCHAN_TO_FILE[2], + "custom1": NCHAN_TO_FILE[11], + "itu_4+5+1": NCHAN_TO_FILE[11], + "t_design_4": NCHAN_TO_FILE[12], +} + +FORMAT_TO_METADATA_FILES = { + "ISM1": [str(TESTV_DIR.joinpath("stvISM1.csv"))], + "ISM2": [ + str(TESTV_DIR.joinpath("stvISM1.csv")), + str(TESTV_DIR.joinpath("stvISM2.csv")), + ], + "ISM3": [ + str(TESTV_DIR.joinpath("stvISM1.csv")), + str(TESTV_DIR.joinpath("stvISM2.csv")), + str(TESTV_DIR.joinpath("stvISM3.csv")), + ], + "ISM4": [ + str(TESTV_DIR.joinpath("stvISM1.csv")), + str(TESTV_DIR.joinpath("stvISM2.csv")), + str(TESTV_DIR.joinpath("stvISM3.csv")), + str(TESTV_DIR.joinpath("stvISM4.csv")), + ], + "MASA1": [str(TESTV_DIR.joinpath("stv_IVASMASA_1dir1TC.met"))], + "MASA2": [str(TESTV_DIR.joinpath("stv_IVASMASA_2dir2TC.met"))], +} + +FORMAT_TO_IVAS = { + "MONO": ["", ""], + "STEREO": ["-stereo", ""], + "FOA": ["-sba", "1"], + "HOA2": ["-sba", "2"], + "HOA3": ["-sba", "3"], + "5_1": ["-mc", "5_1"], + "7_1": ["-mc", "7_1"], + "5_1_2": ["-mc", "5_1_2"], + "5_1_4": ["-mc", "5_1_4"], + "7_1_4": ["-mc", "7_1_4"], + "ISM1": ["-ism", "1"], + "ISM2": ["-ism", "2"], + "ISM3": ["-ism", "3"], + "ISM4": ["-ism", "4"], +} + +FORMAT_TO_IVAS_BR = { + "MONO": "128000", + "STEREO": "256000", + "FOA": "512000", + "HOA2": "512000", + "HOA3": "512000", + "5_1": "512000", + "7_1": "512000", + "5_1_2": "512000", + "5_1_4": "512000", + "7_1_4": "512000", + "ISM1": "256000", + "ISM2": "256000", + "ISM3": "256000", + "ISM4": "256000", +} + +FORMAT_TO_CREND_FORMAT = { + "MONO": "0", + "STEREO": "1", + "BINAURAL": "2", + "BINAURAL_ROOM": "2", + "FOA": "3", + "5_1": "4", + "7_1": "5", + "5_1_2": "6", + "5_1_4": "7", + "7_1_4": "8", + "HOA2": "9", + "HOA3": "10", +} + +""" Input formats """ +INPUT_FORMATS_AMBI = ["FOA", "HOA2", "HOA3"] +INPUT_FORMATS_MC = ["MONO", "STEREO", "5_1", "5_1_2", "5_1_4", "7_1", "7_1_4"] +INPUT_FORMATS_ISM = ["ISM1", "ISM2", "ISM3", "ISM4"] +INPUT_FORMATS_MASA = ["MASA1", "MASA2"] + +""" Non binaural / parametric output formats """ +OUTPUT_FORMATS = [ + "MONO", + "STEREO", + "5_1", + "5_1_2", + "5_1_4", + "7_1", + "7_1_4", + "FOA", + "HOA2", + "HOA3", +] + +""" Custom loudspeaker input/output """ +CUSTOM_LS_TO_TEST = [ + "t_design_4", + "4d4", + "itu_4+5+1", + "16ch_8+4+4", +] + +""" Mixed scene ( metadata ) rendering """ +METADATA_SCENES_TO_TEST = ["mixed_scene", "mixed_scene_simple"] +METADATA_SCENES_TO_TEST_NO_BE = ["masa_scene"] + +""" Binaural rendering """ +OUTPUT_FORMATS_BINAURAL = ["BINAURAL", "BINAURAL_ROOM"] +HR_TRAJECTORIES_TO_TEST = [ + "full_circle_in_15s", + "rotate_yaw_pitch_roll1", +] + +""" Per-testcase xfail SNR thresholds (dB) """ +pass_snr = { + #################################################################### + # + # External Renderer vs Standalone and pyaudio3dtools renderers tests + # + #################################################################### + # TODO needs debugging + "test_ambisonics_binaural_headrotation[HOA2-BINAURAL-full_circle_in_15s]": 18, + "test_ambisonics_binaural_headrotation[HOA3-BINAURAL-full_circle_in_15s]": 15, + # Failure reason: Crend unit test does not support intermediate conversion to 7_1_4 or SHD BRIRs + # Comparison with pyaudio3dtools results in bad SNR + "test_ambisonics_binaural_headrotation[FOA-BINAURAL_ROOM-full_circle_in_15s]": 0, + "test_ambisonics_binaural_headrotation[FOA-BINAURAL_ROOM-rotate_yaw_pitch_roll1]": 0, + "test_ambisonics_binaural_headrotation[HOA2-BINAURAL_ROOM-full_circle_in_15s]": 0, + "test_ambisonics_binaural_headrotation[HOA2-BINAURAL_ROOM-rotate_yaw_pitch_roll1]": 0, + "test_ambisonics_binaural_headrotation[HOA2-BINAURAL-rotate_yaw_pitch_roll1]": 4, + "test_ambisonics_binaural_headrotation[HOA3-BINAURAL_ROOM-full_circle_in_15s]": 0, + "test_ambisonics_binaural_headrotation[HOA3-BINAURAL_ROOM-rotate_yaw_pitch_roll1]": 0, + "test_ambisonics_binaural_headrotation[HOA3-BINAURAL-rotate_yaw_pitch_roll1]": 3, + "test_ambisonics_binaural_static[FOA-BINAURAL_ROOM]": 0, + "test_ambisonics_binaural_static[HOA2-BINAURAL_ROOM]": 0, + "test_ambisonics_binaural_static[HOA3-BINAURAL_ROOM]": 0, + # Failure reason: TD Object Renderer standalone does not support custom LS input + # Comparison with pyaudio3dtools results in bad SNR + "test_custom_ls_input_binaural[16ch_8+4+4-BINAURAL]": 0, + "test_custom_ls_input_binaural[16ch_8+4+4-BINAURAL_ROOM]": 0, + "test_custom_ls_input_binaural[4d4-BINAURAL]": 0, + "test_custom_ls_input_binaural[4d4-BINAURAL_ROOM]": 0, + "test_custom_ls_input_binaural_headrotation[16ch_8+4+4-BINAURAL-full_circle_in_15s]": 0, + "test_custom_ls_input_binaural_headrotation[16ch_8+4+4-BINAURAL_ROOM-full_circle_in_15s]": 0, + "test_custom_ls_input_binaural_headrotation[16ch_8+4+4-BINAURAL_ROOM-rotate_yaw_pitch_roll1]": 0, + "test_custom_ls_input_binaural_headrotation[16ch_8+4+4-BINAURAL-rotate_yaw_pitch_roll1]": 0, + "test_custom_ls_input_binaural_headrotation[4d4-BINAURAL-full_circle_in_15s]": 0, + "test_custom_ls_input_binaural_headrotation[4d4-BINAURAL_ROOM-full_circle_in_15s]": 0, + "test_custom_ls_input_binaural_headrotation[4d4-BINAURAL_ROOM-rotate_yaw_pitch_roll1]": 0, + "test_custom_ls_input_binaural_headrotation[4d4-BINAURAL-rotate_yaw_pitch_roll1]": 0, + "test_custom_ls_input_binaural_headrotation[itu_4+5+1-BINAURAL-full_circle_in_15s]": 0, + "test_custom_ls_input_binaural_headrotation[itu_4+5+1-BINAURAL_ROOM-full_circle_in_15s]": 3, + "test_custom_ls_input_binaural_headrotation[itu_4+5+1-BINAURAL_ROOM-rotate_yaw_pitch_roll1]": 3, + "test_custom_ls_input_binaural_headrotation[itu_4+5+1-BINAURAL-rotate_yaw_pitch_roll1]": 0, + "test_custom_ls_input_binaural_headrotation[t_design_4-BINAURAL-full_circle_in_15s]": 0, + "test_custom_ls_input_binaural_headrotation[t_design_4-BINAURAL_ROOM-full_circle_in_15s]": 0, + "test_custom_ls_input_binaural_headrotation[t_design_4-BINAURAL_ROOM-rotate_yaw_pitch_roll1]": 0, + "test_custom_ls_input_binaural_headrotation[t_design_4-BINAURAL-rotate_yaw_pitch_roll1]": 0, + "test_custom_ls_input_binaural[itu_4+5+1-BINAURAL]": 0, + "test_custom_ls_input_binaural[itu_4+5+1-BINAURAL_ROOM]": 3, + "test_custom_ls_input_binaural[t_design_4-BINAURAL]": 0, + "test_custom_ls_input_binaural[t_design_4-BINAURAL_ROOM]": 0, + # TODO need to verify 5ms rendering in external renderer + # Crend unit test does not support intermediate conversion to 7_1_4 + # Comparison with pyaudio3dtools results in bad SNR + "test_ism_binaural_headrotation[ISM1-BINAURAL_ROOM-full_circle_in_15s]": 9, + "test_ism_binaural_headrotation[ISM1-BINAURAL_ROOM-rotate_yaw_pitch_roll1]": 4, + "test_ism_binaural_headrotation[ISM2-BINAURAL_ROOM-full_circle_in_15s]": 10, + "test_ism_binaural_headrotation[ISM2-BINAURAL_ROOM-rotate_yaw_pitch_roll1]": 3, + "test_ism_binaural_headrotation[ISM2-BINAURAL-rotate_yaw_pitch_roll1]": 24, + "test_ism_binaural_headrotation[ISM3-BINAURAL_ROOM-full_circle_in_15s]": 10, + "test_ism_binaural_headrotation[ISM3-BINAURAL_ROOM-rotate_yaw_pitch_roll1]": 4, + "test_ism_binaural_headrotation[ISM3-BINAURAL-rotate_yaw_pitch_roll1]": 24, + "test_ism_binaural_headrotation[ISM4-BINAURAL_ROOM-full_circle_in_15s]": 10, + "test_ism_binaural_headrotation[ISM4-BINAURAL_ROOM-rotate_yaw_pitch_roll1]": 4, + "test_ism_binaural_headrotation[ISM4-BINAURAL-rotate_yaw_pitch_roll1]": 24, + "test_ism_binaural_static[ISM1-BINAURAL_ROOM]": 23, + "test_ism_binaural_static[ISM2-BINAURAL_ROOM]": 21, + "test_ism_binaural_static[ISM3-BINAURAL_ROOM]": 21, + "test_ism_binaural_static[ISM4-BINAURAL_ROOM]": 21, + # TODO needs debugging + # Failure reason: minor differences could be due to crossfades or metadata position rounding + "test_ism[ISM1-5_1_2]": 48, + "test_ism[ISM1-5_1_4]": 48, + "test_ism[ISM1-5_1]": 48, + "test_ism[ISM1-7_1_4]": 46, + "test_ism[ISM1-7_1]": 45, + "test_ism[ISM1-FOA]": 44, + "test_ism[ISM1-HOA2]": 40, + "test_ism[ISM1-HOA3]": 37, + "test_ism[ISM1-STEREO]": 54, + "test_ism[ISM2-5_1_2]": 46, + "test_ism[ISM2-5_1_4]": 45, + "test_ism[ISM2-5_1]": 47, + "test_ism[ISM2-7_1_4]": 43, + "test_ism[ISM2-7_1]": 45, + "test_ism[ISM2-FOA]": 41, + "test_ism[ISM2-HOA2]": 37, + "test_ism[ISM2-HOA3]": 34, + "test_ism[ISM2-STEREO]": 55, + "test_ism[ISM3-5_1_2]": 44, + "test_ism[ISM3-5_1_4]": 43, + "test_ism[ISM3-5_1]": 45, + "test_ism[ISM3-7_1_4]": 42, + "test_ism[ISM3-7_1]": 44, + "test_ism[ISM3-FOA]": 39, + "test_ism[ISM3-HOA2]": 36, + "test_ism[ISM3-HOA3]": 33, + "test_ism[ISM3-STEREO]": 54, + "test_ism[ISM4-5_1_2]": 44, + "test_ism[ISM4-5_1_4]": 44, + "test_ism[ISM4-5_1]": 46, + "test_ism[ISM4-7_1_4]": 43, + "test_ism[ISM4-7_1]": 44, + "test_ism[ISM4-FOA]": 40, + "test_ism[ISM4-HOA2]": 36, + "test_ism[ISM4-HOA3]": 33, + "test_ism[ISM4-STEREO]": 57, + # TODO delay alignment of LFE in binaural output + # Failure reason: bitexact except for delay alignment of LFE signal (Issue 59) + "test_multichannel_binaural_headrotation[5_1-BINAURAL-full_circle_in_15s]": 7, + "test_multichannel_binaural_headrotation[5_1-BINAURAL-rotate_yaw_pitch_roll1]": 6, + "test_multichannel_binaural_headrotation[7_1-BINAURAL-full_circle_in_15s]": 8, + "test_multichannel_binaural_headrotation[7_1-BINAURAL-rotate_yaw_pitch_roll1]": 8, + # Failure reason: bitexact except for clicks and differences in center channel, could be due to crossfades + "test_multichannel_binaural_headrotation[5_1_2-BINAURAL-full_circle_in_15s]": 30, + "test_multichannel_binaural_headrotation[5_1_2-BINAURAL-rotate_yaw_pitch_roll1]": 30, + "test_multichannel_binaural_headrotation[5_1_4-BINAURAL-full_circle_in_15s]": 29, + "test_multichannel_binaural_headrotation[5_1_4-BINAURAL-rotate_yaw_pitch_roll1]": 29, + "test_multichannel_binaural_headrotation[7_1_4-BINAURAL-full_circle_in_15s]": 30, + "test_multichannel_binaural_headrotation[7_1_4-BINAURAL-rotate_yaw_pitch_roll1]": 30, + "test_multichannel_binaural_static[5_1_2-BINAURAL]": 30, + "test_multichannel_binaural_static[5_1_4-BINAURAL]": 29, + "test_multichannel_binaural_static[5_1-BINAURAL]": 27, + "test_multichannel_binaural_static[7_1-BINAURAL]": 30, + "test_multichannel_binaural_static[7_1_4-BINAURAL]": 30, + # TODO needs debugging + # Failure reason: headrotation may be applied differently, differences increase progressively + "test_multichannel_binaural_headrotation[5_1-BINAURAL_ROOM-full_circle_in_15s]": 10, + "test_multichannel_binaural_headrotation[5_1-BINAURAL_ROOM-rotate_yaw_pitch_roll1]": 4, + "test_multichannel_binaural_headrotation[5_1_2-BINAURAL_ROOM-full_circle_in_15s]": 11, + "test_multichannel_binaural_headrotation[5_1_2-BINAURAL_ROOM-rotate_yaw_pitch_roll1]": 3, + "test_multichannel_binaural_headrotation[5_1_4-BINAURAL_ROOM-full_circle_in_15s]": 12, + "test_multichannel_binaural_headrotation[5_1_4-BINAURAL_ROOM-rotate_yaw_pitch_roll1]": 3, + "test_multichannel_binaural_headrotation[7_1-BINAURAL_ROOM-full_circle_in_15s]": 10, + "test_multichannel_binaural_headrotation[7_1-BINAURAL_ROOM-rotate_yaw_pitch_roll1]": 3, + "test_multichannel_binaural_headrotation[7_1_4-BINAURAL_ROOM-full_circle_in_15s]": 10, + "test_multichannel_binaural_headrotation[7_1_4-BINAURAL_ROOM-rotate_yaw_pitch_roll1]": 3, + ##################################### + # + # External vs Internal Renderer tests + # + ##################################### + # Failure reason: only fails for this trajectory with very high SNR, possible minor diff. in crossfade + # or due to usage of multiple TD Object Renderer instances + "test_ism_binaural_headrotation_vs_decoder[ISM2-BINAURAL-rotate_yaw_pitch_roll1]": 84, + "test_ism_binaural_headrotation_vs_decoder[ISM3-BINAURAL-rotate_yaw_pitch_roll1]": 78, + "test_ism_binaural_headrotation_vs_decoder[ISM4-BINAURAL-rotate_yaw_pitch_roll1]": 85, + # TODO needs investigation + # Failure reason: conversion to 7_1_4 could be implemented differently w.r.t decoder + "test_ism_binaural_headrotation_vs_decoder[ISM1-BINAURAL_ROOM-full_circle_in_15s]": 15, + "test_ism_binaural_headrotation_vs_decoder[ISM1-BINAURAL_ROOM-rotate_yaw_pitch_roll1]": 15, + "test_ism_binaural_headrotation_vs_decoder[ISM2-BINAURAL_ROOM-full_circle_in_15s]": 12, + "test_ism_binaural_headrotation_vs_decoder[ISM2-BINAURAL_ROOM-rotate_yaw_pitch_roll1]": 13, + "test_ism_binaural_headrotation_vs_decoder[ISM3-BINAURAL_ROOM-full_circle_in_15s]": 12, + "test_ism_binaural_headrotation_vs_decoder[ISM3-BINAURAL_ROOM-rotate_yaw_pitch_roll1]": 13, + "test_ism_binaural_headrotation_vs_decoder[ISM4-BINAURAL_ROOM-full_circle_in_15s]": 12, + "test_ism_binaural_headrotation_vs_decoder[ISM4-BINAURAL_ROOM-rotate_yaw_pitch_roll1]": 13, + "test_ism_binaural_static_vs_decoder[ISM1-BINAURAL_ROOM]": 15, + "test_ism_binaural_static_vs_decoder[ISM2-BINAURAL_ROOM]": 12, + "test_ism_binaural_static_vs_decoder[ISM3-BINAURAL_ROOM]": 12, + "test_ism_binaural_static_vs_decoder[ISM4-BINAURAL_ROOM]": 12, + # TODO harmonize panning to stereo + # Failure reason ISM to stereo panning is done via EFAP in the renderer and tangent law in decoder, harmonize + "test_ism_vs_decoder[ISM1-STEREO]": 8, + "test_ism_vs_decoder[ISM2-STEREO]": 17, + "test_ism_vs_decoder[ISM3-STEREO]": 14, + "test_ism_vs_decoder[ISM4-STEREO]": 14, + # TODO needs investigation + # Failure reason: likely differences between metadata position rounding (decoder uses ceil()) and crossfades + "test_ism_vs_decoder[ISM1-5_1_2]": 26, + "test_ism_vs_decoder[ISM1-5_1]": 26, + "test_ism_vs_decoder[ISM1-5_1_4]": 26, + "test_ism_vs_decoder[ISM1-7_1]": 26, + "test_ism_vs_decoder[ISM1-7_1_4]": 26, + "test_ism_vs_decoder[ISM1-FOA]": 26, + "test_ism_vs_decoder[ISM1-HOA2]": 26, + "test_ism_vs_decoder[ISM1-HOA3]": 26, + "test_ism_vs_decoder[ISM2-5_1_2]": 31, + "test_ism_vs_decoder[ISM2-5_1_4]": 31, + "test_ism_vs_decoder[ISM2-5_1]": 6, + "test_ism_vs_decoder[ISM2-7_1_4]": 31, + "test_ism_vs_decoder[ISM2-7_1]": 5, + "test_ism_vs_decoder[ISM2-FOA]": 31, + "test_ism_vs_decoder[ISM2-HOA2]": 30, + "test_ism_vs_decoder[ISM2-HOA3]": 29, + "test_ism_vs_decoder[ISM3-5_1_2]": 32, + "test_ism_vs_decoder[ISM3-5_1_4]": 32, + "test_ism_vs_decoder[ISM3-5_1]": 8, + "test_ism_vs_decoder[ISM3-7_1_4]": 31, + "test_ism_vs_decoder[ISM3-7_1]": 7, + "test_ism_vs_decoder[ISM3-FOA]": 32, + "test_ism_vs_decoder[ISM3-HOA2]": 31, + "test_ism_vs_decoder[ISM3-HOA3]": 29, + "test_ism_vs_decoder[ISM3-MONO]": 77, + "test_ism_vs_decoder[ISM4-5_1_2]": 31, + "test_ism_vs_decoder[ISM4-5_1_4]": 30, + "test_ism_vs_decoder[ISM4-5_1]": 8, + "test_ism_vs_decoder[ISM4-7_1_4]": 30, + "test_ism_vs_decoder[ISM4-7_1]": 7, + "test_ism_vs_decoder[ISM4-FOA]": 31, + "test_ism_vs_decoder[ISM4-HOA2]": 30, + "test_ism_vs_decoder[ISM4-HOA3]": 29, + "test_ism_vs_decoder[ISM4-MONO]": 77, + # TODO needs investigation + # Failure reason: headrotation and crossfade could have differences + "test_multichannel_binaural_headrotation_vs_decoder[5_1_2-BINAURAL-full_circle_in_15s]": 4, + "test_multichannel_binaural_headrotation_vs_decoder[5_1_2-BINAURAL-rotate_yaw_pitch_roll1]": 0, + "test_multichannel_binaural_headrotation_vs_decoder[5_1_4-BINAURAL-full_circle_in_15s]": 4, + "test_multichannel_binaural_headrotation_vs_decoder[5_1_4-BINAURAL-rotate_yaw_pitch_roll1]": 0, + "test_multichannel_binaural_headrotation_vs_decoder[7_1_4-BINAURAL-full_circle_in_15s]": 4, + "test_multichannel_binaural_headrotation_vs_decoder[7_1_4-BINAURAL-rotate_yaw_pitch_roll1]": 0, + # TODO needs investigation + "test_multichannel_binaural_headrotation_vs_decoder[5_1-BINAURAL_ROOM-full_circle_in_15s]": 5, + "test_multichannel_binaural_headrotation_vs_decoder[5_1-BINAURAL_ROOM-rotate_yaw_pitch_roll1]": 0, + "test_multichannel_binaural_headrotation_vs_decoder[5_1_2-BINAURAL_ROOM-full_circle_in_15s]": 6, + "test_multichannel_binaural_headrotation_vs_decoder[5_1_2-BINAURAL_ROOM-rotate_yaw_pitch_roll1]": 0, + "test_multichannel_binaural_headrotation_vs_decoder[5_1_4-BINAURAL_ROOM-full_circle_in_15s]": 7, + "test_multichannel_binaural_headrotation_vs_decoder[5_1_4-BINAURAL_ROOM-rotate_yaw_pitch_roll1]": 0, + "test_multichannel_binaural_headrotation_vs_decoder[7_1-BINAURAL_ROOM-full_circle_in_15s]": 5, + "test_multichannel_binaural_headrotation_vs_decoder[7_1-BINAURAL_ROOM-rotate_yaw_pitch_roll1]": 0, + "test_multichannel_binaural_headrotation_vs_decoder[7_1_4-BINAURAL_ROOM-full_circle_in_15s]": 5, + "test_multichannel_binaural_headrotation_vs_decoder[7_1_4-BINAURAL_ROOM-rotate_yaw_pitch_roll1]": 0, + # TODO needs investigation + "test_multichannel_binaural_static_vs_decoder[5_1_2-BINAURAL_ROOM]": 18, + "test_multichannel_binaural_static_vs_decoder[5_1_4-BINAURAL_ROOM]": 18, + "test_multichannel_binaural_static_vs_decoder[5_1-BINAURAL_ROOM]": 18, + "test_multichannel_binaural_static_vs_decoder[7_1_4-BINAURAL_ROOM]": 18, + "test_multichannel_binaural_static_vs_decoder[7_1-BINAURAL_ROOM]": 19, + # Failure reason: Mono downmix significantly different, needs a fix + "test_multichannel_vs_decoder[5_1_2-MONO]": 1, + "test_multichannel_vs_decoder[5_1_4-MONO]": 1, + "test_multichannel_vs_decoder[5_1-MONO]": 1, + "test_multichannel_vs_decoder[7_1_4-MONO]": 1, + "test_multichannel_vs_decoder[7_1-MONO]": 1, + "test_multichannel_vs_decoder[STEREO-MONO]": 17, + # Failure reason: Stereo downmix differs slightly, needs a fix + "test_multichannel_vs_decoder[5_1_2-STEREO]": 44, + "test_multichannel_vs_decoder[5_1_4-STEREO]": 48, + "test_multichannel_vs_decoder[5_1-STEREO]": 48, + "test_multichannel_vs_decoder[7_1_4-STEREO]": 46, + "test_multichannel_vs_decoder[7_1-STEREO]": 44, + # TODO needs investigation + # Failure reason: possibly due to minor differences in crossfades + "test_multichannel_vs_decoder[5_1_2-5_1_4]": 63, + "test_multichannel_vs_decoder[5_1_2-5_1]": 63, + "test_multichannel_vs_decoder[5_1_2-7_1_4]": 63, + "test_multichannel_vs_decoder[5_1_2-7_1]": 63, + "test_multichannel_vs_decoder[5_1_4-5_1_2]": 63, + "test_multichannel_vs_decoder[5_1_4-5_1]": 62, + "test_multichannel_vs_decoder[5_1_4-7_1_4]": 61, + "test_multichannel_vs_decoder[5_1_4-7_1]": 62, + "test_multichannel_vs_decoder[5_1-5_1_2]": 62, + "test_multichannel_vs_decoder[5_1-5_1_4]": 62, + "test_multichannel_vs_decoder[5_1-7_1_4]": 62, + "test_multichannel_vs_decoder[5_1-7_1]": 62, + "test_multichannel_vs_decoder[7_1_4-5_1_2]": 63, + "test_multichannel_vs_decoder[7_1_4-5_1_4]": 63, + "test_multichannel_vs_decoder[7_1_4-5_1]": 63, + "test_multichannel_vs_decoder[7_1_4-7_1]": 62, + "test_multichannel_vs_decoder[7_1-5_1_2]": 63, + "test_multichannel_vs_decoder[7_1-5_1_4]": 63, + "test_multichannel_vs_decoder[7_1-5_1]": 63, + "test_multichannel_vs_decoder[7_1-7_1_4]": 63, +} diff --git a/tests/renderer/cut/.gitignore b/tests/renderer/cut/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..f935021a8f8a7bd22f9d6703cafa5134bb6a57f8 --- /dev/null +++ b/tests/renderer/cut/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/scripts/prerenderer_configs/ism1_ivas_mtdt_config.txt b/tests/renderer/data/ism1_ivas_mtdt_config.txt similarity index 100% rename from scripts/prerenderer_configs/ism1_ivas_mtdt_config.txt rename to tests/renderer/data/ism1_ivas_mtdt_config.txt diff --git a/scripts/prerenderer_configs/ism1_shorthand_config.txt b/tests/renderer/data/ism1_shorthand_config.txt similarity index 100% rename from scripts/prerenderer_configs/ism1_shorthand_config.txt rename to tests/renderer/data/ism1_shorthand_config.txt diff --git a/scripts/prerenderer_configs/ism2_ivas_mtdt_config.txt b/tests/renderer/data/ism2_ivas_mtdt_config.txt similarity index 100% rename from scripts/prerenderer_configs/ism2_ivas_mtdt_config.txt rename to tests/renderer/data/ism2_ivas_mtdt_config.txt diff --git a/scripts/prerenderer_configs/ism2_shorthand_config.txt b/tests/renderer/data/ism2_shorthand_config.txt similarity index 100% rename from scripts/prerenderer_configs/ism2_shorthand_config.txt rename to tests/renderer/data/ism2_shorthand_config.txt diff --git a/scripts/prerenderer_configs/ism3_ivas_mtdt_config.txt b/tests/renderer/data/ism3_ivas_mtdt_config.txt similarity index 100% rename from scripts/prerenderer_configs/ism3_ivas_mtdt_config.txt rename to tests/renderer/data/ism3_ivas_mtdt_config.txt diff --git a/scripts/prerenderer_configs/ism3_shorthand_config.txt b/tests/renderer/data/ism3_shorthand_config.txt similarity index 100% rename from scripts/prerenderer_configs/ism3_shorthand_config.txt rename to tests/renderer/data/ism3_shorthand_config.txt diff --git a/scripts/prerenderer_configs/ism4_ivas_mtdt_config.txt b/tests/renderer/data/ism4_ivas_mtdt_config.txt similarity index 100% rename from scripts/prerenderer_configs/ism4_ivas_mtdt_config.txt rename to tests/renderer/data/ism4_ivas_mtdt_config.txt diff --git a/scripts/prerenderer_configs/ism4_shorthand_config.txt b/tests/renderer/data/ism4_shorthand_config.txt similarity index 100% rename from scripts/prerenderer_configs/ism4_shorthand_config.txt rename to tests/renderer/data/ism4_shorthand_config.txt diff --git a/tests/renderer/data/ism_0a_0e.csv b/tests/renderer/data/ism_0a_0e.csv new file mode 100644 index 0000000000000000000000000000000000000000..fa12ec7c33f477bf9fec00278f8f86d0cafcdd49 --- /dev/null +++ b/tests/renderer/data/ism_0a_0e.csv @@ -0,0 +1,750 @@ +0,0,0,1,0,1 +1,0,0,1,0,1 +2,0,0,1,0,1 +3,0,0,1,0,1 +4,0,0,1,0,1 +5,0,0,1,0,1 +6,0,0,1,0,1 +7,0,0,1,0,1 +8,0,0,1,0,1 +9,0,0,1,0,1 +10,0,0,1,0,1 +11,0,0,1,0,1 +12,0,0,1,0,1 +13,0,0,1,0,1 +14,0,0,1,0,1 +15,0,0,1,0,1 +16,0,0,1,0,1 +17,0,0,1,0,1 +18,0,0,1,0,1 +19,0,0,1,0,1 +20,0,0,1,0,1 +21,0,0,1,0,1 +22,0,0,1,0,1 +23,0,0,1,0,1 +24,0,0,1,0,1 +25,0,0,1,0,1 +26,0,0,1,0,1 +27,0,0,1,0,1 +28,0,0,1,0,1 +29,0,0,1,0,1 +30,0,0,1,0,1 +31,0,0,1,0,1 +32,0,0,1,0,1 +33,0,0,1,0,1 +34,0,0,1,0,1 +35,0,0,1,0,1 +36,0,0,1,0,1 +37,0,0,1,0,1 +38,0,0,1,0,1 +39,0,0,1,0,1 +40,0,0,1,0,1 +41,0,0,1,0,1 +42,0,0,1,0,1 +43,0,0,1,0,1 +44,0,0,1,0,1 +45,0,0,1,0,1 +46,0,0,1,0,1 +47,0,0,1,0,1 +48,0,0,1,0,1 +49,0,0,1,0,1 +50,0,0,1,0,1 +51,0,0,1,0,1 +52,0,0,1,0,1 +53,0,0,1,0,1 +54,0,0,1,0,1 +55,0,0,1,0,1 +56,0,0,1,0,1 +57,0,0,1,0,1 +58,0,0,1,0,1 +59,0,0,1,0,1 +60,0,0,1,0,1 +61,0,0,1,0,1 +62,0,0,1,0,1 +63,0,0,1,0,1 +64,0,0,1,0,1 +65,0,0,1,0,1 +66,0,0,1,0,1 +67,0,0,1,0,1 +68,0,0,1,0,1 +69,0,0,1,0,1 +70,0,0,1,0,1 +71,0,0,1,0,1 +72,0,0,1,0,1 +73,0,0,1,0,1 +74,0,0,1,0,1 +75,0,0,1,0,1 +76,0,0,1,0,1 +77,0,0,1,0,1 +78,0,0,1,0,1 +79,0,0,1,0,1 +80,0,0,1,0,1 +81,0,0,1,0,1 +82,0,0,1,0,1 +83,0,0,1,0,1 +84,0,0,1,0,1 +85,0,0,1,0,1 +86,0,0,1,0,1 +87,0,0,1,0,1 +88,0,0,1,0,1 +89,0,0,1,0,1 +90,0,0,1,0,1 +91,0,0,1,0,1 +92,0,0,1,0,1 +93,0,0,1,0,1 +94,0,0,1,0,1 +95,0,0,1,0,1 +96,0,0,1,0,1 +97,0,0,1,0,1 +98,0,0,1,0,1 +99,0,0,1,0,1 +100,0,0,1,0,1 +101,0,0,1,0,1 +102,0,0,1,0,1 +103,0,0,1,0,1 +104,0,0,1,0,1 +105,0,0,1,0,1 +106,0,0,1,0,1 +107,0,0,1,0,1 +108,0,0,1,0,1 +109,0,0,1,0,1 +110,0,0,1,0,1 +111,0,0,1,0,1 +112,0,0,1,0,1 +113,0,0,1,0,1 +114,0,0,1,0,1 +115,0,0,1,0,1 +116,0,0,1,0,1 +117,0,0,1,0,1 +118,0,0,1,0,1 +119,0,0,1,0,1 +120,0,0,1,0,1 +121,0,0,1,0,1 +122,0,0,1,0,1 +123,0,0,1,0,1 +124,0,0,1,0,1 +125,0,0,1,0,1 +126,0,0,1,0,1 +127,0,0,1,0,1 +128,0,0,1,0,1 +129,0,0,1,0,1 +130,0,0,1,0,1 +131,0,0,1,0,1 +132,0,0,1,0,1 +133,0,0,1,0,1 +134,0,0,1,0,1 +135,0,0,1,0,1 +136,0,0,1,0,1 +137,0,0,1,0,1 +138,0,0,1,0,1 +139,0,0,1,0,1 +140,0,0,1,0,1 +141,0,0,1,0,1 +142,0,0,1,0,1 +143,0,0,1,0,1 +144,0,0,1,0,1 +145,0,0,1,0,1 +146,0,0,1,0,1 +147,0,0,1,0,1 +148,0,0,1,0,1 +149,0,0,1,0,1 +150,0,0,1,0,1 +151,0,0,1,0,1 +152,0,0,1,0,1 +153,0,0,1,0,1 +154,0,0,1,0,1 +155,0,0,1,0,1 +156,0,0,1,0,1 +157,0,0,1,0,1 +158,0,0,1,0,1 +159,0,0,1,0,1 +160,0,0,1,0,1 +161,0,0,1,0,1 +162,0,0,1,0,1 +163,0,0,1,0,1 +164,0,0,1,0,1 +165,0,0,1,0,1 +166,0,0,1,0,1 +167,0,0,1,0,1 +168,0,0,1,0,1 +169,0,0,1,0,1 +170,0,0,1,0,1 +171,0,0,1,0,1 +172,0,0,1,0,1 +173,0,0,1,0,1 +174,0,0,1,0,1 +175,0,0,1,0,1 +176,0,0,1,0,1 +177,0,0,1,0,1 +178,0,0,1,0,1 +179,0,0,1,0,1 +180,0,0,1,0,1 +181,0,0,1,0,1 +182,0,0,1,0,1 +183,0,0,1,0,1 +184,0,0,1,0,1 +185,0,0,1,0,1 +186,0,0,1,0,1 +187,0,0,1,0,1 +188,0,0,1,0,1 +189,0,0,1,0,1 +190,0,0,1,0,1 +191,0,0,1,0,1 +192,0,0,1,0,1 +193,0,0,1,0,1 +194,0,0,1,0,1 +195,0,0,1,0,1 +196,0,0,1,0,1 +197,0,0,1,0,1 +198,0,0,1,0,1 +199,0,0,1,0,1 +200,0,0,1,0,1 +201,0,0,1,0,1 +202,0,0,1,0,1 +203,0,0,1,0,1 +204,0,0,1,0,1 +205,0,0,1,0,1 +206,0,0,1,0,1 +207,0,0,1,0,1 +208,0,0,1,0,1 +209,0,0,1,0,1 +210,0,0,1,0,1 +211,0,0,1,0,1 +212,0,0,1,0,1 +213,0,0,1,0,1 +214,0,0,1,0,1 +215,0,0,1,0,1 +216,0,0,1,0,1 +217,0,0,1,0,1 +218,0,0,1,0,1 +219,0,0,1,0,1 +220,0,0,1,0,1 +221,0,0,1,0,1 +222,0,0,1,0,1 +223,0,0,1,0,1 +224,0,0,1,0,1 +225,0,0,1,0,1 +226,0,0,1,0,1 +227,0,0,1,0,1 +228,0,0,1,0,1 +229,0,0,1,0,1 +230,0,0,1,0,1 +231,0,0,1,0,1 +232,0,0,1,0,1 +233,0,0,1,0,1 +234,0,0,1,0,1 +235,0,0,1,0,1 +236,0,0,1,0,1 +237,0,0,1,0,1 +238,0,0,1,0,1 +239,0,0,1,0,1 +240,0,0,1,0,1 +241,0,0,1,0,1 +242,0,0,1,0,1 +243,0,0,1,0,1 +244,0,0,1,0,1 +245,0,0,1,0,1 +246,0,0,1,0,1 +247,0,0,1,0,1 +248,0,0,1,0,1 +249,0,0,1,0,1 +250,0,0,1,0,1 +251,0,0,1,0,1 +252,0,0,1,0,1 +253,0,0,1,0,1 +254,0,0,1,0,1 +255,0,0,1,0,1 +256,0,0,1,0,1 +257,0,0,1,0,1 +258,0,0,1,0,1 +259,0,0,1,0,1 +260,0,0,1,0,1 +261,0,0,1,0,1 +262,0,0,1,0,1 +263,0,0,1,0,1 +264,0,0,1,0,1 +265,0,0,1,0,1 +266,0,0,1,0,1 +267,0,0,1,0,1 +268,0,0,1,0,1 +269,0,0,1,0,1 +270,0,0,1,0,1 +271,0,0,1,0,1 +272,0,0,1,0,1 +273,0,0,1,0,1 +274,0,0,1,0,1 +275,0,0,1,0,1 +276,0,0,1,0,1 +277,0,0,1,0,1 +278,0,0,1,0,1 +279,0,0,1,0,1 +280,0,0,1,0,1 +281,0,0,1,0,1 +282,0,0,1,0,1 +283,0,0,1,0,1 +284,0,0,1,0,1 +285,0,0,1,0,1 +286,0,0,1,0,1 +287,0,0,1,0,1 +288,0,0,1,0,1 +289,0,0,1,0,1 +290,0,0,1,0,1 +291,0,0,1,0,1 +292,0,0,1,0,1 +293,0,0,1,0,1 +294,0,0,1,0,1 +295,0,0,1,0,1 +296,0,0,1,0,1 +297,0,0,1,0,1 +298,0,0,1,0,1 +299,0,0,1,0,1 +300,0,0,1,0,1 +301,0,0,1,0,1 +302,0,0,1,0,1 +303,0,0,1,0,1 +304,0,0,1,0,1 +305,0,0,1,0,1 +306,0,0,1,0,1 +307,0,0,1,0,1 +308,0,0,1,0,1 +309,0,0,1,0,1 +310,0,0,1,0,1 +311,0,0,1,0,1 +312,0,0,1,0,1 +313,0,0,1,0,1 +314,0,0,1,0,1 +315,0,0,1,0,1 +316,0,0,1,0,1 +317,0,0,1,0,1 +318,0,0,1,0,1 +319,0,0,1,0,1 +320,0,0,1,0,1 +321,0,0,1,0,1 +322,0,0,1,0,1 +323,0,0,1,0,1 +324,0,0,1,0,1 +325,0,0,1,0,1 +326,0,0,1,0,1 +327,0,0,1,0,1 +328,0,0,1,0,1 +329,0,0,1,0,1 +330,0,0,1,0,1 +331,0,0,1,0,1 +332,0,0,1,0,1 +333,0,0,1,0,1 +334,0,0,1,0,1 +335,0,0,1,0,1 +336,0,0,1,0,1 +337,0,0,1,0,1 +338,0,0,1,0,1 +339,0,0,1,0,1 +340,0,0,1,0,1 +341,0,0,1,0,1 +342,0,0,1,0,1 +343,0,0,1,0,1 +344,0,0,1,0,1 +345,0,0,1,0,1 +346,0,0,1,0,1 +347,0,0,1,0,1 +348,0,0,1,0,1 +349,0,0,1,0,1 +350,0,0,1,0,1 +351,0,0,1,0,1 +352,0,0,1,0,1 +353,0,0,1,0,1 +354,0,0,1,0,1 +355,0,0,1,0,1 +356,0,0,1,0,1 +357,0,0,1,0,1 +358,0,0,1,0,1 +359,0,0,1,0,1 +360,0,0,1,0,1 +361,0,0,1,0,1 +362,0,0,1,0,1 +363,0,0,1,0,1 +364,0,0,1,0,1 +365,0,0,1,0,1 +366,0,0,1,0,1 +367,0,0,1,0,1 +368,0,0,1,0,1 +369,0,0,1,0,1 +370,0,0,1,0,1 +371,0,0,1,0,1 +372,0,0,1,0,1 +373,0,0,1,0,1 +374,0,0,1,0,1 +375,0,0,1,0,1 +376,0,0,1,0,1 +377,0,0,1,0,1 +378,0,0,1,0,1 +379,0,0,1,0,1 +380,0,0,1,0,1 +381,0,0,1,0,1 +382,0,0,1,0,1 +383,0,0,1,0,1 +384,0,0,1,0,1 +385,0,0,1,0,1 +386,0,0,1,0,1 +387,0,0,1,0,1 +388,0,0,1,0,1 +389,0,0,1,0,1 +390,0,0,1,0,1 +391,0,0,1,0,1 +392,0,0,1,0,1 +393,0,0,1,0,1 +394,0,0,1,0,1 +395,0,0,1,0,1 +396,0,0,1,0,1 +397,0,0,1,0,1 +398,0,0,1,0,1 +399,0,0,1,0,1 +400,0,0,1,0,1 +401,0,0,1,0,1 +402,0,0,1,0,1 +403,0,0,1,0,1 +404,0,0,1,0,1 +405,0,0,1,0,1 +406,0,0,1,0,1 +407,0,0,1,0,1 +408,0,0,1,0,1 +409,0,0,1,0,1 +410,0,0,1,0,1 +411,0,0,1,0,1 +412,0,0,1,0,1 +413,0,0,1,0,1 +414,0,0,1,0,1 +415,0,0,1,0,1 +416,0,0,1,0,1 +417,0,0,1,0,1 +418,0,0,1,0,1 +419,0,0,1,0,1 +420,0,0,1,0,1 +421,0,0,1,0,1 +422,0,0,1,0,1 +423,0,0,1,0,1 +424,0,0,1,0,1 +425,0,0,1,0,1 +426,0,0,1,0,1 +427,0,0,1,0,1 +428,0,0,1,0,1 +429,0,0,1,0,1 +430,0,0,1,0,1 +431,0,0,1,0,1 +432,0,0,1,0,1 +433,0,0,1,0,1 +434,0,0,1,0,1 +435,0,0,1,0,1 +436,0,0,1,0,1 +437,0,0,1,0,1 +438,0,0,1,0,1 +439,0,0,1,0,1 +440,0,0,1,0,1 +441,0,0,1,0,1 +442,0,0,1,0,1 +443,0,0,1,0,1 +444,0,0,1,0,1 +445,0,0,1,0,1 +446,0,0,1,0,1 +447,0,0,1,0,1 +448,0,0,1,0,1 +449,0,0,1,0,1 +450,0,0,1,0,1 +451,0,0,1,0,1 +452,0,0,1,0,1 +453,0,0,1,0,1 +454,0,0,1,0,1 +455,0,0,1,0,1 +456,0,0,1,0,1 +457,0,0,1,0,1 +458,0,0,1,0,1 +459,0,0,1,0,1 +460,0,0,1,0,1 +461,0,0,1,0,1 +462,0,0,1,0,1 +463,0,0,1,0,1 +464,0,0,1,0,1 +465,0,0,1,0,1 +466,0,0,1,0,1 +467,0,0,1,0,1 +468,0,0,1,0,1 +469,0,0,1,0,1 +470,0,0,1,0,1 +471,0,0,1,0,1 +472,0,0,1,0,1 +473,0,0,1,0,1 +474,0,0,1,0,1 +475,0,0,1,0,1 +476,0,0,1,0,1 +477,0,0,1,0,1 +478,0,0,1,0,1 +479,0,0,1,0,1 +480,0,0,1,0,1 +481,0,0,1,0,1 +482,0,0,1,0,1 +483,0,0,1,0,1 +484,0,0,1,0,1 +485,0,0,1,0,1 +486,0,0,1,0,1 +487,0,0,1,0,1 +488,0,0,1,0,1 +489,0,0,1,0,1 +490,0,0,1,0,1 +491,0,0,1,0,1 +492,0,0,1,0,1 +493,0,0,1,0,1 +494,0,0,1,0,1 +495,0,0,1,0,1 +496,0,0,1,0,1 +497,0,0,1,0,1 +498,0,0,1,0,1 +499,0,0,1,0,1 +500,0,0,1,0,1 +501,0,0,1,0,1 +502,0,0,1,0,1 +503,0,0,1,0,1 +504,0,0,1,0,1 +505,0,0,1,0,1 +506,0,0,1,0,1 +507,0,0,1,0,1 +508,0,0,1,0,1 +509,0,0,1,0,1 +510,0,0,1,0,1 +511,0,0,1,0,1 +512,0,0,1,0,1 +513,0,0,1,0,1 +514,0,0,1,0,1 +515,0,0,1,0,1 +516,0,0,1,0,1 +517,0,0,1,0,1 +518,0,0,1,0,1 +519,0,0,1,0,1 +520,0,0,1,0,1 +521,0,0,1,0,1 +522,0,0,1,0,1 +523,0,0,1,0,1 +524,0,0,1,0,1 +525,0,0,1,0,1 +526,0,0,1,0,1 +527,0,0,1,0,1 +528,0,0,1,0,1 +529,0,0,1,0,1 +530,0,0,1,0,1 +531,0,0,1,0,1 +532,0,0,1,0,1 +533,0,0,1,0,1 +534,0,0,1,0,1 +535,0,0,1,0,1 +536,0,0,1,0,1 +537,0,0,1,0,1 +538,0,0,1,0,1 +539,0,0,1,0,1 +540,0,0,1,0,1 +541,0,0,1,0,1 +542,0,0,1,0,1 +543,0,0,1,0,1 +544,0,0,1,0,1 +545,0,0,1,0,1 +546,0,0,1,0,1 +547,0,0,1,0,1 +548,0,0,1,0,1 +549,0,0,1,0,1 +550,0,0,1,0,1 +551,0,0,1,0,1 +552,0,0,1,0,1 +553,0,0,1,0,1 +554,0,0,1,0,1 +555,0,0,1,0,1 +556,0,0,1,0,1 +557,0,0,1,0,1 +558,0,0,1,0,1 +559,0,0,1,0,1 +560,0,0,1,0,1 +561,0,0,1,0,1 +562,0,0,1,0,1 +563,0,0,1,0,1 +564,0,0,1,0,1 +565,0,0,1,0,1 +566,0,0,1,0,1 +567,0,0,1,0,1 +568,0,0,1,0,1 +569,0,0,1,0,1 +570,0,0,1,0,1 +571,0,0,1,0,1 +572,0,0,1,0,1 +573,0,0,1,0,1 +574,0,0,1,0,1 +575,0,0,1,0,1 +576,0,0,1,0,1 +577,0,0,1,0,1 +578,0,0,1,0,1 +579,0,0,1,0,1 +580,0,0,1,0,1 +581,0,0,1,0,1 +582,0,0,1,0,1 +583,0,0,1,0,1 +584,0,0,1,0,1 +585,0,0,1,0,1 +586,0,0,1,0,1 +587,0,0,1,0,1 +588,0,0,1,0,1 +589,0,0,1,0,1 +590,0,0,1,0,1 +591,0,0,1,0,1 +592,0,0,1,0,1 +593,0,0,1,0,1 +594,0,0,1,0,1 +595,0,0,1,0,1 +596,0,0,1,0,1 +597,0,0,1,0,1 +598,0,0,1,0,1 +599,0,0,1,0,1 +600,0,0,1,0,1 +601,0,0,1,0,1 +602,0,0,1,0,1 +603,0,0,1,0,1 +604,0,0,1,0,1 +605,0,0,1,0,1 +606,0,0,1,0,1 +607,0,0,1,0,1 +608,0,0,1,0,1 +609,0,0,1,0,1 +610,0,0,1,0,1 +611,0,0,1,0,1 +612,0,0,1,0,1 +613,0,0,1,0,1 +614,0,0,1,0,1 +615,0,0,1,0,1 +616,0,0,1,0,1 +617,0,0,1,0,1 +618,0,0,1,0,1 +619,0,0,1,0,1 +620,0,0,1,0,1 +621,0,0,1,0,1 +622,0,0,1,0,1 +623,0,0,1,0,1 +624,0,0,1,0,1 +625,0,0,1,0,1 +626,0,0,1,0,1 +627,0,0,1,0,1 +628,0,0,1,0,1 +629,0,0,1,0,1 +630,0,0,1,0,1 +631,0,0,1,0,1 +632,0,0,1,0,1 +633,0,0,1,0,1 +634,0,0,1,0,1 +635,0,0,1,0,1 +636,0,0,1,0,1 +637,0,0,1,0,1 +638,0,0,1,0,1 +639,0,0,1,0,1 +640,0,0,1,0,1 +641,0,0,1,0,1 +642,0,0,1,0,1 +643,0,0,1,0,1 +644,0,0,1,0,1 +645,0,0,1,0,1 +646,0,0,1,0,1 +647,0,0,1,0,1 +648,0,0,1,0,1 +649,0,0,1,0,1 +650,0,0,1,0,1 +651,0,0,1,0,1 +652,0,0,1,0,1 +653,0,0,1,0,1 +654,0,0,1,0,1 +655,0,0,1,0,1 +656,0,0,1,0,1 +657,0,0,1,0,1 +658,0,0,1,0,1 +659,0,0,1,0,1 +660,0,0,1,0,1 +661,0,0,1,0,1 +662,0,0,1,0,1 +663,0,0,1,0,1 +664,0,0,1,0,1 +665,0,0,1,0,1 +666,0,0,1,0,1 +667,0,0,1,0,1 +668,0,0,1,0,1 +669,0,0,1,0,1 +670,0,0,1,0,1 +671,0,0,1,0,1 +672,0,0,1,0,1 +673,0,0,1,0,1 +674,0,0,1,0,1 +675,0,0,1,0,1 +676,0,0,1,0,1 +677,0,0,1,0,1 +678,0,0,1,0,1 +679,0,0,1,0,1 +680,0,0,1,0,1 +681,0,0,1,0,1 +682,0,0,1,0,1 +683,0,0,1,0,1 +684,0,0,1,0,1 +685,0,0,1,0,1 +686,0,0,1,0,1 +687,0,0,1,0,1 +688,0,0,1,0,1 +689,0,0,1,0,1 +690,0,0,1,0,1 +691,0,0,1,0,1 +692,0,0,1,0,1 +693,0,0,1,0,1 +694,0,0,1,0,1 +695,0,0,1,0,1 +696,0,0,1,0,1 +697,0,0,1,0,1 +698,0,0,1,0,1 +699,0,0,1,0,1 +700,0,0,1,0,1 +701,0,0,1,0,1 +702,0,0,1,0,1 +703,0,0,1,0,1 +704,0,0,1,0,1 +705,0,0,1,0,1 +706,0,0,1,0,1 +707,0,0,1,0,1 +708,0,0,1,0,1 +709,0,0,1,0,1 +710,0,0,1,0,1 +711,0,0,1,0,1 +712,0,0,1,0,1 +713,0,0,1,0,1 +714,0,0,1,0,1 +715,0,0,1,0,1 +716,0,0,1,0,1 +717,0,0,1,0,1 +718,0,0,1,0,1 +719,0,0,1,0,1 +720,0,0,1,0,1 +721,0,0,1,0,1 +722,0,0,1,0,1 +723,0,0,1,0,1 +724,0,0,1,0,1 +725,0,0,1,0,1 +726,0,0,1,0,1 +727,0,0,1,0,1 +728,0,0,1,0,1 +729,0,0,1,0,1 +730,0,0,1,0,1 +731,0,0,1,0,1 +732,0,0,1,0,1 +733,0,0,1,0,1 +734,0,0,1,0,1 +735,0,0,1,0,1 +736,0,0,1,0,1 +737,0,0,1,0,1 +738,0,0,1,0,1 +739,0,0,1,0,1 +740,0,0,1,0,1 +741,0,0,1,0,1 +742,0,0,1,0,1 +743,0,0,1,0,1 +744,0,0,1,0,1 +745,0,0,1,0,1 +746,0,0,1,0,1 +747,0,0,1,0,1 +748,0,0,1,0,1 +749,0,0,1,0,1 diff --git a/tests/renderer/data/masa_scene.txt b/tests/renderer/data/masa_scene.txt new file mode 100644 index 0000000000000000000000000000000000000000..b304b168d27dba00f7ab705563843e5e5dd2955b --- /dev/null +++ b/tests/renderer/data/masa_scene.txt @@ -0,0 +1,6 @@ +stv_IVASMASA_2dir2TC.pcm +1 +MASA +1 +2 +stv_IVASMASA_2dir2TC.met diff --git a/scripts/prerenderer_configs/mixed_ivas_mtdt_config.txt b/tests/renderer/data/mixed_ivas_mtdt_config.txt similarity index 93% rename from scripts/prerenderer_configs/mixed_ivas_mtdt_config.txt rename to tests/renderer/data/mixed_ivas_mtdt_config.txt index 851d507f9d534b29759f39cbdffc50a9fcc0bd2a..de4d877f73d91cc0e460cc15467ed61ae86bd35f 100644 --- a/scripts/prerenderer_configs/mixed_ivas_mtdt_config.txt +++ b/tests/renderer/data/mixed_ivas_mtdt_config.txt @@ -11,4 +11,4 @@ SBA 1 MC 7 -2 \ No newline at end of file +STEREO diff --git a/scripts/prerenderer_configs/mixed_ivas_mtdt_gain_config.txt b/tests/renderer/data/mixed_ivas_mtdt_gain_config.txt similarity index 95% rename from scripts/prerenderer_configs/mixed_ivas_mtdt_gain_config.txt rename to tests/renderer/data/mixed_ivas_mtdt_gain_config.txt index 3d4e576fcc79cdeb7f3c291b6cda4b233e3325b4..fea4161535b74e477d2ee0c15ad97a1f3b7349ac 100644 --- a/scripts/prerenderer_configs/mixed_ivas_mtdt_gain_config.txt +++ b/tests/renderer/data/mixed_ivas_mtdt_gain_config.txt @@ -13,4 +13,4 @@ SBA gain_dB:0.5 MC 7 -2 \ No newline at end of file +CICP2 diff --git a/tests/renderer/data/mixed_scene.txt b/tests/renderer/data/mixed_scene.txt new file mode 100644 index 0000000000000000000000000000000000000000..2501c75383abc8f7c27aa6b64beca353d721e33b --- /dev/null +++ b/tests/renderer/data/mixed_scene.txt @@ -0,0 +1,15 @@ +spectral_test_16ch_48kHz.wav +4 +ISM +1 +ism_0a_0e.csv +ISM +2 +1 +1,-30,0 +SBA +3 +1 +MC +7 +5_1_4 diff --git a/tests/renderer/data/mixed_scene_simple.txt b/tests/renderer/data/mixed_scene_simple.txt new file mode 100644 index 0000000000000000000000000000000000000000..85d38b8e939aac8cf9c06a5f1ef5dc780b8db2a1 --- /dev/null +++ b/tests/renderer/data/mixed_scene_simple.txt @@ -0,0 +1,12 @@ +spectral_test_4ch_48kHz.wav +3 +ISM +1 +ism_0a_0e.csv +ISM +2 +1 +1,-30,0 +MC +3 +STEREO diff --git a/scripts/prerenderer_configs/mixed_shorthand_config.txt b/tests/renderer/data/mixed_shorthand_config.txt similarity index 89% rename from scripts/prerenderer_configs/mixed_shorthand_config.txt rename to tests/renderer/data/mixed_shorthand_config.txt index edb510e23782bb8c507e4156dfc983fe8f486e97..b674960789be4cc8906902ca2240f8aea04b16f7 100644 --- a/scripts/prerenderer_configs/mixed_shorthand_config.txt +++ b/tests/renderer/data/mixed_shorthand_config.txt @@ -13,4 +13,4 @@ SBA 1 MC 7 -2 \ No newline at end of file +STEREO diff --git a/scripts/prerenderer_configs/mixed_shorthand_gain_config.txt b/tests/renderer/data/mixed_shorthand_gain_config.txt similarity index 91% rename from scripts/prerenderer_configs/mixed_shorthand_gain_config.txt rename to tests/renderer/data/mixed_shorthand_gain_config.txt index b0cd5770a2b6dd933d0cc4f0c5af9f906986d180..cb1c9fa156c06351915a3013a7a77cd0026f2a13 100644 --- a/scripts/prerenderer_configs/mixed_shorthand_gain_config.txt +++ b/tests/renderer/data/mixed_shorthand_gain_config.txt @@ -15,4 +15,5 @@ SBA gain_dB:0.5 MC 7 -2 \ No newline at end of file +STEREO + diff --git a/scripts/prerenderer_configs/mixed_shorthand_limiter_config.txt b/tests/renderer/data/mixed_shorthand_limiter_config.txt similarity index 83% rename from scripts/prerenderer_configs/mixed_shorthand_limiter_config.txt rename to tests/renderer/data/mixed_shorthand_limiter_config.txt index 7e39b337377fc440053f134f010ef77b267bd93a..55bf08aeefd925dcbbc1b77358d9a4716ddf8b4a 100644 --- a/scripts/prerenderer_configs/mixed_shorthand_limiter_config.txt +++ b/tests/renderer/data/mixed_shorthand_limiter_config.txt @@ -16,5 +16,5 @@ SBA gain_dB:20 MC 7 -2 -gain_dB:20 \ No newline at end of file +STEREO +gain_dB:20 diff --git a/scripts/prerenderer_configs/prerenderer_config_format_readme.txt b/tests/renderer/data/renderer_config_format_readme.txt similarity index 90% rename from scripts/prerenderer_configs/prerenderer_config_format_readme.txt rename to tests/renderer/data/renderer_config_format_readme.txt index 9f78ca4327cb82732a1d186e27098600aa2a108e..adf4f0136276b8c2de29a85722c39dd1d419efd7 100644 --- a/scripts/prerenderer_configs/prerenderer_config_format_readme.txt +++ b/tests/renderer/data/renderer_config_format_readme.txt @@ -31,9 +31,9 @@ *******************************************************************************************************/ -######################## Pre-renderer config file format ######################## +########################## Renderer config file format ########################## -To run, the pre-renderer requires a config file describing the input scene. +To run, the renderer requires a config file describing the input scene. The expected format of the config file is as follows: ------------------------------------ Line 1: ------------------------------------ @@ -43,12 +43,8 @@ channel 5 - an object and channels 6-11 - a 5.1 channel bed. The path given must be relative to the location of the config file. -This path has lower priority than the one given on the command line: -* The path in the config file is ignored if the --inputAudio argument to the - pre-renderer executable is specified. -* When running together with the encoder using EVS_cod_prerenderer.sh, the path in - the config file is ignored and the one provided as script argument is used - instead. +This path has lower priority than the one given on the command line: it is +ignored if the --inputAudio argument to the renderer executable is specified. ------------------------------------ Line 2: ------------------------------------ Contains number of inputs. An input can either be an Ambisonics scene, an @@ -56,10 +52,11 @@ object or a channel bed. This is NOT the total number of channels in the input audio file. -The pre-renderer currently supports simultaneously: - * Up to 2 SBA inputs - * Up to 2 MC inputs - * Up to 16 ISM inputs +The renderer currently supports simultaneously: + * 1 SBA input + * 1 MC input + * 1 MASA input + * Up to 4 ISM inputs These limits can be freely changed with pre-processor macros, if needed. @@ -78,7 +75,12 @@ Ambisonics order MC Index of the first channel of this input in the multitrack file (1-indexed) -CICP index of the speaker layout +Name of speaker layout (X_Y_Z or CICPx format) + +MASA +Index of the first channel of this input in the multitrack file (1-indexed) +Number of transport channels +Path to MASA metadata file (must be relative to config file location) ISM Index of this input's audio in the multitrack file (1-indexed) @@ -133,7 +135,7 @@ SBA gain_dB:-6 MC 5 -6 +5_1 ISM 11 2 diff --git a/tests/renderer/data/spectral_test_10ch_48kHz.wav b/tests/renderer/data/spectral_test_10ch_48kHz.wav new file mode 100644 index 0000000000000000000000000000000000000000..4f96b5075c5b35f574589788f8292cbd8e9b8958 --- /dev/null +++ b/tests/renderer/data/spectral_test_10ch_48kHz.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1dff871cc4787b53ef73920e9611938c85ab698024cb58384c29d74eb089cfe0 +size 960080 diff --git a/tests/renderer/data/spectral_test_11ch_48kHz.wav b/tests/renderer/data/spectral_test_11ch_48kHz.wav new file mode 100644 index 0000000000000000000000000000000000000000..4406f9e2fa648af72c3d1e04328695077d3efd6f --- /dev/null +++ b/tests/renderer/data/spectral_test_11ch_48kHz.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:79501e014bb57ae483875fd0626b0135d1a3f5273584acd7b8f367a332fc9228 +size 1056044 diff --git a/tests/renderer/data/spectral_test_12ch_48kHz.wav b/tests/renderer/data/spectral_test_12ch_48kHz.wav new file mode 100644 index 0000000000000000000000000000000000000000..4ee3e1d862d82116315c9408b0d08beb398e0fc5 --- /dev/null +++ b/tests/renderer/data/spectral_test_12ch_48kHz.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:17b2c1303af29e39ff72075b12b58dc0595411711bdcfdf52ebcc30074d1e987 +size 1152080 diff --git a/tests/renderer/data/spectral_test_15ch_48kHz.wav b/tests/renderer/data/spectral_test_15ch_48kHz.wav new file mode 100644 index 0000000000000000000000000000000000000000..b74d476014f974bbf6cce358b79a8c6ae4d10773 --- /dev/null +++ b/tests/renderer/data/spectral_test_15ch_48kHz.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7ab7acc0e905b617a3f1053fb1c61e8ac9f4da9c7f22d1290ad4fe457e933b35 +size 1440044 diff --git a/tests/renderer/data/spectral_test_16ch_48kHz.wav b/tests/renderer/data/spectral_test_16ch_48kHz.wav new file mode 100644 index 0000000000000000000000000000000000000000..9a367181c9832cd204fb8c4aacb5d42a251193b5 --- /dev/null +++ b/tests/renderer/data/spectral_test_16ch_48kHz.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0df9a7fc7739481190e8a04bbe31dc352481e362114fcbca827f54b7bc32a9e8 +size 1536080 diff --git a/tests/renderer/data/spectral_test_1ch_48kHz.wav b/tests/renderer/data/spectral_test_1ch_48kHz.wav new file mode 100644 index 0000000000000000000000000000000000000000..ae67b5138c51c3ea5924105fb12c773288316796 --- /dev/null +++ b/tests/renderer/data/spectral_test_1ch_48kHz.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dd111053f9742063020e4ffb0f2705971ccaa87b5fbb78bb34626e0e3f7742e7 +size 96044 diff --git a/tests/renderer/data/spectral_test_2ch_48kHz.wav b/tests/renderer/data/spectral_test_2ch_48kHz.wav new file mode 100644 index 0000000000000000000000000000000000000000..334a479916b988c6d9fd9ac24f00f9fc50d82a52 --- /dev/null +++ b/tests/renderer/data/spectral_test_2ch_48kHz.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2f612f319abdd28cf859d75ae912364fd73b2b591200e307ab6903c629bc10e4 +size 192044 diff --git a/tests/renderer/data/spectral_test_3ch_48kHz.wav b/tests/renderer/data/spectral_test_3ch_48kHz.wav new file mode 100644 index 0000000000000000000000000000000000000000..b68e211f2ef655f19f3905f8ce6c65a8f62b8d2e --- /dev/null +++ b/tests/renderer/data/spectral_test_3ch_48kHz.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:16559e4b0603e7d1079c9df98fd5763b84194e882b3e6d8086f6ddd6455bbb4f +size 288080 diff --git a/tests/renderer/data/spectral_test_4ch_48kHz.wav b/tests/renderer/data/spectral_test_4ch_48kHz.wav new file mode 100644 index 0000000000000000000000000000000000000000..6b9296e281b1c9a798605ef9c6b8f2ebfcfc6dce --- /dev/null +++ b/tests/renderer/data/spectral_test_4ch_48kHz.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:80437a0e2cbb2adb8d9b6ca8a8fab8da229d88af6a14ce64f1cd703f4645bebf +size 384080 diff --git a/tests/renderer/data/spectral_test_5ch_48kHz.wav b/tests/renderer/data/spectral_test_5ch_48kHz.wav new file mode 100644 index 0000000000000000000000000000000000000000..f76de9fb586e76ec540634d05c0bfb9f9738c0d1 --- /dev/null +++ b/tests/renderer/data/spectral_test_5ch_48kHz.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:36254bd6bbc80967d9148635286ae9b192c05c31fe8ae0f2c88203cacf1789ce +size 480044 diff --git a/tests/renderer/data/spectral_test_6ch_48kHz.wav b/tests/renderer/data/spectral_test_6ch_48kHz.wav new file mode 100644 index 0000000000000000000000000000000000000000..41ed7967c9402172e88920175139ce84921d268b --- /dev/null +++ b/tests/renderer/data/spectral_test_6ch_48kHz.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:28c44d1f897897fa8331889ab4de4464f7cee9bd80ea0936f715e4e9860b1c9f +size 576080 diff --git a/tests/renderer/data/spectral_test_8ch_48kHz.wav b/tests/renderer/data/spectral_test_8ch_48kHz.wav new file mode 100644 index 0000000000000000000000000000000000000000..6bf42f52fab1e8c68ad068d245012c8d0a084761 --- /dev/null +++ b/tests/renderer/data/spectral_test_8ch_48kHz.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:220829574ff2adebece2c38afa607f0e2df00443519c5ffe795ce6db9a329666 +size 768080 diff --git a/tests/renderer/data/spectral_test_9ch_48kHz.wav b/tests/renderer/data/spectral_test_9ch_48kHz.wav new file mode 100644 index 0000000000000000000000000000000000000000..5c7d9f2a711e6fc34e70f57cafdc002706b161a3 --- /dev/null +++ b/tests/renderer/data/spectral_test_9ch_48kHz.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a99522fe24aa91c8be4cca8aaa9b6817e85bdf3e255f8705dee1b82b4e39dbab +size 864080 diff --git a/tests/renderer/ref/.gitignore b/tests/renderer/ref/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..f935021a8f8a7bd22f9d6703cafa5134bb6a57f8 --- /dev/null +++ b/tests/renderer/ref/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/tests/renderer/run_test_renderer_vs_decoder.sh b/tests/renderer/run_test_renderer_vs_decoder.sh new file mode 100755 index 0000000000000000000000000000000000000000..215a6435b0d1489dba398c0bb7b489543a1dd71b --- /dev/null +++ b/tests/renderer/run_test_renderer_vs_decoder.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# WARNING! This script is a temporary helper, ideally these steps should be done manually and the pytest suite also run manually +cd ../../ +mkdir build +cmake -B build -G "Unix Makefiles" -DDEC_TO_REND_FLOAT_DUMP=true -DCOPY_EXECUTABLES_FROM_BUILD_DIR=true +cmake --build build -- -j +python3 -m pytest -q -n 1 -rA tests/renderer/test_renderer_vs_decoder.py + +echo "WARNING! Existing executables in root were overwritten!" diff --git a/tests/renderer/test_renderer.py b/tests/renderer/test_renderer.py new file mode 100644 index 0000000000000000000000000000000000000000..9660910afc8f4ad057e84279e0ac9172bce3a473 --- /dev/null +++ b/tests/renderer/test_renderer.py @@ -0,0 +1,244 @@ +#!/usr/bin/env python3 + +""" + (C) 2022 Baseline Development Group with portions copyright Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies OY, Orange, + Panasonic Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation. All Rights Reserved. + + This software is protected by copyright law and by international treaties. + The Baseline Development Group consisting of Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies OY, Orange, + Panasonic Corporation, Qualcomm Technologies, Inc., and VoiceAge Corporation retain full ownership + rights in their respective contributions in the software. No license of any kind, including but not + limited to patent license, of any foregoing parties is hereby granted by implication, estoppel or + otherwise. + + This software is provided "AS IS", without any express or implied warranties. The software is in the + development stage. It is intended exclusively for experts who have experience with such software and + solely for the purpose of inspection. All implied warranties of non-infringement, merchantability + and/or fitness for a particular purpose are hereby disclaimed and excluded. + + Any dispute, controversy or claim arising under or in relation to providing this software shall be + submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in + accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and + the United Nations Convention on Contracts on the International Sales of Goods. +""" + + +import pytest +from .utils import * + + +""" Ambisonics """ + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_AMBI) +def test_ambisonics(test_info, in_fmt, out_fmt): + compare_renderer_vs_pyscripts(test_info, in_fmt, out_fmt) + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_AMBI) +def test_ambisonics_binaural_static(test_info, in_fmt, out_fmt): + compare_renderer_vs_crend_unit_test(test_info, in_fmt, out_fmt) + + +@pytest.mark.parametrize("trj_file", HR_TRAJECTORIES_TO_TEST) +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_AMBI) +def test_ambisonics_binaural_headrotation(test_info, in_fmt, out_fmt, trj_file): + compare_renderer_vs_crend_unit_test( + test_info, + in_fmt, + out_fmt, + trj_file=HR_TRAJECTORY_DIR.joinpath(f"{trj_file}.csv"), + ) + + +""" Multichannel """ + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_MC) +def test_multichannel(test_info, in_fmt, out_fmt): + compare_renderer_vs_pyscripts(test_info, in_fmt, out_fmt) + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_MC) +def test_multichannel_binaural_static(test_info, in_fmt, out_fmt): + if in_fmt in ["MONO", "STEREO"]: + pytest.skip("MONO or STEREO to Binaural rendering unsupported") + + compare_renderer_vs_crend_unit_test(test_info, in_fmt, out_fmt) + + +@pytest.mark.parametrize("trj_file", HR_TRAJECTORIES_TO_TEST) +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_MC) +def test_multichannel_binaural_headrotation(test_info, in_fmt, out_fmt, trj_file): + if in_fmt in ["MONO", "STEREO"]: + pytest.skip("MONO or STEREO to Binaural rendering unsupported") + + if (in_fmt == "5_1" or in_fmt == "7_1") and out_fmt == "BINAURAL": + compare_renderer_vs_td_standalone( + test_info, + in_fmt, + out_fmt, + trj_file=HR_TRAJECTORY_DIR.joinpath(f"{trj_file}.csv"), + ) + else: + compare_renderer_vs_crend_unit_test( + test_info, + in_fmt, + out_fmt, + trj_file=HR_TRAJECTORY_DIR.joinpath(f"{trj_file}.csv"), + ) + + +""" ISM """ + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_ISM) +def test_ism(test_info, in_fmt, out_fmt): + compare_renderer_vs_pyscripts( + test_info, in_fmt, out_fmt, in_meta_files=FORMAT_TO_METADATA_FILES[in_fmt] + ) + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_ISM) +def test_ism_binaural_static(test_info, in_fmt, out_fmt): + try: + in_meta_files = FORMAT_TO_METADATA_FILES[in_fmt] + except: + in_meta_files = None + + if out_fmt == "BINAURAL": + compare_renderer_vs_td_standalone( + test_info, in_fmt, out_fmt, in_meta_files=in_meta_files + ) + else: + compare_renderer_vs_pyscripts( + test_info, in_fmt, out_fmt, in_meta_files=in_meta_files + ) + + +@pytest.mark.parametrize("trj_file", HR_TRAJECTORIES_TO_TEST) +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_ISM) +def test_ism_binaural_headrotation(test_info, in_fmt, out_fmt, trj_file): + try: + in_meta_files = FORMAT_TO_METADATA_FILES[in_fmt] + except: + in_meta_files = None + + if out_fmt == "BINAURAL": + compare_renderer_vs_td_standalone( + test_info, + in_fmt, + out_fmt, + trj_file=HR_TRAJECTORY_DIR.joinpath(f"{trj_file}.csv"), + in_meta_files=in_meta_files, + ) + else: + compare_renderer_vs_pyscripts( + test_info, + in_fmt, + out_fmt, + trj_file=HR_TRAJECTORY_DIR.joinpath(f"{trj_file}.csv"), + in_meta_files=in_meta_files, + ) + + +""" MASA """ +# # MASA inputs not supported yet +# @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) +# @pytest.mark.parametrize("in_fmt", INPUT_FORMATS_MASA) +# def test_masa(test_info, in_fmt, out_fmt): +# # TODO: implement MASA in Python, compare BE +# compare_renderer_vs_pyscripts( test_info, in_fmt, out_fmt, in_meta_files=FORMAT_TO_METADATA_FILES[in_fmt] +# ) + + +# MASA inputs not supported yet +# @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) +# @pytest.mark.parametrize("in_fmt", METADATA_SCENES_TO_TEST_NO_BE) +# def test_metadata_masa(test_info, in_fmt, out_fmt): +# # TODO: unify with test_metadata once Python supports MASA +# cut, cut_fs = run_renderer( +# "META", +# out_fmt, +# metadata_input=TEST_VECTOR_DIR.joinpath(f"{in_fmt}.txt"), +# ) + + +""" Custom loudspeaker layouts """ + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) +@pytest.mark.parametrize("in_layout", CUSTOM_LS_TO_TEST) +def test_custom_ls_input(test_info, in_layout, out_fmt): + compare_renderer_vs_pyscripts( + test_info, CUSTOM_LAYOUT_DIR.joinpath(f"{in_layout}.txt"), out_fmt + ) + + +@pytest.mark.parametrize("out_fmt", CUSTOM_LS_TO_TEST) +@pytest.mark.parametrize("in_fmt", OUTPUT_FORMATS) +def test_custom_ls_output(test_info, in_fmt, out_fmt): + compare_renderer_vs_pyscripts( + test_info, + in_fmt, + CUSTOM_LAYOUT_DIR.joinpath(f"{out_fmt}.txt"), + ) + + +@pytest.mark.parametrize("out_fmt", CUSTOM_LS_TO_TEST) +@pytest.mark.parametrize("in_fmt", CUSTOM_LS_TO_TEST) +def test_custom_ls_input_output(test_info, in_fmt, out_fmt): + compare_renderer_vs_pyscripts( + test_info, + CUSTOM_LAYOUT_DIR.joinpath(f"{in_fmt}.txt"), + CUSTOM_LAYOUT_DIR.joinpath(f"{out_fmt}.txt"), + ) + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_layout", CUSTOM_LS_TO_TEST) +def test_custom_ls_input_binaural(test_info, in_layout, out_fmt): + compare_renderer_vs_pyscripts( + test_info, + CUSTOM_LAYOUT_DIR.joinpath(f"{in_layout}.txt"), + out_fmt, + ) + + +@pytest.mark.parametrize("trj_file", HR_TRAJECTORIES_TO_TEST) +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_layout", CUSTOM_LS_TO_TEST) +def test_custom_ls_input_binaural_headrotation(test_info, in_layout, out_fmt, trj_file): + compare_renderer_vs_pyscripts( + test_info, + CUSTOM_LAYOUT_DIR.joinpath(f"{in_layout}.txt"), + out_fmt, + trj_file=HR_TRAJECTORY_DIR.joinpath(f"{trj_file}.csv"), + ) + + +""" Metadata / scene description input """ + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) +@pytest.mark.parametrize("in_fmt", METADATA_SCENES_TO_TEST) +def test_metadata(test_info, in_fmt, out_fmt): + compare_renderer_vs_pyscripts( + test_info, + "META", + out_fmt, + metadata_input=TEST_VECTOR_DIR.joinpath(f"{in_fmt}.txt"), + ) diff --git a/tests/renderer/test_renderer_be_comparison.py b/tests/renderer/test_renderer_be_comparison.py new file mode 100644 index 0000000000000000000000000000000000000000..5d89136a890dbdd0298fde5f18d94be8276d8929 --- /dev/null +++ b/tests/renderer/test_renderer_be_comparison.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python3 + +""" + (C) 2022 Baseline Development Group with portions copyright Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies OY, Orange, + Panasonic Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation. All Rights Reserved. + + This software is protected by copyright law and by international treaties. + The Baseline Development Group consisting of Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies OY, Orange, + Panasonic Corporation, Qualcomm Technologies, Inc., and VoiceAge Corporation retain full ownership + rights in their respective contributions in the software. No license of any kind, including but not + limited to patent license, of any foregoing parties is hereby granted by implication, estoppel or + otherwise. + + This software is provided "AS IS", without any express or implied warranties. The software is in the + development stage. It is intended exclusively for experts who have experience with such software and + solely for the purpose of inspection. All implied warranties of non-infringement, merchantability + and/or fitness for a particular purpose are hereby disclaimed and excluded. + + Any dispute, controversy or claim arising under or in relation to providing this software shall be + submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in + accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and + the United Nations Convention on Contracts on the International Sales of Goods. +""" + + +import pytest +from .utils import * + + +""" Ambisonics """ + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_AMBI) +def test_ambisonics(test_info, in_fmt, out_fmt): + compare_renderer_vs_mergetarget(test_info, in_fmt, out_fmt) + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_AMBI) +def test_ambisonics_binaural_static(test_info, in_fmt, out_fmt): + compare_renderer_vs_mergetarget(test_info, in_fmt, out_fmt) + + +@pytest.mark.parametrize("trj_file", HR_TRAJECTORIES_TO_TEST) +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_AMBI) +def test_ambisonics_binaural_headrotation(test_info, in_fmt, out_fmt, trj_file): + compare_renderer_vs_mergetarget( + test_info, + in_fmt, + out_fmt, + trj_file=HR_TRAJECTORY_DIR.joinpath(f"{trj_file}.csv"), + ) + + +""" Multichannel """ + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_MC) +def test_multichannel(test_info, in_fmt, out_fmt): + compare_renderer_vs_mergetarget(test_info, in_fmt, out_fmt) + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_MC) +def test_multichannel_binaural_static(test_info, in_fmt, out_fmt): + if in_fmt in ["MONO", "STEREO"]: + pytest.skip("MONO or STEREO to Binaural rendering unsupported") + + compare_renderer_vs_mergetarget(test_info, in_fmt, out_fmt) + + +@pytest.mark.parametrize("trj_file", HR_TRAJECTORIES_TO_TEST) +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_MC) +def test_multichannel_binaural_headrotation(test_info, in_fmt, out_fmt, trj_file): + if in_fmt in ["MONO", "STEREO"]: + pytest.skip("MONO or STEREO to Binaural rendering unsupported") + + compare_renderer_vs_mergetarget( + test_info, + in_fmt, + out_fmt, + trj_file=HR_TRAJECTORY_DIR.joinpath(f"{trj_file}.csv"), + ) + + +""" ISM """ + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_ISM) +def test_ism(test_info, in_fmt, out_fmt): + compare_renderer_vs_mergetarget( + test_info, in_fmt, out_fmt, in_meta_files=FORMAT_TO_METADATA_FILES[in_fmt] + ) + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_ISM) +def test_ism_binaural_static(test_info, in_fmt, out_fmt): + try: + in_meta_files = FORMAT_TO_METADATA_FILES[in_fmt] + except: + in_meta_files = None + + compare_renderer_vs_mergetarget( + test_info, in_fmt, out_fmt, in_meta_files=in_meta_files + ) + + +@pytest.mark.parametrize("trj_file", HR_TRAJECTORIES_TO_TEST) +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_ISM) +def test_ism_binaural_headrotation(test_info, in_fmt, out_fmt, trj_file): + try: + in_meta_files = FORMAT_TO_METADATA_FILES[in_fmt] + except: + in_meta_files = None + + compare_renderer_vs_mergetarget( + test_info, + in_fmt, + out_fmt, + trj_file=HR_TRAJECTORY_DIR.joinpath(f"{trj_file}.csv"), + in_meta_files=in_meta_files, + ) + + +""" MASA """ +# TODO: MASA inputs not supported yet + +""" Custom loudspeaker layouts """ + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) +@pytest.mark.parametrize("in_layout", CUSTOM_LS_TO_TEST) +def test_custom_ls_input(test_info, in_layout, out_fmt): + compare_renderer_vs_mergetarget( + test_info, CUSTOM_LAYOUT_DIR.joinpath(f"{in_layout}.txt"), out_fmt + ) + + +@pytest.mark.parametrize("out_fmt", CUSTOM_LS_TO_TEST) +@pytest.mark.parametrize("in_fmt", OUTPUT_FORMATS) +def test_custom_ls_output(test_info, in_fmt, out_fmt): + compare_renderer_vs_mergetarget( + test_info, in_fmt, CUSTOM_LAYOUT_DIR.joinpath(f"{out_fmt}.txt") + ) + + +@pytest.mark.parametrize("out_fmt", CUSTOM_LS_TO_TEST) +@pytest.mark.parametrize("in_fmt", CUSTOM_LS_TO_TEST) +def test_custom_ls_input_output(test_info, in_fmt, out_fmt): + compare_renderer_vs_mergetarget( + test_info, + CUSTOM_LAYOUT_DIR.joinpath(f"{in_fmt}.txt"), + CUSTOM_LAYOUT_DIR.joinpath(f"{out_fmt}.txt"), + ) + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_layout", CUSTOM_LS_TO_TEST) +def test_custom_ls_input_binaural(test_info, in_layout, out_fmt): + compare_renderer_vs_mergetarget( + test_info, + CUSTOM_LAYOUT_DIR.joinpath(f"{in_layout}.txt"), + out_fmt, + ) + + +@pytest.mark.parametrize("trj_file", HR_TRAJECTORIES_TO_TEST) +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_layout", CUSTOM_LS_TO_TEST) +def test_custom_ls_input_binaural_headrotation(test_info, in_layout, out_fmt, trj_file): + compare_renderer_vs_mergetarget( + test_info, + CUSTOM_LAYOUT_DIR.joinpath(f"{in_layout}.txt"), + out_fmt, + trj_file=HR_TRAJECTORY_DIR.joinpath(f"{trj_file}.csv"), + ) + + +""" Metadata / scene description input """ + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) +@pytest.mark.parametrize("in_fmt", METADATA_SCENES_TO_TEST) +def test_metadata(test_info, in_fmt, out_fmt): + compare_renderer_vs_mergetarget( + test_info, + "META", + out_fmt, + metadata_input=TEST_VECTOR_DIR.joinpath(f"{in_fmt}.txt"), + ) diff --git a/tests/renderer/test_renderer_vs_decoder.py b/tests/renderer/test_renderer_vs_decoder.py new file mode 100644 index 0000000000000000000000000000000000000000..cd88580d9b9644c056a2498dd78ed1ab646deff0 --- /dev/null +++ b/tests/renderer/test_renderer_vs_decoder.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 + +""" + (C) 2022 Baseline Development Group with portions copyright Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies OY, Orange, + Panasonic Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation. All Rights Reserved. + + This software is protected by copyright law and by international treaties. + The Baseline Development Group consisting of Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies OY, Orange, + Panasonic Corporation, Qualcomm Technologies, Inc., and VoiceAge Corporation retain full ownership + rights in their respective contributions in the software. No license of any kind, including but not + limited to patent license, of any foregoing parties is hereby granted by implication, estoppel or + otherwise. + + This software is provided "AS IS", without any express or implied warranties. The software is in the + development stage. It is intended exclusively for experts who have experience with such software and + solely for the purpose of inspection. All implied warranties of non-infringement, merchantability + and/or fitness for a particular purpose are hereby disclaimed and excluded. + + Any dispute, controversy or claim arising under or in relation to providing this software shall be + submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in + accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and + the United Nations Convention on Contracts on the International Sales of Goods. +""" + +from .utils import * + +""" Ambisonics """ + + +@pytest.mark.skip("Ambisonics comparison requires CLDFB interface") +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_AMBI) +def test_ambisonics_vs_decoder(test_info, in_fmt, out_fmt): + compare_renderer_vs_decoder(test_info, in_fmt, out_fmt) + + +@pytest.mark.skip("Ambisonics comparison requires CLDFB interface") +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_AMBI) +def test_ambisonics_binaural_static_vs_decoder(test_info, in_fmt, out_fmt): + compare_renderer_vs_decoder(test_info, in_fmt, out_fmt) + + +@pytest.mark.skip("Ambisonics comparison requires CLDFB interface") +@pytest.mark.parametrize("trj_file", HR_TRAJECTORIES_TO_TEST) +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_AMBI) +def test_ambisonics_binaural_headrotation_vs_decoder( + test_info, in_fmt, out_fmt, trj_file +): + compare_renderer_vs_decoder( + test_info, + in_fmt, + out_fmt, + trj_file=HR_TRAJECTORY_DIR.joinpath(f"{trj_file}.csv"), + ) + + +""" Multichannel """ + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_MC) +def test_multichannel_vs_decoder(test_info, in_fmt, out_fmt): + if in_fmt == "MONO": + pytest.skip("(EVS) Mono decoder rendering unsupported") + + if in_fmt == "STEREO" and out_fmt in ["FOA", "HOA2", "HOA3"]: + pytest.skip("Stereo to Ambisonics rendering unsupported in the decoder") + + compare_renderer_vs_decoder(test_info, in_fmt, out_fmt) + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_MC) +def test_multichannel_binaural_static_vs_decoder(test_info, in_fmt, out_fmt): + if in_fmt in ["MONO", "STEREO"]: + pytest.skip("MONO or STEREO to Binaural rendering unsupported") + + compare_renderer_vs_decoder(test_info, in_fmt, out_fmt) + + +@pytest.mark.parametrize("trj_file", HR_TRAJECTORIES_TO_TEST) +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_MC) +def test_multichannel_binaural_headrotation_vs_decoder( + test_info, in_fmt, out_fmt, trj_file +): + if in_fmt in ["MONO", "STEREO"]: + pytest.skip("MONO or STEREO to Binaural rendering unsupported") + + compare_renderer_vs_decoder( + test_info, + in_fmt, + out_fmt, + trj_file=HR_TRAJECTORY_DIR.joinpath(f"{trj_file}.csv"), + ) + + +""" ISM """ + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_ISM) +def test_ism_vs_decoder(test_info, in_fmt, out_fmt): + compare_renderer_vs_decoder(test_info, in_fmt, out_fmt) + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_ISM) +def test_ism_binaural_static_vs_decoder(test_info, in_fmt, out_fmt): + compare_renderer_vs_decoder(test_info, in_fmt, out_fmt) + + +@pytest.mark.parametrize("trj_file", HR_TRAJECTORIES_TO_TEST) +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_ISM) +def test_ism_binaural_headrotation_vs_decoder(test_info, in_fmt, out_fmt, trj_file): + compare_renderer_vs_decoder( + test_info, + in_fmt, + out_fmt, + trj_file=HR_TRAJECTORY_DIR.joinpath(f"{trj_file}.csv"), + ) diff --git a/tests/renderer/utils.py b/tests/renderer/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..8a4ecf739b67d5f199ec54f296cfbff1e7b019eb --- /dev/null +++ b/tests/renderer/utils.py @@ -0,0 +1,452 @@ +#!/usr/bin/env python3 + +""" + (C) 2022 Baseline Development Group with portions copyright Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies OY, Orange, + Panasonic Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation. All Rights Reserved. + + This software is protected by copyright law and by international treaties. + The Baseline Development Group consisting of Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies OY, Orange, + Panasonic Corporation, Qualcomm Technologies, Inc., and VoiceAge Corporation retain full ownership + rights in their respective contributions in the software. No license of any kind, including but not + limited to patent license, of any foregoing parties is hereby granted by implication, estoppel or + otherwise. + + This software is provided "AS IS", without any express or implied warranties. The software is in the + development stage. It is intended exclusively for experts who have experience with such software and + solely for the purpose of inspection. All implied warranties of non-infringement, merchantability + and/or fitness for a particular purpose are hereby disclaimed and excluded. + + Any dispute, controversy or claim arising under or in relation to providing this software shall be + submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in + accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and + the United Nations Convention on Contracts on the International Sales of Goods. +""" + +import logging +import subprocess as sp +import sys +from pathlib import Path +from tempfile import TemporaryDirectory +from typing import Optional, Tuple + +import numpy as np +import pytest + + +from .compare_audio import compare_audio_arrays +from .constants import * + +sys.path.append(SCRIPTS_DIR) +import pyaudio3dtools + +# fixture returns test information, enabling per-testcase SNR +@pytest.fixture +def test_info(request): + return request + + +def run_cmd(cmd): + logging.info(f"\nRunning command\n{' '.join(cmd)}\n") + try: + sp.run(cmd, check=True, capture_output=True, text=True) + except sp.CalledProcessError as e: + raise SystemError( + f"Command returned non-zero exit status ({e.returncode}): {' '.join(e.cmd)}\n{e.stderr}\n{e.stdout}" + ) + + +def check_BE( + test_info, + ref: np.ndarray, + ref_fs: int, + cut: np.ndarray, + cut_fs: int, +): + + if ref is None or np.array_equal(ref, np.zeros_like(ref)): + pytest.fail("REF signal does not exist or is zero!") + + if cut is None or np.array_equal(cut, np.zeros_like(cut)): + pytest.fail("CuT signal does not exist or is zero!") + + snr, gain_b, max_diff = compare_audio_arrays(ref, ref_fs, cut, cut_fs) + + if np.isnan(snr) or gain_b == 0: + pytest.fail("Invalid comparison result, check your signals!") + + # try to get a minimum SNR from the config + if test_info.node.name in pass_snr: + snr_min = pass_snr.get(test_info.node.name) + else: + snr_min = np.inf + + # TODO temporary fix to pad TD Object Renderer Standalone output + if ref.shape != cut.shape: + ref = np.pad(ref, [(0, cut.shape[0] - ref.shape[0]), (0, 0)]) + + # check max_diff as well, since compare_audio_arrays will try to adjust for small delay differences + if not np.allclose(ref, cut, rtol=0, atol=2) and max_diff > 2: + if snr >= snr_min: + pytest.xfail( + f"Expected failure with minimum SNR {snr_min} vs {snr:3.2f}dB, Gain CuT: {gain_b:1.3f}, Max Diff = {int(max_diff)}" + ) + else: + pytest.fail( + f"CuT not BE to REF! SNR : {snr:3.2f} dB, Gain CuT: {gain_b:1.3f}, Max Diff = {int(max_diff)}" + ) + + +def run_enc( + in_fmt: str, + bit_file: str, + in_meta_files: Optional[list] = None, +) -> None: + + cmd = IVAS_COD_CMD[:] + cmd[1] = FORMAT_TO_IVAS[in_fmt][0] + cmd[2] = FORMAT_TO_IVAS[in_fmt][1] + cmd[3] = FORMAT_TO_IVAS_BR[in_fmt] + cmd[5] = str(FORMAT_TO_FILE[in_fmt]) + cmd[6] = bit_file + + if in_meta_files is not None: + cmd[3:3] = [*in_meta_files] + + if in_fmt == "MONO": + cmd.pop(1) + cmd.pop(1) + elif in_fmt == "STEREO": + cmd.pop(2) + + run_cmd(cmd) + + +def run_dec( + bit_file: str, + out_file: str, + out_fmt: str, + trj_file: Optional[str] = None, +) -> Tuple[np.ndarray, int]: + + cmd = IVAS_DEC_CMD[:] + cmd[2] = out_fmt + cmd[4] = bit_file + cmd[5] = out_file + + if trj_file is not None and out_fmt in ["BINAURAL", "BINAURAL_ROOM"]: + cmd[1:1] = ["-T", str(trj_file)] + + run_cmd(cmd) + + return pyaudio3dtools.audiofile.readfile(out_file) + + +def run_renderer( + in_fmt: str, + out_fmt: str, + metadata_input: Optional[str] = None, + in_meta_files: Optional[list] = None, + trj_file: Optional[str] = None, + output_path_base: str = OUTPUT_PATH_CUT, + binary_suffix: str = "", +) -> Tuple[np.ndarray, int]: + """CuT creation with standalone renderer""" + if trj_file is not None: + trj_name = f"_{trj_file.stem}" + else: + trj_name = "" + + if not isinstance(out_fmt, str): + out_name = f"{out_fmt.stem}" + else: + out_name = out_fmt + + if metadata_input is not None: + in_file = metadata_input + in_name = metadata_input.stem + elif not isinstance(in_fmt, str): + in_file = FORMAT_TO_FILE[in_fmt.stem] + in_name = in_fmt.stem + else: + in_file = FORMAT_TO_FILE[in_fmt] + in_name = in_fmt + + out_file = str(output_path_base.joinpath(f"{in_name}_to_{out_name}{trj_name}.wav")) + + + cmd = RENDERER_CMD[:] + cmd[2] = str(in_file) + cmd[4] = str(in_fmt) + cmd[6] = str(out_file) + cmd[8] = str(out_fmt) + + cmd[0] += binary_suffix + + if in_meta_files is not None: + cmd[5:5] = ["-im", *in_meta_files] + + if trj_file is not None: + cmd.extend(["-tf", str(trj_file)]) + + run_cmd(cmd) + + return pyaudio3dtools.audiofile.readfile(out_file) + + +def run_renderer_ext( + in_file: str, + in_fmt: str, + out_fmt: str, + in_meta_files: Optional[list] = None, + trj_file: Optional[str] = None, +) -> Tuple[np.ndarray, int]: + """Run renderer with decoder float dump""" + if trj_file is not None: + trj_name = f"_{trj_file.stem}" + else: + trj_name = "" + + if not isinstance(out_fmt, str): + out_name = f"{out_fmt.stem}" + else: + out_name = out_fmt + + in_name = in_fmt + + out_file = str(OUTPUT_PATH_CUT.joinpath(f"{in_name}_to_{out_name}{trj_name}.wav")) + + cmd = RENDERER_CMD[:] + cmd[2] = str(in_file) + cmd[4] = str(in_fmt) + cmd[6] = str(out_file) + cmd[8] = str(out_fmt) + + if in_meta_files is not None: + cmd[5:5] = ["-im", *in_meta_files] + + if trj_file is not None: + cmd.extend(["-tf", str(trj_file)]) + + run_cmd(cmd) + + return pyaudio3dtools.audiofile.readfile(out_file) + + +def run_crend_unittest( + in_fmt: str, + out_fmt: str, + metadata_input: Optional[str] = None, + in_meta_files: Optional[list] = None, + trj_file: Optional[str] = None, +) -> Tuple[np.ndarray, int]: + """Reference creation with standalone renderer""" + if trj_file is not None: + trj_name = f"_{trj_file.stem}" + else: + trj_name = "" + + if not isinstance(out_fmt, str): + out_name = f"{out_fmt.stem}" + else: + out_name = out_fmt + + if metadata_input is not None: + in_file = metadata_input + in_name = metadata_input.stem + elif not isinstance(in_fmt, str): + in_file = FORMAT_TO_FILE[in_fmt.stem] + in_name = in_fmt.stem + else: + in_file = FORMAT_TO_FILE[in_fmt] + in_name = in_fmt + + out_file = str(OUTPUT_PATH_REF.joinpath(f"{in_name}_to_{out_name}{trj_name}.wav")) + + cmd = CREND_CMD[:] + cmd[6] = FORMAT_TO_CREND_FORMAT[str(in_fmt)] + cmd[8] = FORMAT_TO_CREND_FORMAT[str(out_fmt)] + cmd[10] = str(in_file) + cmd[12] = str(out_file) + if out_fmt == "BINAURAL_ROOM": + cmd.append("-brir") + + if trj_file is not None: + cmd.extend(["-t", str(trj_file)]) + cmd.extend(["-otr", "REF"]) + + run_cmd(cmd) + + return pyaudio3dtools.audiofile.readfile(out_file) + + +def run_td_standalone( + in_fmt: str, + out_fmt: str, + metadata_input: Optional[str] = None, + in_meta_files: Optional[list] = None, + trj_file: Optional[str] = None, +): + """Reference creation with TD Object renderer""" + if trj_file is not None: + trj_name = f"_{trj_file.stem}" + else: + trj_name = "" + + if not isinstance(out_fmt, str): + out_name = f"{out_fmt.stem}" + else: + out_name = out_fmt + + if metadata_input is not None: + in_file = metadata_input + in_name = metadata_input.stem + elif not isinstance(in_fmt, str): + in_file = FORMAT_TO_FILE[in_fmt.stem] + in_name = in_fmt.stem + else: + in_file = FORMAT_TO_FILE[in_fmt] + in_name = in_fmt + + out_file = str(OUTPUT_PATH_REF.joinpath(f"{in_name}_to_{out_name}{trj_name}.pcm")) + + in_spfmt = pyaudio3dtools.spatialaudioformat.Format(in_fmt) + + with TemporaryDirectory() as tmp_dir: + # write PCM tmp file + tmp_dir = Path(tmp_dir) + in_file_pcm = tmp_dir.joinpath(in_file.stem + ".pcm") + + in_sig, _ = pyaudio3dtools.audiofile.readfile(in_file) + pyaudio3dtools.audiofile.writefile(in_file_pcm, in_sig) + + cmd = TDRENDERER_CMD[:] + cmd[2] = str(in_file_pcm) + cmd[3] = str(out_file) + + if in_spfmt.isloudspeaker: + cmd[1:1] = ["-mc", in_spfmt.name] + else: + cmd[1:1] = str(in_spfmt.nchannels) + if in_meta_files is not None: + cmd[2:2] = in_meta_files + else: + cmd[2:2] = ["NULL"] * in_spfmt.nchannels + + if trj_file is not None: + cmd[1:1] = ["-T", str(trj_file)] + + run_cmd(cmd) + + return pyaudio3dtools.audiofile.readfile(out_file, nchannels=2) + + +def run_pyscripts( + in_fmt, + out_fmt, + metadata_input: Optional[str] = None, + in_meta_files: Optional[list] = None, + trj_file: Optional[str] = None, +) -> Tuple[np.ndarray, int]: + """Reference creation with pyaudio3dtools""" + if trj_file is not None: + trj_name = f"_{trj_file.stem}" + else: + trj_name = "" + + if not isinstance(out_fmt, str): + out_name = f"{out_fmt.stem}" + else: + out_name = out_fmt + + if metadata_input is not None: + in_file = metadata_input + in_name = metadata_input.stem + elif isinstance(in_fmt, Path): + in_file = FORMAT_TO_FILE[in_fmt.stem] + in_name = in_fmt.stem + else: + in_file = FORMAT_TO_FILE[in_fmt] + in_name = in_fmt + + out_file = str(OUTPUT_PATH_REF.joinpath(f"{in_name}_to_{out_name}{trj_name}.wav")) + + pyaudio3dtools.spatialaudioconvert.spatial_audio_convert( + in_file, + out_file, + in_format=in_fmt, + out_format=out_fmt, + in_meta_files=in_meta_files, + trajectory=trj_file, + limit_output=True, + ) + + return pyaudio3dtools.audiofile.readfile(out_file) + + +def compare_renderer_vs_mergetarget(test_info, in_fmt, out_fmt, **kwargs): + ref, ref_fs = run_renderer( + in_fmt, out_fmt, binary_suffix=BIN_SUFFIX_MERGETARGET, output_path_base=OUTPUT_PATH_REF, **kwargs + ) + cut, cut_fs = run_renderer(in_fmt, out_fmt, **kwargs) + check_BE(test_info, ref, ref_fs, cut, cut_fs) + + +def compare_renderer_vs_pyscripts(test_info, in_fmt, out_fmt, **kwargs): + ref, ref_fs = run_pyscripts(in_fmt, out_fmt, **kwargs) + cut, cut_fs = run_renderer(in_fmt, out_fmt, **kwargs) + check_BE(test_info, ref, ref_fs, cut, cut_fs) + + +def compare_renderer_vs_crend_unit_test(test_info, in_fmt, out_fmt, **kwargs): + ref, ref_fs = run_crend_unittest(in_fmt, out_fmt, **kwargs) + cut, cut_fs = run_renderer(in_fmt, out_fmt, **kwargs) + check_BE(test_info, ref, ref_fs, cut, cut_fs) + + +def compare_renderer_vs_td_standalone(test_info, in_fmt, out_fmt, **kwargs): + ref, ref_fs = run_td_standalone(in_fmt, out_fmt, **kwargs) + cut, cut_fs = run_renderer(in_fmt, out_fmt, **kwargs) + check_BE(test_info, ref, ref_fs, cut, cut_fs) + + +def compare_renderer_vs_decoder(test_info, in_fmt, out_fmt, **kwargs): + with TemporaryDirectory() as tmp_dir: + tmp_dir = Path(tmp_dir) + + in_meta_files = None + bit_file = str(tmp_dir.joinpath(f"{in_fmt}_to_{out_fmt}.192")) + out_file_decoder = str(OUTPUT_PATH_REF.joinpath(f"{in_fmt}_to_{out_fmt}.wav")) + + # Ref: cod -> dec (out_fmt) + if in_fmt in FORMAT_TO_METADATA_FILES.keys(): + in_meta_files = FORMAT_TO_METADATA_FILES[in_fmt] + + # encoder + run_enc(in_fmt, bit_file, in_meta_files=in_meta_files) + # decoder renderer + ref, ref_fs = run_dec(bit_file, out_file_decoder, out_fmt, **kwargs) + + # CuT cod -> dec (in_fmt) -> rend + if in_fmt in FORMAT_TO_METADATA_FILES.keys(): + tmp_fmt = "EXT" + in_meta_files = [ + str(tmp_dir.joinpath(f"{in_fmt}_to_EXT.wav.{n}.csv")) + for n in range(int(in_fmt[-1])) + ] + else: + tmp_fmt = in_fmt + + out_file_ext = str(tmp_dir.joinpath(f"{in_fmt}_to_{tmp_fmt}.wav")) + + # passthrough decoder + run_dec(bit_file, out_file_ext, tmp_fmt, **kwargs) + # external renderer + cut, cut_fs = run_renderer_ext( + out_file_ext, in_fmt, out_fmt, in_meta_files=in_meta_files, **kwargs + ) + + check_BE(test_info, ref, ref_fs, cut, cut_fs)