diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml deleted file mode 100644 index 25be91a1..00000000 --- a/.github/workflows/docs.yml +++ /dev/null @@ -1,57 +0,0 @@ -name: Documentation - -on: - schedule: - # run at 5am every day - - cron: '0 5 * * *' - push: - paths: - - '.github/workflows/docs.yml' - - 'settings.json' - - 'doc/exclude_patterns.inc' - - '**/doc/**' - pull_request: - paths: - - '.github/workflows/docs.yml' - - 'settings.json' - - 'doc/exclude-patterns.inc' - - '**/doc/**' - - # Allow manually triggering the workflow. - workflow_dispatch: {} - -env: - XCORE_DOC_BUILDER: 'ghcr.io/xmos/doc_builder:v3.0.0' - -jobs: - build_documentation: - name: Build and package documentation - if: github.repository_owner == 'xmos' - runs-on: ubuntu-latest - steps: - - name: Checkout this repo - uses: actions/checkout@v3 - - - uses: actions/setup-python@v4 - with: - python-version: '3.10.x' - - - name: Pull doc_builder container - run: | - docker pull ${XCORE_DOC_BUILDER} - - - name: Build documentation - run: | - pwd - docker run --user "$(id -u):$(id -g)" --rm -v ${{ github.workspace }}:/build -e EXCLUDE_PATTERNS="/build/doc/exclude-patterns.inc" -e OUTPUT_DIR="/build/doc/_build" -e PDF=1 -e SKIP_LINK=1 -e DOXYGEN_INCLUDE=/build/doc/Doxyfile.inc -e DOXYGEN_INPUT=ignore ${XCORE_DOC_BUILDER} - tree - DOC_VERSION=$(grep version settings.json | grep -o "[0-9]*\.[0-9]*\.[0-9]") - mv doc/_build/pdf/programming_guide.pdf doc/_build/pdf/programming_guide_v${DOC_VERSION}.pdf - - - name: Save documentation artifacts - uses: actions/upload-artifact@v3 - with: - name: docs lib_src - path: doc/_build - if-no-files-found: error # 'warn' or 'ignore' are also available, defaults to `warn` - retention-days: 30 diff --git a/.gitignore b/.gitignore index 4e187a4b..5409e72c 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,6 @@ tests/vpu_ff3_test/autogen tests/vpu_rat_test/autogen *.csv python/lib_src.egg-info +**/tmp_models/ +*.bak +make_docs.sh \ No newline at end of file diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 967b1116..ca5d7db5 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,16 @@ lib_src change log ================== +2.4.0 +----- + + * ADDED: Support for building the core ASRC code in the C emulator as a + library + * ADDED: Auto-generated ASRC and SSRC performance plots in documentation + * CHANGED: Documents built under Jenkins instead of Github Actions + * ADDED: Documentation warning about overflow in XS3 optimized SRC components + * CHANGED: Tested against fwk_core v1.0.2 updated from v1.0.0 + 2.3.0 ----- diff --git a/CMakeLists.txt b/CMakeLists.txt index bb1ed5e7..af416b1a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ if(PROJECT_IS_TOP_LEVEL) FetchContent_Declare( fwk_core GIT_REPOSITORY https://github.com/xmos/fwk_core.git - GIT_TAG v1.0.0 + GIT_TAG v1.0.2 ) FetchContent_MakeAvailable(fwk_core) endif() diff --git a/Jenkinsfile b/Jenkinsfile index 4b9e3175..491024fe 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -17,17 +17,17 @@ def localRunPytest(String extra_args="") { getApproval() pipeline { - agent { - label 'x86_64&&macOS' // These agents have 24 cores so good for parallel xsim runs - } + agent none environment { REPO = 'lib_src' VIEW = getViewName(REPO) PYTHON_VERSION = "3.10.5" VENV_DIRNAME = ".venv" + XMOSDOC_VERSION = "v4.0" } options { skipDefaultCheckout() + timestamps() } parameters { string( @@ -37,119 +37,142 @@ pipeline { ) } stages { - stage('Get repo') { - steps { - sh "mkdir ${REPO}" - // source checks require the directory - // name to be the same as the repo name - dir("${REPO}") { - // checkout repo - checkout scm - sh 'git submodule update --init --recursive --depth 1' - } + stage('Build and Test') { + when { + expression { !env.GH_LABEL_DOC_ONLY.toBoolean() } } - } - stage ("Create Python environment") - { - steps { - dir("${REPO}") { - createVenv('requirements.txt') - withVenv { - sh 'pip install -r requirements.txt' + agent { + label 'x86_64&&docker' // These agents have 24 cores so good for parallel xsim runs + } + stages { + stage('Get repo') { + steps { + sh "mkdir ${REPO}" + // source checks require the directory + // name to be the same as the repo name + dir("${REPO}") { + // checkout repo + checkout scm + sh 'git submodule update --init --recursive --depth 1' + } } } - } - } - stage('Library checks') { - steps { - dir("${REPO}") { - sh 'git clone git@github.com:xmos/infr_apps.git' - sh 'git clone git@github.com:xmos/infr_scripts_py.git' - // These are needed for xmake legacy build and also changelog check - sh 'git clone git@github.com:xmos/lib_logging.git' - sh 'git clone git@github.com:xmos/lib_xassert.git' - withVenv { - sh 'pip install -e infr_scripts_py' - sh 'pip install -e infr_apps' - dir("tests") { - withEnv(["XMOS_ROOT=.."]) { - localRunPytest('-s test_lib_checks.py -vv') + stage ("Create Python environment") { + steps { + dir("${REPO}") { + createVenv('requirements.txt') + withVenv { + sh 'pip install -r requirements.txt' } } } } - } - } - stage('Test xmake build') { - steps { - runningOn(env.NODE_NAME) - dir("${REPO}") { - withTools(params.TOOLS_VERSION) { - withVenv { - dir("tests") { - localRunPytest('-k "legacy" -vv') + stage('Library checks') { + steps { + dir("${REPO}") { + sh 'git clone git@github.com:xmos/infr_apps.git' + sh 'git clone git@github.com:xmos/infr_scripts_py.git' + // These are needed for xmake legacy build and also changelog check + sh 'git clone git@github.com:xmos/lib_logging.git' + sh 'git clone git@github.com:xmos/lib_xassert.git' + withVenv { + sh 'pip install -e infr_scripts_py' + sh 'pip install -e infr_apps' + dir("tests") { + withEnv(["XMOS_ROOT=.."]) { + localRunPytest('-s test_lib_checks.py -vv') + } + } } } } } - } - } - stage('Tests XS2') { - steps { - runningOn(env.NODE_NAME) - dir("${REPO}") { - withTools(params.TOOLS_VERSION) { - withVenv { - sh 'mkdir build' - dir("build") { - sh 'rm -rf' - sh 'cmake --toolchain ../xmos_cmake_toolchain/xs2a.cmake ..' - sh 'make test_ds3_voice test_us3_voice test_unity_gain_voice -j' + stage('Test xmake build') { + steps { + runningOn(env.NODE_NAME) + dir("${REPO}") { + withTools(params.TOOLS_VERSION) { + withVenv { + dir("tests") { + localRunPytest('-k "legacy" -vv') + } + } } - dir("tests") { - localRunPytest('-n auto -k "xs2" -vv') + } + } + } + stage('Run doc python') { + steps { + runningOn(env.NODE_NAME) + dir("${REPO}") { + withTools(params.TOOLS_VERSION) { + withVenv { + sh "sh doc/build_docs_ci.sh $XMOSDOC_VERSION" + archiveArtifacts artifacts: "doc_build.zip", allowEmptyArchive: true + } } - dir("build") { - sh 'rm -rf' // Cleanup XS2 cmake cache for next stage + } + } + } + stage('Tests XS2') { + steps { + runningOn(env.NODE_NAME) + dir("${REPO}") { + withTools(params.TOOLS_VERSION) { + withVenv { + sh 'mkdir -p build' + dir("build") { + sh 'rm CMakeCache.txt' + sh 'cmake --toolchain ../xmos_cmake_toolchain/xs2a.cmake ..' + sh 'make test_ds3_voice test_us3_voice test_unity_gain_voice -j' + } + dir("tests") { + localRunPytest('-n auto -k "xs2" -vv') + } + dir("build") { + sh 'rm CMakeCache.txt' // Cleanup XS2 cmake cache for next stage + } + } } } } } - } - } - stage('Tests XS3') { - steps { - runningOn(env.NODE_NAME) - dir("${REPO}") { - withTools(params.TOOLS_VERSION) { - withVenv { - dir("tests") { - localRunPytest('-m prepare') // Do all pre work like building and generating golden ref where needed + stage('Tests XS3') { + steps { + runningOn(env.NODE_NAME) + dir("${REPO}") { + withTools(params.TOOLS_VERSION) { + withVenv { + dir("tests") { + localRunPytest('-m prepare') // Do all pre work like building and generating golden ref where needed - // FF3 HiFi tests for OS3 and DS3 - localRunPytest('-m main -n auto -k "hifi_ff3" -vv') + // FF3 HiFi tests for OS3 and DS3 + localRunPytest('-m main -n auto -k "hifi_ff3" -vv') - // ASRC and SSRC tests across all in/out freqs and deviations (asrc only) - localRunPytest('-m main -n auto -k "mrhf" -vv') - archiveArtifacts artifacts: "mips_report*.csv", allowEmptyArchive: true + // ASRC and SSRC tests across all in/out freqs and deviations (asrc only) + localRunPytest('-m main -n auto -k "mrhf" -vv') + archiveArtifacts artifacts: "mips_report*.csv", allowEmptyArchive: true - // VPU enabled ff3 and rat tests - localRunPytest('-m main -k "vpu" -vv') // xdist not working yet so no -n auto + // VPU enabled ff3 and rat tests + localRunPytest('-m main -k "vpu" -vv') // xdist not working yet so no -n auto - // Profile the ASRC - localRunPytest('-m main -k "profile_asrc" -vv') - sh 'tree' - archiveArtifacts artifacts: "gprof_results/*.png", allowEmptyArchive: true + // Profile the ASRC + localRunPytest('-m main -k "profile_asrc" -vv') + sh 'tree' + archiveArtifacts artifacts: "gprof_results/*.png", allowEmptyArchive: true + } + } } } } } } + post { + cleanup { + xcoreCleanSandbox() + } + } } - } - post { - cleanup { - xcoreCleanSandbox() - } + } } diff --git a/README.rst b/README.rst index f837efb8..09ed3250 100644 --- a/README.rst +++ b/README.rst @@ -60,6 +60,12 @@ There are three different component options that support fixed factor of 3 up/do #. If voice quality (65 dB SNR) is required running on xCORE-200, use ds3_voice or us3_voice. #. If voice quality (75 dB SNR) is required running xcore-ai, use ff3_96t_ds or ff3_96t_us. +.. warning:: + Synchronous fixed factor of 3 and 3/2 downsample and oversample functions for voice applications optimized for the XS3 Vector Processing Unit + currently overflow rather than saturate in cases where a full scale input causes a perturbation above full scale at the output. + To avoid this scenario, please ensure that the input amplitude is always 3.5 dB below full scale. + The overflow behavior of these SRC components will be replaced by saturating behavior (to match all other SRC components) in a future release. + Related Application Notes ......................... diff --git a/doc/build_docs_ci.sh b/doc/build_docs_ci.sh new file mode 100644 index 00000000..b5bcbaf4 --- /dev/null +++ b/doc/build_docs_ci.sh @@ -0,0 +1,21 @@ +# This script is designed to be run by the CI under a venv set by requirements.txt in the repo root. +# Take a look at the Jenkinsfile for the general idea. +# It should be run as /doc/build_docs_ci.sh from the repo root as the cwd. + +if [ -z "$1" ] + then + echo "Please pass the xmosdoc version. Eg. $/doc/build_docs_ci.sh v4.0" + exit -1 +fi + +# Build the autogenerated section +cd doc/python +python doc_asrc.py +cd ../.. + +# Create the docs from source +docker pull ghcr.io/xmos/xmosdoc:$1 +docker run -u "$(id -u):$(id -g)" --rm -v `pwd`:/build ghcr.io/xmos/xmosdoc:$1 -v + +# Leave a nice zip file for picking up as an artefact +zip -r doc_build.zip doc/_build diff --git a/doc/exclude-patterns.inc b/doc/exclude_patterns.inc similarity index 78% rename from doc/exclude-patterns.inc rename to doc/exclude_patterns.inc index e568e88a..934f156d 100644 --- a/doc/exclude-patterns.inc +++ b/doc/exclude_patterns.inc @@ -1,14 +1,15 @@ # The following patterns are to be excluded from the documentation build tests/* build/* +**LICENSE* # Do not build .md files *.md **/*.md +lib_logging/** +lib_xassert/** # We need to ensure these are not processed twice which breaks figure numbering. They are included via index.rst. -**/multi_rate_hifi_src.rst -**/fixed_ratio_src.rst **/resource_usage_asrc.rst -**/resource_usage_ssrc.rst \ No newline at end of file +**/resource_usage_ssrc.rst diff --git a/doc/rst/programming_guide/fixed_ratio_src.rst b/doc/programming_guide/fixed_ratio_src.rst similarity index 92% rename from doc/rst/programming_guide/fixed_ratio_src.rst rename to doc/programming_guide/fixed_ratio_src.rst index 4225a6e7..615f8875 100644 --- a/doc/rst/programming_guide/fixed_ratio_src.rst +++ b/doc/programming_guide/fixed_ratio_src.rst @@ -121,7 +121,15 @@ They have been designed for voice applications and, in particular, conformance t .. note:: These filters will only run on XCORE-AI due to the inner dot product calculation employing the XS3 VPU. +.. warning:: + Synchronous fixed factor of 3 and 3/2 downsample and oversample functions for voice applications optimized for the XS3 Vector Processing Unit + currently overflow rather than saturate in cases where a full scale input causes a perturbation above full scale at the output. + To avoid this scenario, please ensure that the input amplitude is always 3.5 dB below full scale. + The overflow behavior of these SRC components will be replaced by saturating behavior (to match all other SRC components) in a future release. + + .. _ff3_voice_vpu_hdr: + Fixed factor of 3 VPU --------------------- @@ -200,14 +208,14 @@ The fixed factor of 3/2 VPU sample rate converts use a rational factor polyphase - Ripple - Attenuation - Taps - * - src_ff3_96t_ds + * - src_rat_2_3_96t_ds - 112 - 0.46875 - 0.53125 - 0.03 dB - 70 dB - 96 - * - src_ff3_96t_us + * - src_rat_3_2_96t_us - 95 - 0.46875 - 0.53125 diff --git a/doc/rst/programming_guide/images/asrc_structure.pdf b/doc/programming_guide/images/asrc_structure.pdf similarity index 100% rename from doc/rst/programming_guide/images/asrc_structure.pdf rename to doc/programming_guide/images/asrc_structure.pdf diff --git a/doc/rst/programming_guide/images/quad_dual_instance.pdf b/doc/programming_guide/images/quad_dual_instance.pdf similarity index 100% rename from doc/rst/programming_guide/images/quad_dual_instance.pdf rename to doc/programming_guide/images/quad_dual_instance.pdf diff --git a/doc/rst/programming_guide/images/src_ff3_vpu.png b/doc/programming_guide/images/src_ff3_vpu.png similarity index 100% rename from doc/rst/programming_guide/images/src_ff3_vpu.png rename to doc/programming_guide/images/src_ff3_vpu.png diff --git a/doc/rst/programming_guide/images/src_ff3_vpu_pb.png b/doc/programming_guide/images/src_ff3_vpu_pb.png similarity index 100% rename from doc/rst/programming_guide/images/src_ff3_vpu_pb.png rename to doc/programming_guide/images/src_ff3_vpu_pb.png diff --git a/doc/rst/programming_guide/images/src_proc.pdf b/doc/programming_guide/images/src_proc.pdf similarity index 100% rename from doc/rst/programming_guide/images/src_proc.pdf rename to doc/programming_guide/images/src_proc.pdf diff --git a/doc/rst/programming_guide/images/src_rat_vpu.png b/doc/programming_guide/images/src_rat_vpu.png similarity index 100% rename from doc/rst/programming_guide/images/src_rat_vpu.png rename to doc/programming_guide/images/src_rat_vpu.png diff --git a/doc/rst/programming_guide/images/src_rat_vpu_pb.png b/doc/programming_guide/images/src_rat_vpu_pb.png similarity index 100% rename from doc/rst/programming_guide/images/src_rat_vpu_pb.png rename to doc/programming_guide/images/src_rat_vpu_pb.png diff --git a/doc/rst/programming_guide/images/ssrc_structure.pdf b/doc/programming_guide/images/ssrc_structure.pdf similarity index 100% rename from doc/rst/programming_guide/images/ssrc_structure.pdf rename to doc/programming_guide/images/ssrc_structure.pdf diff --git a/doc/rst/programming_guide/images/stereo_dual_instance.pdf b/doc/programming_guide/images/stereo_dual_instance.pdf similarity index 100% rename from doc/rst/programming_guide/images/stereo_dual_instance.pdf rename to doc/programming_guide/images/stereo_dual_instance.pdf diff --git a/doc/rst/programming_guide/images/stereo_single_instance.pdf b/doc/programming_guide/images/stereo_single_instance.pdf similarity index 100% rename from doc/rst/programming_guide/images/stereo_single_instance.pdf rename to doc/programming_guide/images/stereo_single_instance.pdf diff --git a/doc/programming_guide/index.rst b/doc/programming_guide/index.rst new file mode 100644 index 00000000..0e8c0b41 --- /dev/null +++ b/doc/programming_guide/index.rst @@ -0,0 +1,9 @@ +Programming Guide +################# + +.. toctree:: + + ../../README + multi_rate_hifi_src + fixed_ratio_src + ../../CHANGELOG diff --git a/doc/rst/programming_guide/multi_rate_hifi_src.rst b/doc/programming_guide/multi_rate_hifi_src.rst similarity index 89% rename from doc/rst/programming_guide/multi_rate_hifi_src.rst rename to doc/programming_guide/multi_rate_hifi_src.rst index 0a7cc6da..6afed6df 100644 --- a/doc/rst/programming_guide/multi_rate_hifi_src.rst +++ b/doc/programming_guide/multi_rate_hifi_src.rst @@ -46,11 +46,11 @@ There is an initialization call which sets up the variables within the structure Initialization ensures the correct selection, ordering and configuration of the filtering stages, be they decimators, interpolators or pass-through blocks. This initialization call contains arguments defining selected input and output nominal sample rates as well as settings for the sample rate converter: -.. doxygenfunction:: ssrc_init +:c:func:`ssrc_init` The initialization call is the same for ASRC: -.. doxygenfunction:: asrc_init +:c:func:`asrc_init` The input block size must be a power of 2 and is set by the ``n_in_samples`` argument. In the case where more than one channel is to be processed per SRC instance, the total number of input samples expected for each processing call is ``n_in_samples * n_channels_per_instance``. @@ -75,11 +75,11 @@ The logic is designed so that the final filtering stage always receives a sample The processing function call is passed the input and output buffers and a reference to the control structure: -.. doxygenfunction:: ssrc_process +:c:func:`ssrc_process` In the case of ASRC a fractional frequency ratio argument is also supplied: -.. doxygenfunction:: asrc_process +:c:func:`asrc_process` The SRC processing call always returns a whole number of output samples produced by the sample rate conversion. Depending on the sample ratios selected, this number may be between zero and ``(n_in_samples * n_channels_per_instance * SRC_N_OUT_IN_RATIO_MAX)``. ``SRC_N_OUT_IN_RATIO_MAX`` is the maximum number of output samples for a single input sample. For example, if the input frequency is 44.1 kHz and the output rate is 192 kHz then a sample rate conversion of one sample input may produce up to 5 output samples. @@ -144,28 +144,7 @@ The performance of the SSRC library is as follows: * SNR: 140 dB (or better). Note that when dither is not used, SNR is infinite as output from a zero input signal is zero. -The performance was analyzed by converting output test files to 32 bit integer ``wav`` files. These files were then run through an audio analysis tool (WinAudio MLS: http://www.dr-jordan-design.de/Winaudiomls.htm). - -Figures :numref:`fig_fft_44_192`, :numref:`fig_fft_176_48` and :numref:`fig_fft_96_88` are a series of FFT plots showing the most demanding rate conversion case. These clearly show that the above targets are comfortably exceeded. All outputs have been generated using 8192 samples at input sampling rate. A Kaiser-Bessel window with alpha=7 has been used. - -.. _fig_fft_44_192: -.. figure:: images/ssrc_fft_44_192.pdf - :width: 90% - - FFT of 1 kHz sine, 0 dB, 44.1 kHz to 192 kHz - -.. _fig_fft_176_48: -.. figure:: images/ssrc_fft_176_48.pdf - :width: 90% - - FFT of 1 kHz sine, 0 dB, 176.4 kHz to 48 kHz - -.. _fig_fft_96_88: -.. figure:: images/ssrc_fft_96_88.pdf - :width: 90% - - FFT of 10 kHz + 11 kHz sines, -6 dB, 96 kHz to 88.2 kHz - +To see frequency plots illustrating the noise floor with respect to a sample rate converted tone please refer to the :ref:`performance-plots` section of this document. SSRC Resource utilization @@ -185,28 +164,7 @@ The performance of the ASRC library is as follows: * THD+N: (1 kHz, 0 dBFs): better than -130 dB * SNR: 135 dB (or better). Note that when dither is not used, SNR is infinite as output from a zero input signal is zero. -The performance was analyzed by converting output test files to 32 bit integer ``wav`` files. These files were then run through an audio analysis tool (WinAudio MLS: http://www.dr-jordan-design.de/Winaudiomls.htm). - -Figures :numref:`fig_fft_44_192_a`, :numref:`fig_fft_176_48_a` and :numref:`fig_fft_96_88_a` are a series of FFT plots showing the most demanding rate conversion case. These clearly show that the above targets are comfortably exceeded. All outputs have been generated using 8192 samples at input sampling rate. A Kaiser-Bessel window with alpha=7 has been used. - -.. _fig_fft_44_192_a: -.. figure:: images/asrc_fft_44_192.pdf - :width: 90% - - FFT of 1 kHz sine, 0 dB, 44.1 kHz to 192 kHz - -.. _fig_fft_176_48_a: -.. figure:: images/asrc_fft_176_48.pdf - :width: 90% - - FFT of 1 kHz sine, 0 dB, 176.4 kHz to 48 kHz - -.. _fig_fft_96_88_a: -.. figure:: images/asrc_fft_96_88.pdf - :width: 90% - - FFT of 10 kHz + 11 kHz sines, -6 dB, 96 kHz to 88.2 kHz - +To see frequency plots illustrating the noise floor with respect to a sample rate converted tone please refer to the :ref:`performance-plots` section of this document. ASRC Resource utilization @@ -477,4 +435,4 @@ ASRC API -------- .. doxygengroup:: src_asrc - :content-only: \ No newline at end of file + :content-only: diff --git a/doc/rst/programming_guide/resource_usage_asrc.rst b/doc/programming_guide/resource_usage_asrc.rst similarity index 100% rename from doc/rst/programming_guide/resource_usage_asrc.rst rename to doc/programming_guide/resource_usage_asrc.rst diff --git a/doc/rst/programming_guide/resource_usage_ssrc.rst b/doc/programming_guide/resource_usage_ssrc.rst similarity index 100% rename from doc/rst/programming_guide/resource_usage_ssrc.rst rename to doc/programming_guide/resource_usage_ssrc.rst diff --git a/doc/python/README.txt b/doc/python/README.txt new file mode 100644 index 00000000..66c70a37 --- /dev/null +++ b/doc/python/README.txt @@ -0,0 +1,36 @@ +INTRODUCTION +============ + +This folder includes a python script which runs the C emulators for the ASRC, SSRC, OS3 and DS3 modules. +For each model test signals are injected - both a single tone to test the THD performance and a range of tones to +observe roll-off at the top of the band. Using an un-windowed FFT the SNR and THD is calculated. +The logs are scraped to extract MIPS and utilization where supported. + +Tests are run for all suported input and output sample freqencies, and for worst case frequency deviation. +Note that not all modules support all input scenarios due to input and output rate specifications - these cases are ignored. + +The result of this script is the generation an `rst` file which is included into the documentation, +incorporating a full set of charts and summary table. + + + +REQUIREMENTS +============ + +Python 3.9 or above. Make sure the dependencies listed in lib_src/requirements.txt are installed. + + cd ../../ + pip install -r requirements.txt + + + +To run the script: + + python3 doc_asrc.py + + +By default, this will create a "_build" folder, containing sub-folders: + ./input all generated test files + ./expected simulation output from the models + ./output plots and a csv format log file + ./rst a single rst file which incorporates all the plots and log file as a table diff --git a/doc/python/asrc_utils.py b/doc/python/asrc_utils.py new file mode 100644 index 00000000..4e145fc3 --- /dev/null +++ b/doc/python/asrc_utils.py @@ -0,0 +1,506 @@ +# Copyright 2023 XMOS LIMITED. +# This Software is subject to the terms of the XMOS Public Licence: Version 1. +from subprocess import Popen, PIPE +from subprocess import run as srun +import os +import shutil +import matplotlib.pyplot as plt +from scipy.fft import fft, ifft +import scipy.signal as signal +import numpy as np +from scipy.io import wavfile +import re +from math import gcd +from mpmath import mp +from scipy.io.wavfile import write as writewav +from pathlib import Path +import subprocess + + +class asrc_util: + def __init__(self, path, relative, fftPoints, ch0_bins, ch1_bins): + self.path = str(path) + self.relative = relative + + # locations for results - note they are purged every time this is run + self.cleanFolder("_build") # create temp location for results + self.outputFolder = self.cleanFolder("_build/output") # put all results of simulation and plots here + self.inputFolder = self.cleanFolder("_build/input") # put any input test files here + self.expectedFolder = self.cleanFolder("_build/expected") # put C ref model o/p here + self.rstFolder = self.cleanFolder("_build/rst") # put RST o/p here + + self.logFile = "/log.csv" # tabulated results + + # the C-models and XSIM models which get run + self.asrc_model = self.build_model_exe("asrc") + self.ssrc_model = self.build_model_exe("ssrc") + self.ds3_model = self.build_model_exe("ds3") + self.os3_model = self.build_model_exe("os3") + + # definitions of sample ratea + self.allRates =["16", "32", "44", "48", "88", "96", "176", "192"] + self.srcRates =["44", "48", "88", "96", "176", "192"] # supported by the asrc and ssrc + self.factors = {"44":0, "48":1, "88":2, "96":3, "176":4, "192":5} # look up to convert rate to the filter bank id needed by the ASRC + self.sampleRates = {"16":16000, "32":32000, "44":44100, "48":48000, "88":88200, "96":96000, "176":176400, "192":192000} #convenience to save typing out sample rates in full + self.sigMax = {"16":7300, "32":14600, "44":18000, "48":21800, "88":40000, "96":42000, "176":80000, "192":85000} # upper limit on input freq for given sample rate. Note that these need to be well within Nyquist point to prevent aliasing. + + self.numSamples = {} #populated later based on op-rate to ensure sufficient samples for fft + self.ignoreSamples = 2000 #worst case for high up-sampling + self.fftPoints = fftPoints + + self.sig = [ch0_bins, ch1_bins] # defines the test signals for each of 2 channels, each is a list so more than one tone can be generated and combined + self.log=[] + self.plots=[] + mp.prec = 1024 + self.fudge = 0.5**128 + self.plot_data = [] + self.plot_label = [] + self.plot_text = [] + self.skip_xsim = True # FIX THIS LATER + self.rstFile = "" + + def build_model_exe(self, target): + file_dir = Path(__file__).resolve().parent + build_path = file_dir / "../../build" + build_path.mkdir(exist_ok=True) + subprocess.run("rm -rf CMakeCache.txt CMakeFiles/", shell=True, cwd=str(build_path)) + subprocess.run("cmake ..", shell=True, cwd=str(build_path)) + bin_path = file_dir / f"{target}_/model" + subprocess.run(f"make {target}_golden", shell=True, cwd=str(build_path)) + + return f"{build_path}/tests/{target}_test/{target}_golden" + + + + # update the input frequencies + # allows input of frequencies as a list for each channel, or use the autoFill which + # populates channel 1 with mutiple tones, spaced logrithmically, most dense at higher freq, and ch0 is always 10% in + # set in 'sigMax' beyond which reflections degrade the SNR. + def updateSig(self, ipRate, ch0_bins, ch1_bins, autoFill=False): + # this manipulates the fft size to attempt to force any reflections around the ip sample rate nyquist also ending up + # in an integer fft bin + self.fftPoints = int(10000 *(self.sampleRates[ipRate] * self.fDev) / self.sampleRates[self.opRate]) + print(f"Over-wrote the fft size to {self.fftPoints} points. ipRate = {ipRate}") + + #for channel 0 + ch0_ratio = 0.05 # 5% of the way into the fft bins, so 10% on the plot + ch0bin = int(self.fftPoints * ch0_ratio) + print("For channel 0: Creating test tone at FFT bin: {}".format(ch0bin)) + # for channel 1 + sigMax = self.sigMax # upper limit on ip frequency to be safely inside the filter cutoff + maxBin = int((min(sigMax[ipRate], sigMax[self.opRate]) / (self.sampleRates[self.opRate])) * self.fftPoints) + i=1 + r=2 # spread factor + ch1_range = [maxBin] + while maxBin-r**i > 0: # this bit spreads the bins out exponentially, concentrated at the top + ch1_range.append(int(maxBin-r**i)) + i+=1 + print("For channel 1: Creating data set for ipRate {} and opRate {} which resulted in\n{}\n".format(ipRate, self.opRate, ch1_range )) + if autoFill: + self.sig = [[ch0bin], ch1_range] + else: + self.sig = [ch0_bins, ch1_bins] + + + + # handy util to add data into an array + def pushPlotInfo(self, a,b,c): + self.plot_data.append(a) + self.plot_label.append(b) + self.plot_text.append(c) + + def resetPlotInfo(self): + self.plot_data = [] + self.plot_label = [] + self.plot_text = [] + + def makePlotTitle(self, simLog, channel): + firstKey = list(simLog[channel].keys())[0] + title = simLog[channel][firstKey][0].replace(self.expectedFolder, '')[1:] + title = title.split('_')[:-1] + for k in simLog[channel]: title.append(k) + title = "_".join(title) + return title + + + def logResults(self, source, ipRate, opRate, fDev, ch, SNR, THD, totalmips, ch0mips=None, ch1mips=None, txt=None, snr2=None, mips2=None): + # simple function to append information to a log, which is kept as an array of dictionaries + realRate = self.sampleRates[opRate] / self.fDev + sigList = [] + for s in self.sig[ch]: + sigList.append("{:.3f}".format(float(mp.fmul(realRate ,mp.fdiv(s, self.fftPoints))))) + signals = ";".join(sigList) + #info = {"source":source, "ipRate(Hz)":self.sampleRates[ipRate], "opRate(Hz)":self.sampleRates[opRate], "fDev":fDev, "ch":ch, "signals(Hz)":signals, "SNR(dB)":SNR, "THD(dB)":THD,"Total MIPS":totalmips, "MIPS(ch0)":ch0mips, "MIPS(ch1)":ch1mips, "Text":txt} + info = {"source":source, "ipRate(Hz)":str(self.sampleRates[ipRate]), "opRate(Hz)":str(self.sampleRates[opRate]), "fDev":str(fDev), "ch":str(ch), "signals(Hz)":signals, "SNR(dB)":"{:.1f}".format(SNR), "THD(dB)":THD} + self.log.append(info) + + + def log2csv(self): + # export the log array as a csv file, using the keys as headings + logfile = open(self.outputFolder + self.logFile, "w") + logfile.write(",".join(self.log[0].keys()) + "\n") # write headings + for i in self.log: + logfile.write((','.join(i.values())).replace("\n", ";") + "\n") # write data + logfile.close() + + + def setOpRate(self, opRate, fDev): + self.opRate = opRate + self.fDev = fDev + + def cleanFolder(self, folder): + # utility to delete and recreate folders used for input and output files + p = "{}/{}".format(self.path, folder) + if os.path.isdir(p): + shutil.rmtree(p) + os.mkdir(p) + if self.relative: + p = p.replace(self.path, ".") + return p + + def listFactors(self, x): + factors=[] + for i in range(1,x+1): + if x%i==0: + factors.append(i) + return factors + + def makeSignal(self, fsamp, fsig, asig, l, ferr=1.0): + # populates ipdata with sinewave + # fsamp: sample rate, as abbreviated string (e.g., "48", interpreted as 48,000Hz) + # fsig: signal freq, in Hz + # asig: amplitude, in range 0-1 + # l: number of samples + fsamphz = mp.fmul(mp.fmul(float(self.sampleRates[fsamp]), self.fDev), ferr) + f = mp.fdiv(fsig, fsamphz) + print("Make signal: {:.2f}".format(float(fsig))) + a = asig * 2**31 + x = np.linspace(0, float(2*mp.pi*f*(l-1)), l) + ipdata = a * np.sin(x) + return ipdata + + + def saveInputFile(self, signals, fileName): + # sums the signals into one signal and saves as a file + # signals: an array of arrays [data0[], data[1], ...] + # return the file with path + data = np.sum(signals, axis=0).astype('int32') + file = self.inputFolder + "/" + fileName + np.savetxt(file, data, fmt='%d', delimiter='\n') + #writewav("/mnt/c/Users/andrewdewhurst/OneDrive - Xmos/Temp/rawdata/test.wav", 44100, (data[0]/2**16).astype(np.int16)) + return file, data + + def makeInputFiles(self, test_rates, ferr=1.0): + # create two channels of input + # define some test signal frequencies w.r.t. op rate + realRate = self.sampleRates[self.opRate] # mp.fdiv(self.sampleRates[self.opRate], self.fDev) + # + ipFiles=np.empty((0,3), str) + for ipRate in test_rates: + self.numSamples[ipRate] = int( ((self.sampleRates[ipRate]/self.sampleRates[self.opRate]) * ((self.fftPoints*1.5) + self.ignoreSamples ))) + chan=[] + chanSigData=[] + i=0 + for ch in self.sig: #iterates over channels + sig=[] + for s in ch: + f = mp.fmul(realRate ,mp.fdiv(s, self.fftPoints)) + data = self.makeSignal(ipRate, f, 0.98/len(ch), self.numSamples[ipRate], ferr) + sig.append([data]) + #freqs = "-".join(str(round(realRate * x / self.fftPoints)) for x in ch)[0:32] #crop filename + if len(ch)==1: + freqs = str(round(realRate * ch[0] / self.fftPoints)) + else: + freqs = str(round(realRate * ch[0] / self.fftPoints)) + "_to_" + str(round(realRate * ch[-1] / self.fftPoints)) + if ferr==1: + filename = "ch{}_{}_fsi{}.dat".format(str(i), freqs, ipRate) + else: + filename = "ch{}_{}_fsi{}_err{}.dat".format(str(i), freqs, ipRate, ferr) + print("Making {} with F={:.6f} using {} points".format(filename, float(f), self.numSamples[ipRate])) + sigFile, sigData = self.saveInputFile(sig, filename) + chan.append(sigFile) + chanSigData.append(sigData) + i+=1 + ipFiles = np.append(ipFiles, np.array([[chan[0], chan[1], ipRate]]), axis=0) + return ipFiles, chanSigData + + + def doFFT(self, data, window=False): + # convenient way to select between fft styles. Note that the periodic one will need a lot more samples, so + # use window=True for debuging. + if window: + return self.winFFT(data) + else: + return self.rawFFT(data) + + + def mpAbs(self, data): + result = np.zeros(len(data)) + for i in range(0, len(data)): + result[i] = mp.fabs(data[i]) + return result + + + def rawFFT(self, data): + # fft assuming signal is already an integer fraction of the true output rate + min = np.argmin(np.abs(data[self.ignoreSamples:-(self.fftPoints)])) + self.ignoreSamples #investigating if picking a data set that looks like it is nearly zero crossing at the ends is better + samples = data[min:min+self.fftPoints] + l=self.fftPoints + fftData = self.mpAbs(np.fft.fft(samples)) + fftDataDB = 20 * np.log10((fftData/np.max(fftData)) + self.fudge) + realRate = self.sampleRates[self.opRate] * float(self.fDev) + x = np.linspace(0,realRate/2, num=int(l/2) ) / 1000 + return [x, fftDataDB[0:int(l/2)], fftData[0:int(l/2)]/np.max(fftData[0:int(l/2)])] + + + def plotFFT(self, xydata, combine=False, title=None, subtitles=None, log=True, text=None): + # Plot style setup for the FFT plots, labels x asis as KHz and y axis as dB. + # xydata: an array of datasets, each dataset is a 3 element array containing the xdata array and ydata_dB array and ydata_lin array + # combine: (optional) forces all plots onto the same chart, otherwise it will create a grid of plots + # title: (optional) the chart title at the top, common to any subplots + # subtitles: (optional) an array of subtitles, used for each subplot. + # log: plots the dB data in the input + grid = {1:[1,1], 2:[1,2], 3:[1,3], 4:[2,2], 5:[2,3], 6:[2,3]} + clut = ['lawngreen','deepskyblue','orange','dimgray'] + slut = [(0,(1,1)), (0,(5,1))] if combine else ['solid'] + n = len(xydata) + if n in grid and not(combine): # preffered layout of multiple subplots + [prows, pcols] = grid[n] + else: + prows = int(1 if combine else int(n/3) + 1) + pcols = int(1 if combine else np.ceil(n/prows)) + fig = plt.figure(figsize=(6*pcols, 6*prows)) + i = 0 + for xy in xydata: + if not(combine) or i<1: + ax = fig.add_subplot(prows, pcols, i+1) + ax.plot(xy[0], xy[1] if log else xy[2], linestyle=slut[i%len(slut)], color=clut[i%len(clut)], linewidth=1, label=subtitles[i]) + ax.set_xlabel("Frequency (KHz)") + ax.set_ylabel("dB") + ax.set_ylim(-250,0) + ax.grid() + if text != None: + tl = np.sum([text[x].count("\n") + 1 for x in range(i)]) if combine else 0 # this stacks up the text comments in the case of combined plots + ax.text(0.95, 0.95-(0.025*tl), text[i], transform=ax.transAxes, fontsize=6, fontweight='normal', va='top', horizontalalignment='right') + if subtitles != None and not(combine) and i<=n: + ax.set_title(subtitles[i], fontsize=8, x=0, y=1.0, ha='left') #subtitle above subplots when not combined + i += 1 + fig.suptitle(title, fontsize=12, x=0.5, y=0.97, ha='center') # title top centre + if title != None: + filename = title + "-combined" if combine else title + else: + "-".join(subtitles) + "-combined" if combine else "-".join(subtitles) + if combine: + fig.legend(loc='lower left') + plotFile = "{}/{}.png".format(self.outputFolder, filename) + plt.savefig(plotFile, dpi=100) + plt.close() + return "{}.png".format(filename) + + + def opFileName(self, fin, label, fDev, opRate): + #a simple utility to convert an input file path+name to an output version, appending the frequency deviation + newName = fin.replace(self.inputFolder, self.outputFolder).replace(".dat", "_fso{}_fdev{:f}_{}.dat".format(opRate, fDev, label)) + return newName + + def exFileName(self, fin, label, fDev, opRate): + #a simple utility to convert an input file path+name to an output version, appending the frequency deviation + newName = fin.replace(self.inputFolder, self.expectedFolder).replace(".dat", "_fso{}_fdev{:f}_{}.dat".format(opRate, fDev, label)) + return newName + + + def run_c_model(self, ipFiles, opRate, blocksize, fDev): + # Runs the various simulators across the input files, for the provided opRate and frequency deviation + # ipFiles: an array of arrays, each sub array in format [Ch0 file name, Ch1 filename, ipRate] + # generates the output filename based on input and returns another array of files in the same format as the input files. + # also returns the "simLog" which is a dictionary which links every input file to all available output results for that input, + # since we can't run all sims on all data (e.g. DS3 needs a factor of 3) + dither=0 + opFiles=np.empty((0,6), str) + simLog = {} + for ipFile in ipFiles: + simLog[ipFile[0]]={} + simLog[ipFile[1]]={} + + # ASRC C Model + # ------------ + if ipFile[2] in self.srcRates and self.opRate in self.srcRates: + ch0 = self.exFileName(ipFile[0], 'c-asrc', fDev, opRate) + ch1 = self.exFileName(ipFile[1], 'c-asrc', fDev, opRate) + + cmd = "{} -i{} -j{} -k{} -o{} -p{} -q{} -d{} -l{} -n{} -e{}".format( + self.asrc_model, ipFile[0], ipFile[1] , self.factors[ipFile[2]], ch0, ch1, self.factors[opRate], dither, self.numSamples[ipFile[2]], blocksize, fDev) + p = Popen([cmd], stdin=PIPE, stdout=PIPE, shell=True) + output, error = p.communicate(input=b'\n') + opFiles = np.append(opFiles, np.array([[ch0, ch1, opRate, str(fDev), output, "c-asrc"]]), axis=0) + simLog[ipFile[0]]['asrc']=[str(ch0), 0, opRate, str(fDev), output, "c-asrc"] + simLog[ipFile[1]]['asrc']=[str(ch1), 1, opRate, str(fDev), output, "c-asrc"] + + + # SSRC C Model + # ------------ + if ipFile[2] in self.srcRates and self.opRate in self.srcRates and fDev==1: + ch0 = self.exFileName(ipFile[0], 'c-ssrc', fDev, opRate) + ch1 = self.exFileName(ipFile[1], 'c-ssrc', fDev, opRate) + + cmd = "{} -i{} -j{} -k{} -o{} -p{} -q{} -d{} -l{} -n{}".format( + self.ssrc_model, ipFile[0], ipFile[1] , self.factors[ipFile[2]], ch0, ch1, self.factors[opRate], dither, self.numSamples[ipFile[2]], blocksize) + p = Popen([cmd], stdin=PIPE, stdout=PIPE, shell=True) + output, error = p.communicate(input=b'\n') + opFiles = np.append(opFiles, np.array([[ch0, ch1, opRate, "1.0", output, "c-ssrc"]]), axis=0) + simLog[ipFile[0]]['ssrc']=[str(ch0), 0, opRate, str(fDev), output, "c-ssrc"] + simLog[ipFile[1]]['ssrc']=[str(ch1), 1, opRate, str(fDev), output, "c-ssrc"] + + # DS3 C Model + # ------------ + if self.sampleRates[opRate] * 3 == self.sampleRates[ipFile[2]] and fDev==1.0: + opf=["na", "na"] + for c in [0,1]: #do both channels seperately + if os.path.isfile("./input.dat"): # this only proceses a single channel and assumes an input file called "input.dat" and generates "output.dat" + os.remove("./input.dat") + os.link(ipFile[c], "./input.dat") + opf[c] = self.exFileName(ipFile[c], 'c-ds3', fDev, opRate) + cmd = '{}'.format(self.ds3_model) + p = Popen([cmd], stdin=PIPE, stdout=PIPE, shell=True) + output, error = p.communicate(input=b'\n') + shutil.copy("output.dat", opf[c]) + simLog[ipFile[c]]['ds3']=[str(opf[c]), c, opRate, str(fDev), output, "c-ds3"] + opFiles = np.append(opFiles, np.array([[opf[0], opf[1], opRate, "1.0", output, "c-ds3"]]), axis=0) + os.remove("./input.dat") + os.remove("./output.dat") + + # OS3 C Model + # ------------ + if self.sampleRates[opRate] == self.sampleRates[ipFile[2]] * 3 and fDev==1.0: + opf=["na", "na"] + for c in [0,1]: #do both channels seperately + if os.path.isfile("./input.dat"): # this only proceses a single channel and assumes an input file called "input.dat" and generates "output.dat" + os.remove("./input.dat") + os.link(ipFile[c], "./input.dat") + opf[c] = self.exFileName(ipFile[c], 'c-os3', fDev, opRate) + cmd = '{}'.format(self.os3_model) + p = Popen([cmd], stdin=PIPE, stdout=PIPE, shell=True) + output, error = p.communicate(input=b'\n') + shutil.copy("output.dat", opf[c]) + simLog[ipFile[c]]['os3']=[str(opf[c]), c, opRate, str(fDev), output, "c-os3"] + opFiles = np.append(opFiles, np.array([[opf[0], opf[1], opRate, "1.0", output, "c-os3"]]), axis=0) + os.remove("./input.dat") + os.remove("./output.dat") + + + return opFiles, simLog + + def scrapeXsimLog(self, data): + # This bit scrapes the output of xsim and takes the average of all reported CPU utilization + log = data['log'] + p = re.compile("Thread util=\d*%") + q = re.compile("\d+") + u=re.findall(p,log) + tu=0 + for i in range(0, len(u)): + tu += int(re.findall(q, u[i])[0]) + tu = tu / len(u) + return tu #thread utilization % + + + def scrapeCLog(self, data): + # This scrapes the CPU data from the C model + ch0mips = 0 + ch1mips = 0 + totalmips = 0 + txtmips = "" + if data['src'] == "c-asrc": + p = re.compile("MIPS.*?\\\\n") + m = re.findall(p, str(data['log'])) + ch0mips = float(m[0].replace("\\n", "").split(":")[1].strip()) + ch1mips = float(m[5].replace("\\n", "").split(":")[1].strip()) + totalmips = ch0mips + ch1mips + txtmips = "\n".join(m) + if data['src'] == "c-ssrc": + p = re.compile("MIPS.*?\n") + m = re.findall(p, str(data['log']).replace("\\n","\n")) + if len(m) > 0: + totalmips = float(m[0].split(":")[1].strip()) + + return {"log":txtmips, "total_mips":totalmips, "ch0_mips":ch0mips, "ch1_mips":ch1mips} + + + def makeLabel(self, item): + #utility to generate a title / filename to use in plots from an item from a file list which is in the foramt [file1, file2, rate as str] + #and should work for ipFiles and opFiles + a = item[0][item[0].rfind("/")+1:] + label = a[0:a.rfind(".")] + return label + + + def loadDataFromOutputFile(self, file): + #loads an array of files in the format returned by running 'run_c_model' and returns an array of elements, each of which is a dictionary of [ 2 channels of samples], [2 channels of labels], o/p rate + ch0 = np.loadtxt(file[0]).astype("int32") + label = self.makeLabel(file) + return {'samples':ch0, 'labels':label, 'chan':file[-5], 'rate':file[-4], 'fdev':file[-3], 'log':file[-2], 'src':file[-1]} + + + def calcSNR(self, fftdata, ch): + signal=0 + tmpNoise=np.copy(fftdata[2]) + #calculates the SNR from fft data + for s in self.sig[ch]: #these are the expected bins in the output + bin = int(s) + signal += fftdata[2][bin] + tmpNoise[bin] = 0 + noise = np.sum(tmpNoise) + snr = 20 * np.log10((signal / noise) + self.fudge) + return snr + + + # calculates the THD, but limited to cases where there is PRECISELY one signal tone (i.e., in one fft bin) + def calcTHD(self, fftdata, ch): + if len(self.sig[ch]) > 1: + thd = "NA" + else: + sigbin = self.sig[ch][0] + signal = np.abs(fftdata[2][sigbin]) ** 2 + noisebins = np.arange(int(2*sigbin), (self.fftPoints/2)-1, sigbin).astype(int) # harmonic bins, which are the noise + noise = 0 + for nb in noisebins: + noise += np.abs(fftdata[2][int(nb)]) ** 2 + thd = 10*np.log10((noise/signal)) + thd="{:.1f}dB".format(thd) #returned as string so we can pass the "NA" for muti-tone cases + return thd + + + def makeRST(self, file, ipRate, opRate, fDev, sims): + relFile = os.path.relpath(self.outputFolder, self.rstFolder) + "/" + file + fsi = "{:,d}Hz".format(self.sampleRates[ipRate]) + fso = "{:,d}Hz".format(self.sampleRates[opRate]) + ferr = "{:f}".format(fDev) + plots = ", ".join(sims) + self.rstFile = self.rstFile + "\n\n\n" + ".. figure:: {}".format(relFile) + self.rstFile = self.rstFile + "\n" + " :scale: {}".format("90%") + self.rstFile = self.rstFile + "\n\n" + " Input Fs: {}, Output Fs: {}, Fs error: {}, Results for: {}".format(fsi, fso, ferr, plots) + + + def addRSTHeader(self, title, level): + underline=["=", "+", "-", "."] + self.rstFile = self.rstFile + "\n\n\n" + title + self.rstFile = self.rstFile + "\n" + underline[level-1] * len(title) + "\n\n" + + def addRSTText(self, text): + self.rstFile = self.rstFile + "\n\n" + text + + + def addLog2RST(self): + relFile = os.path.relpath(self.outputFolder, self.rstFolder) + self.logFile + # source ipRate(Hz) opRate(Hz) fDev ch signals(Hz) SNR(dB) THD(dB) Total MIPS MIPS(ch0) MIPS(ch1) Text + self.rstFile = self.rstFile + "\n\n\n" + ".. csv-table:: Data table" + self.rstFile = self.rstFile + "\n" + " :file: {}".format(relFile) + self.rstFile = self.rstFile + "\n" + " :widths: 8, 9, 9, 9, 6, 14, 9, 9, 9, 9, 9" + self.rstFile = self.rstFile + "\n" + " :header-rows: 1" + self.rstFile = self.rstFile + "\n" + + + + def saveRST(self, file): + rst = open("{}/{}".format(self.rstFolder, file), "w") + rst.write(self.rstFile) + rst.close() diff --git a/doc/python/doc_asrc.py b/doc/python/doc_asrc.py new file mode 100644 index 00000000..0eb4c4d7 --- /dev/null +++ b/doc/python/doc_asrc.py @@ -0,0 +1,112 @@ +# Copyright 2023 XMOS LIMITED. +# This Software is subject to the terms of the XMOS Public Licence: Version 1. +from asrc_utils import asrc_util +import numpy as np +import matplotlib.pyplot as plt +from pathlib import Path + +################################################################################################################ +# OVERVIEW +# +# This script generates a set of test files for the supported input and output rates. +# It passes these through the golden reference "C" models, and +# Plots the FFTs, and extracts the SNR and THD. +# All this info is annotated on the Plot which is saved to the output folder. +# +# Note that both the C model is a dual-channel implementations, so we pass them a pair of source files +# and since this is ASRC, we vary the frequency deviation parameter fDev. +# But, OS3 and DS3 are single channel apps - which is dealt with! +# +################################################################################################################ + + +# Setup some basics +pkg_dir = Path(__file__).parent +xsim = False +fftPoints=1024 # note when updating the test tones with "updateSig" this is overwritten +FERR = 1.0 #this is an additional sample rate deviation applied to the inpt signal to test effect of errors + +U = asrc_util(pkg_dir, True, fftPoints,[200], [500]) # create the util object +U.addRSTText(".. _performance-plots:") +U.addRSTHeader("Performance Characterisation for SSRC ASRC DS3 and OS3", 1) # start the RST it generates with a title +IntroText = """ +The FFT plots in this section provide a visual guide to the performance of the SSRC, ASRC, DS3 and OS3 sample rate converters. +Test signals were created allowing analysis of the sample rate converter output across different scenarios. + +Two input signals were played through a stereo sample rate converter across a range of input and output sample rates. +For Channel 0, a single pure tone was generated ensuring its frequency was well within the overall nyquist rate. +For Channel 1, multiple tones spaced logarithmically were generated with the spacing most dense at higher frequencies. + +The resulting frequency plot output clearly shows the noise floor relative to the sample rate converted injected tone(s). +The plots are annotated with an estimate of the Signal to Noise Ratio (SNR) as well as Total Harmonic Distortion (THD). + +For the case of the ASRC, in addition to the nominal input frequency of 0 PPM deviation, the +/-100 PPM frequency deviation cases are also shown. +""" +U.addRSTText(IntroText) +U.addRSTHeader("Pure Tone FFT SRC Plots Across Sample Rate Combinations", 2) # start the RST it generates with a title + +for fDev in [0.9999, 1.0, 1.0001]: # for a set of different frequency deviations + U.addRSTHeader("Frequency error: {:.6f}Hz".format(fDev), 3) #add a title to the RST for the freq deviation + + for opRate in U.allRates: # for each of the possible output sample rates + U.addRSTHeader("Output Fs : {:,d}Hz".format(U.sampleRates[opRate]), 4) # add a subtitle to RST for the opRate + + # Choose the o/p rate and freq deviation + U.setOpRate(opRate, fDev) + + no_results = True # the RST contains headings for all fDev, opRate and ipRate - so if this is an unsupported combination, add a warning to thr RST. + for ipRate in U.allRates:# for each of the possible input sample rates + # opportunity to choose different test freq, which also sets a safer fft size + U.updateSig(ipRate, [int(fftPoints/5)], [int(fftPoints/4),int(fftPoints/6)], True) # specified in terms of FFT o/p bin, when flag set true it auto-fills a logarithmic range for ch1 and 5% in for ch0 + + # Make a set of input files based on range of sample rates supported + ipFiles, sig = U.makeInputFiles([ipRate], FERR) # makes the signals, saves data as files and returns some info about them + + # Put the input data through the golden "C" emulators + opFiles, simLog = U.run_c_model(ipFiles, opRate, 4, fDev) + + # Iterate over all the input data files and channels + for channels in ipFiles: # For each input sata set there will be an output one for each channel + for channel in channels[0:2]: + print(channel) + + if len(simLog[channel]) > 0: #at least one simulation ran for this combination of iprate, oprate and fdev + no_results = False + for sim in simLog[channel]: + opData = U.loadDataFromOutputFile(simLog[channel][sim]) + + # fft + opfft = U.rawFFT(opData['samples']) + oplabel = opData['labels'] + + # Get MHz utilization info from the log + info = U.scrapeCLog(opData) + + # Calculate the SNR for each fft + opSNR = U.calcSNR(opfft, opData['chan']) + + # Calculate the THD for each fft + opTHD = U.calcTHD(opfft, opData['chan']) + + # Keep the results to plot later + text="SNR:{:.1f}dB\nTHD:{}dB\n".format(opSNR, opTHD) + U.pushPlotInfo(opfft, oplabel, text) + + # Update log with these results + U.logResults(sim, ipRate, opRate, fDev, opData['chan'], opSNR, opTHD, info['total_mips'], info['ch0_mips'], info['ch1_mips'], info['log']) + + + #Plot the results + plotFile = U.plotFFT(U.plot_data, combine=False, title=U.makePlotTitle(simLog, channel), subtitles=U.plot_label, log=True, text=U.plot_text) # plots a grid of charts, one per simulation model + U.makeRST(plotFile, ipRate, opRate, fDev, simLog[channel].keys()) # add this plot to a list to save as an RST file later + U.resetPlotInfo() # otherwise the next iteration of rates etc will add more plots to this instead of starting a new plot. + if no_results: + U.addRSTText("No SRC available for this scenario.") + +U.log2csv() # Save the log file +U.addRSTHeader("Tabulated data", 2) # start the RST it generates with a title +U.addLog2RST() # adds the log, which is the tabulated results, to the RST file +U.saveRST("allPlots.rst") # Save the log file to the output folder + +print("Done") +quit() diff --git a/doc/rst/programming_guide/images/asrc_fft_176_48.pdf b/doc/rst/programming_guide/images/asrc_fft_176_48.pdf deleted file mode 100644 index cf497265..00000000 Binary files a/doc/rst/programming_guide/images/asrc_fft_176_48.pdf and /dev/null differ diff --git a/doc/rst/programming_guide/images/asrc_fft_44_192.pdf b/doc/rst/programming_guide/images/asrc_fft_44_192.pdf deleted file mode 100644 index 6035eb20..00000000 Binary files a/doc/rst/programming_guide/images/asrc_fft_44_192.pdf and /dev/null differ diff --git a/doc/rst/programming_guide/images/asrc_fft_96_88.pdf b/doc/rst/programming_guide/images/asrc_fft_96_88.pdf deleted file mode 100644 index ddcfe312..00000000 Binary files a/doc/rst/programming_guide/images/asrc_fft_96_88.pdf and /dev/null differ diff --git a/doc/rst/programming_guide/images/ssrc_fft_176_48.pdf b/doc/rst/programming_guide/images/ssrc_fft_176_48.pdf deleted file mode 100644 index db38f15d..00000000 Binary files a/doc/rst/programming_guide/images/ssrc_fft_176_48.pdf and /dev/null differ diff --git a/doc/rst/programming_guide/images/ssrc_fft_44_192.pdf b/doc/rst/programming_guide/images/ssrc_fft_44_192.pdf deleted file mode 100644 index 9d9133b8..00000000 Binary files a/doc/rst/programming_guide/images/ssrc_fft_44_192.pdf and /dev/null differ diff --git a/doc/rst/programming_guide/images/ssrc_fft_96_88.pdf b/doc/rst/programming_guide/images/ssrc_fft_96_88.pdf deleted file mode 100644 index e3b24c3e..00000000 Binary files a/doc/rst/programming_guide/images/ssrc_fft_96_88.pdf and /dev/null differ diff --git a/doc/rst/programming_guide/index.rst b/doc/rst/programming_guide/index.rst deleted file mode 100644 index 9453a906..00000000 --- a/doc/rst/programming_guide/index.rst +++ /dev/null @@ -1,10 +0,0 @@ -Programming Guide -################# - -.. include:: ../../../README.rst - -.. include:: ./multi_rate_hifi_src.rst - -.. include:: ./fixed_ratio_src.rst - -.. include:: ../../../CHANGELOG.rst diff --git a/doc/rst/version.rst b/doc/rst/version.rst deleted file mode 100644 index 06defed3..00000000 --- a/doc/rst/version.rst +++ /dev/null @@ -1 +0,0 @@ -.. version:: 2.3.0 diff --git a/doc/src_html/custom.css b/doc/src_html/custom.css deleted file mode 100644 index 8bca20fb..00000000 --- a/doc/src_html/custom.css +++ /dev/null @@ -1,27 +0,0 @@ -/* no longer needed */ -/* This rule allows scrolling the navigation menu on mobile -@media (max-width: 67em) { - .sidebar-drawer { - overflow-y: scroll; - } -} */ - -/* no longer needed */ -/* this helps with the sticky sidebar scrolling focus issues -.sidebar-sticky, .toc-sticky { - height: auto; -} */ - - -h1, h2, h3, h4, h5, h6{ - font-weight: 300; -} - -table.docutils td p{ - font-size: x-small -} - -table.docutils th p{ - font-size: small; - font-weight: 300; -} \ No newline at end of file diff --git a/index.rst b/index.rst index 14e6c13e..6d3b4a84 100644 --- a/index.rst +++ b/index.rst @@ -1,9 +1,10 @@ ###################### -SAMPLE RATE CONVERSION +SAMPLE RATE CONVERSION ###################### .. toctree:: :maxdepth: 5 - ./doc/rst/programming_guide/index.rst \ No newline at end of file + ./doc/programming_guide/index.rst + ./doc/python/_build/rst/allPlots.rst diff --git a/lib_src/module_build_info b/lib_src/module_build_info index 47407bbc..9bca4295 100644 --- a/lib_src/module_build_info +++ b/lib_src/module_build_info @@ -1,4 +1,4 @@ -VERSION = 2.3.0 +VERSION = 2.4.0 DEPENDENT_MODULES = lib_logging(>=3.1.1) \ lib_xassert(>=4.1.0) diff --git a/python/setup.py b/python/setup.py index f452b030..1537b2af 100644 --- a/python/setup.py +++ b/python/setup.py @@ -18,7 +18,8 @@ 'numpy', 'soundfile', 'pytest-xdist', - 'scipy' + 'scipy', + 'mpmath' ], dependency_links=[ ], diff --git a/settings.json b/settings.json deleted file mode 100644 index 78a4bf84..00000000 --- a/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "title": "SAMPLE RATE CONVERSION", - "project": "lib_src documentation", - "version": "2.3.0" -} diff --git a/settings.yml b/settings.yml new file mode 100644 index 00000000..6fbdc36a --- /dev/null +++ b/settings.yml @@ -0,0 +1,17 @@ +--- +project: lib_src +title: SAMPLE RATE CONVERSION +version: 2.4.0 + +documentation: + exclude_patterns_path: doc/exclude_patterns.inc + doxygen_projects: + lib_xcore_math: + doxyfile_path: doc/Doxyfile.inc + pdfs: + doc/programming_guide/index: + pdf_title: "{{title}} - Programming Guide" + pdf_filename: "{{project}}_programming_guide_v{{version}}" + doc/python/_build/rst/allPlots: + pdf_title: "{{title}} - \\\\ Performance Characterisation for SSRC ASRC DS3 and OS3" + pdf_filename: "{{project}}_performance_characterisation_v{{version}}" \ No newline at end of file diff --git a/tests/asrc_test/CMakeLists.txt b/tests/asrc_test/CMakeLists.txt index e3d88c5e..7e0f6e0d 100644 --- a/tests/asrc_test/CMakeLists.txt +++ b/tests/asrc_test/CMakeLists.txt @@ -28,7 +28,7 @@ if ((${CMAKE_SYSTEM_PROCESSOR} STREQUAL XCORE_XS3A) OR (${CMAKE_SYSTEM_PROCESSOR ) set(APP_COMMON_LINK_LIBRARIES - lib_src + lib_src ) set(APP_LINK_OPTIONS @@ -54,14 +54,14 @@ if ((${CMAKE_SYSTEM_PROCESSOR} STREQUAL XCORE_XS3A) OR (${CMAKE_SYSTEM_PROCESSOR else () # Golden ref app + include(${CMAKE_CURRENT_LIST_DIR}/asrc_c_emulator.cmake) set(TARGET_NAME asrc_golden) project(${TARGET_NAME} C) - file(GLOB APP_SOURCES ${CMAKE_CURRENT_LIST_DIR}/model/*.c + file(GLOB APP_SOURCES ${CMAKE_CURRENT_LIST_DIR}/model/Main.c ) set(APP_INCLUDES - ${CMAKE_CURRENT_LIST_DIR}/../../lib_src/src/multirate_hifi ${CMAKE_CURRENT_LIST_DIR}/model ) @@ -79,7 +79,6 @@ else () target_include_directories(${TARGET_NAME} PUBLIC ${APP_INCLUDES}) target_compile_definitions(${TARGET_NAME} PUBLIC ${APP_COMPILE_DEFINITIONS}) target_compile_options(${TARGET_NAME} PRIVATE ${APP_COMPILER_FLAGS}) - target_link_libraries(${TARGET_NAME} PUBLIC ${APP_COMMON_LINK_LIBRARIES}) + target_link_libraries(${TARGET_NAME} PUBLIC asrc_c_emulator_lib) unset(TARGET_NAME) endif() - diff --git a/tests/asrc_test/asrc_c_emulator.cmake b/tests/asrc_test/asrc_c_emulator.cmake new file mode 100644 index 00000000..d3a12fcc --- /dev/null +++ b/tests/asrc_test/asrc_c_emulator.cmake @@ -0,0 +1,33 @@ +set(LIB_NAME asrc_c_emulator_lib) + add_library(${LIB_NAME} STATIC) + + target_sources(${LIB_NAME} + PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/model/src/ASRC_wrapper.c + ${CMAKE_CURRENT_LIST_DIR}/model/src/ASRC.c + ${CMAKE_CURRENT_LIST_DIR}/model/src/FilterDefs.c + ${CMAKE_CURRENT_LIST_DIR}/model/src/FIR.c + ${CMAKE_CURRENT_LIST_DIR}/model/src/IntArithmetic.c + ) + + target_include_directories(${LIB_NAME} + PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/model/src + ${CMAKE_CURRENT_LIST_DIR}/../../lib_src/src/multirate_hifi) + + target_include_directories(${LIB_NAME} + PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/model/api + ) + + target_compile_definitions(${LIB_NAME} + PUBLIC + __int64=int64_t + ) + + target_compile_options(${LIB_NAME} + PRIVATE + -Os + -g + -fPIC + ) diff --git a/tests/asrc_test/model/ASRC.h b/tests/asrc_test/model/ASRC.h deleted file mode 100644 index 3f86f1ce..00000000 --- a/tests/asrc_test/model/ASRC.h +++ /dev/null @@ -1,260 +0,0 @@ -// =========================================================================== -// =========================================================================== -// -// File: ASRC.h -// -// Top level definition file for the ASRC -// -// Target: MS Windows -// Version: 1.0 -// -// =========================================================================== -// =========================================================================== - -#ifndef _ASRC_H_ -#define _ASRC_H_ - - // =========================================================================== - // - // Includes - // - // =========================================================================== - #include "FIR.h" - #include "FilterDefs.h" - - // =========================================================================== - // - // Defines - // - // =========================================================================== - - // General defines - // --------------- - #define ASRC_N_CHANNELS 1 // Number of channels processed by ASRC instance - #define ASRC_N_IO_CHANNELS 2 // Number of input / output channels in I/O streams - #define ASRC_N_IN_OUT_RATIO_MAX 5 // Maximum ratio (as integer) between input and output number of samples - - #define ASRC_STACK_LENGTH_MULT (ASRC_N_CHANNELS * 4) // Multiplier for stack length (stack length = this value x the number of input samples to process) - #define ASRC_ADFIR_COEFS_LENGTH FILTER_DEFS_ADFIR_PHASE_N_TAPS // Length of AD FIR coefficients buffer - - // Nominal Fs Ratio scale value - #define ASRC_NOMINAL_FS_SCALE (268435456.0) // 2^28 - - // Parameter values - // ---------------- - #define ASRC_ON 1 - #define ASRC_OFF 0 - - #define ASRC_DITHER_OFF ASRC_OFF - #define ASRC_DITHER_ON ASRC_ON - #define ASRC_DITHER_ON_OFF_MIN ASRC_DITHER_OFF - #define ASRC_DITHER_ON_OFF_MAX ASRC_DITHER_ON - - - - - // =========================================================================== - // - // Defines - // - // =========================================================================== - - - // =========================================================================== - // - // TypeDefs - // - // =========================================================================== - - // To avoid C type definitions when including this file from assembler - #ifndef INCLUDE_FROM_ASM - - // ASRC Return Codes - // ----------------- - typedef enum _ASRCReturnCodes - { - ASRC_NO_ERROR = 0, - ASRC_ERROR = 1 - } ASRCReturnCodes_t; - - - // Sampling rate codes - // ------------------- - typedef enum _ASRCFs - { - ASRC_FS_44 = 0, // Fs = 44.1kHz code - ASRC_FS_48 = 1, // Fs = 48kHz code - ASRC_FS_88 = 2, // Fs = 88.2kHz code - ASRC_FS_96 = 3, // Fs = 96kHz code - ASRC_FS_176 = 4, // Fs = 176.4kHz code - ASRC_FS_192 = 5, // Fs = 192kHz code - } ASRCFs_t; - #define ASRC_N_FS (ASRC_FS_192 + 1) - #define ASRC_FS_MIN ASRC_FS_44 - #define ASRC_FS_MAX ASRC_FS_192 - - - // ASRC Filters IDs structure - // -------------------------- - #define ASRC_F1_INDEX 0 - #define ASRC_F2_INDEX 1 - #define ASRC_N_F (ASRC_F2_INDEX + 1) - - typedef struct _ASRCFiltersIDs - { - unsigned int uiFID[ASRC_N_F]; - } ASRCFiltersIDs_t; - - // ASRC time ratio configurations - // ------------------------------ - typedef struct _ASRCFsRatioConfigs - { - unsigned int uiNominalFsRatio; - unsigned int uiNominalFsRatio_lo; - unsigned int uiMinFsRatio; - unsigned int uiMaxFsRatio; - int iFsRatioShift; - } ASRCFsRatioConfigs_t; - - // ASRC State structure - // -------------------- - typedef struct _ASRCState - { - unsigned int uiRndSeed; // Dither random seeds current values - int iDelayFIRLong[2 * FILTER_DEFS_FIR_MAX_TAPS_LONG]; // Doubled length for circular buffer simulation - int iDelayFIRShort[2 * FILTER_DEFS_FIR_MAX_TAPS_SHORT]; // Doubled length for circular buffer simulation - int iDelayADFIR[2 * FILTER_DEFS_ADFIR_PHASE_N_TAPS]; // Doubled length for circular buffer simulation - } ASRCState_t; - - - // ASRC Control structure - // ---------------------- - typedef struct _ASRCCtrl - { - int* piIn; // Input buffer pointer (PCM, 32bits, 2 channels time domain interleaved data) - unsigned int uiNInSamples; // Number of input samples to process in one call to the processing function - unsigned int uiNSyncSamples; // Number of synchronous samples produced in one call to the processing function - ASRCFs_t eInFs; // Input sampling rate code - int* piOut; // Output buffer poin ter (PCM, 32bits, 2 channels time domain interleaved data) - unsigned int uiNASRCOutSamples; // Number of output samples produced during last call to the asynchronous processing function - ASRCFs_t eOutFs; // Output sampling rate code - - FIRCtrl_t sFIRF1Ctrl; // F1 FIR controller - FIRCtrl_t sFIRF2Ctrl; // F2 FIR controller - ADFIRCtrl_t sADFIRF3Ctrl; // F3 ADFIR controller - - unsigned int uiFsRatio; // Fs ratio: Fsin / Fsout - unsigned int uiFsRatio_lo; - - int iTimeInt; // Integer part of time - unsigned int uiTimeFract; // Fractional part of time - int iTimeStepInt; // Integer part of time step - unsigned int uiTimeStepFract; // Fractional part of time step - - unsigned int uiDitherOnOff; // Dither on/off flag - unsigned int uiRndSeedInit; // Dither random seed initial value - - ASRCState_t* psState; // Pointer to state structure - int* piStack; // Pointer to stack buffer - int* piADCoefs; // Pointer to AD coefficients - - float fCycleCountF1F2; // Variable to hold cycle count for MIPS estimations for F1 and F2 stages - float fCycleCountF3AdaptiveCoefs; // Variable to hold cycle count for MIPS estimations for F3 adaptive filters computation - float fCycleCountF3; // Variable to hold cycle count for MIPS estimations for F3 computation - float fCycleCountDither; // Variable to hold cycle count for MIPS estimations for dither computation - } ASRCCtrl_t; - - - // =========================================================================== - // - // Function prototypes - // - // =========================================================================== - - // ==================================================================== // - // Function: ASRC_prepare_coefs // - // Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // - // Return values: ASRC_NO_ERROR on success // - // ASRC_ERROR on failure // - // Description: Prepares the ASRC coefficients from the prototype // - // Needs to be called only once // - // ==================================================================== // - ASRCReturnCodes_t ASRC_prepare_coefs(void); - - // ==================================================================== // - // Function: ASRC_init // - // Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // - // Return values: ASRC_NO_ERROR on success // - // ASRC_ERROR on failure // - // Description: Inits the ASRC passed as argument // - // ==================================================================== // - ASRCReturnCodes_t ASRC_init(ASRCCtrl_t* psASRCCtrl); - - // ==================================================================== // - // Function: ASRC_sync // - // Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // - // Return values: ASRC_NO_ERROR on success // - // ASRC_ERROR on failure // - // Description: Syncs the ASRC passed as argument // - // ==================================================================== // - ASRCReturnCodes_t ASRC_sync(ASRCCtrl_t* psASRCCtrl); - - // ==================================================================== // - // Function: ASRC_proc_F1_F2 // - // Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // - // Return values: ASRC_NO_ERROR on success // - // ASRC_ERROR on failure // - // Description: Processes F1 and F2 for a channel // - // ==================================================================== // - ASRCReturnCodes_t ASRC_proc_F1_F2(ASRCCtrl_t* psASRCCtrl); - - // ==================================================================== // - // Function: ASRC_update_fs_ratio // - // Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // - // Return values: ASRC_NO_ERROR on success // - // ASRC_ERROR on failure // - // Description: Updates the ASRC with the new Fs ratio // - // ==================================================================== // - ASRCReturnCodes_t ASRC_update_fs_ratio(ASRCCtrl_t* psASRCCtrl); - - // ==================================================================== // - // Function: ASRC_proc_F3_in_spl // - // Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // - // int iInSample: new input sample // - // Return values: ASRC_NO_ERROR on success // - // ASRC_ERROR on failure // - // Description: Writes new input sample to F3 delay line // - // ==================================================================== // - ASRCReturnCodes_t ASRC_proc_F3_in_spl(ASRCCtrl_t* psASRCCtrl, int iInSample); - - // ==================================================================== // - // Function: ASRC_proc_F3_time // - // Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // - // Return values: ASRC_NO_ERROR if an output sample must be produced // - // ASRC_ERROR if no output sample needs to be produced // - // Description: Processes F3 time // - // ==================================================================== // - ASRCReturnCodes_t ASRC_proc_F3_time(ASRCCtrl_t* psASRCCtrl); - - // ==================================================================== // - // Function: ASRC_proc_F3_macc // - // Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // - // int* piOutSample: Address of output sample // - // Return values: ASRC_NO_ERROR on success // - // ASRC_ERROR on failure // - // Description: Processes F3 for a channel // - // ==================================================================== // - ASRCReturnCodes_t ASRC_proc_F3_macc(ASRCCtrl_t* psASRCCtrl, int* piOutSample); - - // ==================================================================== // - // Function: ASRC_proc_dither // - // Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // - // Return values: ASRC_NO_ERROR on success // - // ASRC_ERROR on failure // - // Description: Processes dither for a channel // - // ==================================================================== // - ASRCReturnCodes_t ASRC_proc_dither(ASRCCtrl_t* psASRCCtrl); - - #endif // nINCLUDE_FROM_ASM - -#endif // _ASRC_H_ diff --git a/tests/asrc_test/model/Main.c b/tests/asrc_test/model/Main.c index fca23b1b..7a60933c 100644 --- a/tests/asrc_test/model/Main.c +++ b/tests/asrc_test/model/Main.c @@ -25,12 +25,18 @@ #include // ASRC includes -#include "ASRC.h" +#include "ASRC_wrapper.h" // Main file includes #include "Main.h" - +#define ASRC_N_CHANNELS 1 // Number of channels processed by ASRC instance +#define ASRC_N_IO_CHANNELS 2 // Number of input / output channels in I/O streams +#define ASRC_N_IN_OUT_RATIO_MAX 5 // Maximum ratio (as integer) between input and output number of samples +// Parameter values +// ---------------- +#define ASRC_DITHER_ON_OFF_MIN ASRC_DITHER_OFF +#define ASRC_DITHER_ON_OFF_MAX ASRC_DITHER_ON // =========================================================================== // @@ -87,15 +93,6 @@ unsigned int uiASRCDitherOnOff; // ASRC Dither on/off flag unsigned int uiASRCRandSeed[ASRC_N_IO_CHANNELS]; // ASRC channel 0/1 random seeds for dither -// ASRC instances variables -// ------------------------ -// State, Stack, Coefs and Control structures (one for each channel) -ASRCState_t sASRCState[ASRC_N_IO_CHANNELS]; -int iASRCStack[ASRC_N_IO_CHANNELS][ASRC_STACK_LENGTH_MULT * N_IN_SAMPLES_MAX]; -ASRCCtrl_t sASRCCtrl[ASRC_N_IO_CHANNELS]; -int iASRCADFIRCoefs[ASRC_ADFIR_COEFS_LENGTH]; - - // Data I/O // -------- // Input and output data buffers and file pointers (stereo, 32bits integer, time-domain interleaved) @@ -155,6 +152,8 @@ int main(int argc, char *argv[]) uiASRCRandSeed[0] = ASRC_RAND_SEED_CHANNEL_0_DEFAULT; // Random seed for channel 0 uiASRCRandSeed[1] = ASRC_RAND_SEED_CHANNEL_1_DEFAULT; // Random seed for channel 1 + ASRCCtrl_profile_only_t *profile_info_ptr[MAX_ASRC_N_IO_CHANNELS]; // Array of pointers + // Parse command line arguments for (ui = 1; ui < (unsigned int)argc; ui++) { @@ -213,38 +212,16 @@ int main(int argc, char *argv[]) // Process init // ------------ - // Prepare the ASRC coefficients - if(ASRC_prepare_coefs() != ASRC_NO_ERROR) - { - sprintf(pzError, "Error at ASRC coefficients preparation"); - HandleError(pzError, FATAL); - } - - for(ui = 0; ui < ASRC_N_IO_CHANNELS; ui++) - { - // Set state, stack and coefs into ctrl structure - sASRCCtrl[ui].psState = &sASRCState[ui]; - sASRCCtrl[ui].piStack = iASRCStack[ui]; - sASRCCtrl[ui].piADCoefs = iASRCADFIRCoefs; - - // Set input/output sampling rate codes - sASRCCtrl[ui].eInFs = uiInFs; - sASRCCtrl[ui].eOutFs = uiOutFs; - - // Set number of samples - sASRCCtrl[ui].uiNInSamples = uiNInSamples; - - // Set dither flag and random seeds - sASRCCtrl[ui].uiDitherOnOff = uiASRCDitherOnOff; - sASRCCtrl[ui].uiRndSeedInit = uiASRCRandSeed[ui]; - - // Init ASRC instances - if(ASRC_init(&sASRCCtrl[ui]) != ASRC_NO_ERROR) - { - sprintf(pzError, "Error at ASRC initialization"); - HandleError(pzError, FATAL); - } - } + uint64_t nominal_fs_ratio = wrapper_asrc_init( + &profile_info_ptr, // Pointer to array of pointers + uiFsTable[uiInFs], + uiFsTable[uiOutFs], + uiNInSamples, + ASRC_N_IO_CHANNELS, + ASRC_N_CHANNELS, + uiASRCDitherOnOff, + uiASRCRandSeed + ); // Load input data from files @@ -266,18 +243,6 @@ int main(int argc, char *argv[]) //iIn[1] = 1000000000; - // Sync - // ---- - // Sync ASRC. This is just to show that the function works and returns success - for(ui = 0; ui < ASRC_N_IO_CHANNELS; ui++) - { - if(ASRC_sync(&sASRCCtrl[ui]) != ASRC_NO_ERROR) - { - sprintf(pzError, "Error at ASRC sync"); - HandleError(pzError, FATAL); - } - } - // Process data // ------------ // Initialize remaing number of samples to total number of input samples, @@ -289,134 +254,59 @@ int main(int argc, char *argv[]) // Update Fs Ratio - for(ui = 0; ui < ASRC_N_IO_CHANNELS; ui++) - { - // Make Fs Ratio deviate - unsigned long long fs_ratio_full = ((unsigned long long)sASRCCtrl[ui].uiFsRatio << 32) | ((unsigned long long)sASRCCtrl[ui].uiFsRatio_lo); - fs_ratio_full = (unsigned long long)(fs_ratio_full * fFsRatioDeviation); - sASRCCtrl[ui].uiFsRatio = (uint32_t)(fs_ratio_full >> 32); - sASRCCtrl[ui].uiFsRatio_lo = (uint32_t)fs_ratio_full; - if(ASRC_update_fs_ratio(&sASRCCtrl[ui]) != ASRC_NO_ERROR) - { - sprintf(pzError, "Error at ASRC update fs ratio"); - HandleError(pzError, FATAL); - } - } + uint64_t actual_fs_ratio = (unsigned long long)(nominal_fs_ratio * fFsRatioDeviation); + // Loop through input samples, block by block - while(iNRemainingSamples >= (int)sASRCCtrl[0].uiNInSamples) + while(iNRemainingSamples >= (int)uiNInSamples) { // Setup // ----- for(ui = 0; ui < ASRC_N_IO_CHANNELS; ui++) { // Reset cycle counts for MIPS estimations - sASRCCtrl[ui].fCycleCountF1F2 = 0; - sASRCCtrl[ui].fCycleCountF3AdaptiveCoefs = 0; - sASRCCtrl[ui].fCycleCountF3 = 0; - sASRCCtrl[ui].fCycleCountDither = 0; - - // Set input and output data pointers - sASRCCtrl[ui].piIn = piIn + ui; - sASRCCtrl[ui].piOut = piOut + ui; + profile_info_ptr[ui]->fCycleCountF1F2 = 0; + profile_info_ptr[ui]->fCycleCountF3AdaptiveCoefs = 0; + profile_info_ptr[ui]->fCycleCountF3 = 0; + profile_info_ptr[ui]->fCycleCountDither = 0; } - // Process synchronous part (F1 + F2) - // ================================== - for(ui = 0; ui < ASRC_N_IO_CHANNELS; ui++) - // Note: this is block based similar to SSRC, output will be on stack - // and there will be sASRCCtrl[0].uiNSyncSamples samples per channel produced - if(ASRC_proc_F1_F2(&sASRCCtrl[ui]) != ASRC_NO_ERROR) - { - sprintf(pzError, "Error at ASRC F1 F2 process"); - HandleError(pzError, FATAL); - } - - - // Run the asynchronous part (F3) - // ============================== - // Clear number of output samples (note that this sample counter would actually not be needed if all was sample by sampe) - for(ui = 0; ui < ASRC_N_IO_CHANNELS; ui++) - sASRCCtrl[ui].uiNASRCOutSamples = 0; - - uiSplCntr = 0; // This is actually only used because of the bizarre mix of block and sample based processing - - // Driven by samples produced during the synchronous phase - for(ui = 0; ui < sASRCCtrl[0].uiNSyncSamples; ui++) - { - // Push new samples into F3 delay line (input from stack) for each new "synchronous" sample (i.e. output of F1, respectively F2) - for(uj = 0; uj < ASRC_N_IO_CHANNELS; uj++) - if(ASRC_proc_F3_in_spl(&sASRCCtrl[uj], sASRCCtrl[uj].piStack[ui]) != ASRC_NO_ERROR) - { - sprintf(pzError, "Error at ASRC F3 in sample process"); - HandleError(pzError, FATAL); - } - - // Run macc loop for F3 - // Check if a new output sample needs to be produced - // Note that this will also update the adaptive filter coefficients - // These must be computed for one channel only and reused in the macc loop of other channels - while(ASRC_proc_F3_time(&sASRCCtrl[0]) == ASRC_NO_ERROR) - { - // Not really needed, just for the beauty of it... - sASRCCtrl[1].iTimeInt = sASRCCtrl[0].iTimeInt; - sASRCCtrl[1].uiTimeFract = sASRCCtrl[0].uiTimeFract; - - // Apply filter F3 with just computed adaptive coefficients - for(uj = 0; uj < ASRC_N_IO_CHANNELS; uj++) - if(ASRC_proc_F3_macc(&sASRCCtrl[uj], sASRCCtrl[uj].piOut + ASRC_N_IO_CHANNELS * uiSplCntr) != ASRC_NO_ERROR) - { - sprintf(pzError, "Error at ASRC F3 in sample process"); - HandleError(pzError, FATAL); - } - - uiSplCntr++; // This is actually only used because of the bizarre mix of block and sample based processing - } - } - - - // Process dither part - // =================== - // We are back to block based processing. This is where the number of ASRC output samples is required again - // (would not be used if sample by sample based (on output samples)) - for(ui = 0; ui < ASRC_N_IO_CHANNELS; ui++) - // Note: this is block based similar to SSRC - if(ASRC_proc_dither(&sASRCCtrl[ui]) != ASRC_NO_ERROR) - { - sprintf(pzError, "Error at ASRC F1 F2 process"); - HandleError(pzError, FATAL); - } - + unsigned num_output_samples = wrapper_asrc_process(piIn, piOut, actual_fs_ratio); // Write output data to files // -------------------------- for(ui = 0; ui < ASRC_N_IO_CHANNELS; ui++) - for(uj = 0; uj < sASRCCtrl[0].uiNASRCOutSamples; uj++) - if(fprintf(OutFileDat[ui], "%i\n", *(sASRCCtrl[ui].piOut + ASRC_N_IO_CHANNELS * uj) ) < 0) + { + int *piOut_ch = piOut + ui; + for(uj = 0; uj < num_output_samples; uj++) + { + if(fprintf(OutFileDat[ui], "%i\n", *(piOut_ch + ASRC_N_IO_CHANNELS * uj) ) < 0) { sprintf(pzError, "Error while writing to output file, %s", pzOutFileName[ui]); HandleError(pzError, FATAL); } + } + } // Update input and outut data pointers for next round - piIn += (sASRCCtrl[0].uiNInSamples * ASRC_N_IO_CHANNELS); - piOut += sASRCCtrl[0].uiNASRCOutSamples * ASRC_N_IO_CHANNELS; - iNRemainingSamples -= (int)sASRCCtrl[0].uiNInSamples; + piIn += (uiNInSamples * ASRC_N_IO_CHANNELS); + piOut += num_output_samples * ASRC_N_IO_CHANNELS; + iNRemainingSamples -= (int)uiNInSamples; // Update total output sample counter - uiNTotalOutSamples += sASRCCtrl[0].uiNASRCOutSamples; + uiNTotalOutSamples += num_output_samples; } // Report MIPS for(ui = 0; ui < ASRC_N_IO_CHANNELS; ui++) { - printf("MIPS total load channel %i: %f\n", ui, (sASRCCtrl[ui].fCycleCountF1F2 + sASRCCtrl[ui].fCycleCountF3AdaptiveCoefs + sASRCCtrl[ui].fCycleCountF3 + sASRCCtrl[ui].fCycleCountDither) / (float)(sASRCCtrl[ui].uiNInSamples) * (float)uiFsTable[sASRCCtrl[ui].eInFs]/ 1000000.0); - printf("MIPS F1+F2 load channel %i: %f\n", ui, sASRCCtrl[ui].fCycleCountF1F2 / (float)(sASRCCtrl[ui].uiNInSamples) * (float)uiFsTable[sASRCCtrl[ui].eInFs]/ 1000000.0); - printf("MIPS F3 Adaptive Coefs computation load channel %i: %f\n", ui, sASRCCtrl[ui].fCycleCountF3AdaptiveCoefs / (float)(sASRCCtrl[ui].uiNInSamples) * (float)uiFsTable[sASRCCtrl[ui].eInFs]/ 1000000.0); - printf("MIPS F3 load channel %i: %f\n", ui, sASRCCtrl[ui].fCycleCountF3 / (float)(sASRCCtrl[ui].uiNInSamples) * (float)uiFsTable[sASRCCtrl[ui].eInFs]/ 1000000.0); - printf("MIPS Dither load channel %i: %f\n\n", ui, sASRCCtrl[ui].fCycleCountDither / (float)(sASRCCtrl[ui].uiNInSamples) * (float)uiFsTable[sASRCCtrl[ui].eInFs]/ 1000000.0); + printf("MIPS total load channel %i: %f\n", ui, (profile_info_ptr[ui]->fCycleCountF1F2 + profile_info_ptr[ui]->fCycleCountF3AdaptiveCoefs + profile_info_ptr[ui]->fCycleCountF3 + profile_info_ptr[ui]->fCycleCountDither) / (float)(uiNInSamples) * (float)uiFsTable[uiInFs]/ 1000000.0); + printf("MIPS F1+F2 load channel %i: %f\n", ui, profile_info_ptr[ui]->fCycleCountF1F2 / (float)(uiNInSamples) * (float)uiFsTable[uiInFs]/ 1000000.0); + printf("MIPS F3 Adaptive Coefs computation load channel %i: %f\n", ui, profile_info_ptr[ui]->fCycleCountF3AdaptiveCoefs / (float)(uiNInSamples) * (float)uiFsTable[uiInFs]/ 1000000.0); + printf("MIPS F3 load channel %i: %f\n", ui, profile_info_ptr[ui]->fCycleCountF3 / (float)(uiNInSamples) * (float)uiFsTable[uiInFs]/ 1000000.0); + printf("MIPS Dither load channel %i: %f\n\n", ui, profile_info_ptr[ui]->fCycleCountDither / (float)(uiNInSamples) * (float)uiFsTable[uiInFs]/ 1000000.0); } // Report number of output samples produced diff --git a/tests/asrc_test/model/api/ASRC_wrapper.h b/tests/asrc_test/model/api/ASRC_wrapper.h new file mode 100644 index 00000000..b924589d --- /dev/null +++ b/tests/asrc_test/model/api/ASRC_wrapper.h @@ -0,0 +1,62 @@ +// Copyright 2023 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#ifndef _ASRC_WRAPPER_H_ +#define _ASRC_WRAPPER_H_ + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +#define MAX_ASRC_N_IO_CHANNELS (2) + +// Sampling rate codes +// ------------------- +typedef enum _ASRCFs +{ + ASRC_FS_44 = 0, // Fs = 44.1kHz code + ASRC_FS_48 = 1, // Fs = 48kHz code + ASRC_FS_88 = 2, // Fs = 88.2kHz code + ASRC_FS_96 = 3, // Fs = 96kHz code + ASRC_FS_176 = 4, // Fs = 176.4kHz code + ASRC_FS_192 = 5, // Fs = 192kHz code +} ASRCFs_t; +#define ASRC_N_FS (ASRC_FS_192 + 1) +#define ASRC_FS_MIN ASRC_FS_44 +#define ASRC_FS_MAX ASRC_FS_192 + +// Parameter values +// ---------------- +#define ASRC_ON 1 +#define ASRC_OFF 0 +#define ASRC_DITHER_OFF ASRC_OFF +#define ASRC_DITHER_ON ASRC_ON + +typedef struct { + float fCycleCountF1F2; // Variable to hold cycle count for MIPS estimations for F1 and F2 stages + float fCycleCountF3AdaptiveCoefs; // Variable to hold cycle count for MIPS estimations for F3 adaptive filters computation + float fCycleCountF3; // Variable to hold cycle count for MIPS estimations for F3 computation + float fCycleCountDither; // Variable to hold cycle count for MIPS estimations for dither computation +}ASRCCtrl_profile_only_t; + +uint64_t wrapper_asrc_init( + ASRCCtrl_profile_only_t* (*profile_info_ptr)[MAX_ASRC_N_IO_CHANNELS], // Pointer to array of pointers + unsigned uiInFs, + unsigned uiOutFs, + unsigned uiNInSamples, + unsigned num_io_channels, + unsigned num_channels_per_asrc_instance, + unsigned dither_on_off, + unsigned *rand_seed); + +unsigned wrapper_asrc_process( + int *piIn, + int *piOut, + uint64_t fs_ratio + ); + +#ifdef __cplusplus + } +#endif +#endif diff --git a/tests/asrc_test/model/ASRC.c b/tests/asrc_test/model/src/ASRC.c similarity index 95% rename from tests/asrc_test/model/ASRC.c rename to tests/asrc_test/model/src/ASRC.c index 3498e4ff..19221151 100644 --- a/tests/asrc_test/model/ASRC.c +++ b/tests/asrc_test/model/src/ASRC.c @@ -1,800 +1,802 @@ -// =========================================================================== -// =========================================================================== -// -// File: ASRC.c -// -// Top level implementation file for the ASRC -// -// Target: MS Windows -// Version: 1.0 -// -// =========================================================================== -// =========================================================================== - - -// =========================================================================== -// -// Includes -// -// =========================================================================== -#include -#include -#include -#include - -// Integer arithmetic include -#include "IntArithmetic.h" -// ASRC include -#include "ASRC.h" - -// =========================================================================== -// -// Defines -// -// =========================================================================== - - -// State init value -#define ASRC_STATE_INIT 0 - -// Nominal Fs Ratio values -#define ASRC_FS_44_F (44100.0) -#define ASRC_FS_48_F (48000.0) -#define ASRC_FS_88_F (88200.0) -#define ASRC_FS_96_F (96000.0) -#define ASRC_FS_176_F (176400.0) -#define ASRC_FS_192_F (192000.0) - -#define ASRC_FS_RATIO_44_44 (uint32_t)((unsigned long long)(((double)ASRC_FS_44_F / ASRC_FS_44_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_44_44_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_44_F / ASRC_FS_44_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) -#define ASRC_FS_RATIO_44_48 (uint32_t)((unsigned long long)(((double)ASRC_FS_44_F / ASRC_FS_48_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_44_48_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_44_F / ASRC_FS_48_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) -#define ASRC_FS_RATIO_44_88 (uint32_t)((unsigned long long)(((double)ASRC_FS_44_F / ASRC_FS_88_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_44_88_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_44_F / ASRC_FS_88_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) -#define ASRC_FS_RATIO_44_96 (uint32_t)((unsigned long long)(((double)ASRC_FS_44_F / ASRC_FS_96_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_44_96_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_44_F / ASRC_FS_96_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) -#define ASRC_FS_RATIO_44_176 (uint32_t)((unsigned long long)(((double)ASRC_FS_44_F / ASRC_FS_176_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_44_176_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_44_F / ASRC_FS_176_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) -#define ASRC_FS_RATIO_44_192 (uint32_t)((unsigned long long)(((double)ASRC_FS_44_F / ASRC_FS_192_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_44_192_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_44_F / ASRC_FS_192_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) - -#define ASRC_FS_RATIO_48_44 (uint32_t)((unsigned long long)(((double)ASRC_FS_48_F / ASRC_FS_44_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_48_44_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_48_F / ASRC_FS_44_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) -#define ASRC_FS_RATIO_48_48 (uint32_t)((unsigned long long)(((double)ASRC_FS_48_F / ASRC_FS_48_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_48_48_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_48_F / ASRC_FS_48_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) -#define ASRC_FS_RATIO_48_88 (uint32_t)((unsigned long long)(((double)ASRC_FS_48_F / ASRC_FS_88_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_48_88_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_48_F / ASRC_FS_88_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) -#define ASRC_FS_RATIO_48_96 (uint32_t)((unsigned long long)(((double)ASRC_FS_48_F / ASRC_FS_96_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_48_96_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_48_F / ASRC_FS_96_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) -#define ASRC_FS_RATIO_48_176 (uint32_t)((unsigned long long)(((double)ASRC_FS_48_F / ASRC_FS_176_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_48_176_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_48_F / ASRC_FS_176_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) -#define ASRC_FS_RATIO_48_192 (uint32_t)((unsigned long long)(((double)ASRC_FS_48_F / ASRC_FS_192_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_48_192_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_48_F / ASRC_FS_192_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) - -#define ASRC_FS_RATIO_88_44 (uint32_t)((unsigned long long)(((double)ASRC_FS_88_F / ASRC_FS_44_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_88_44_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_88_F / ASRC_FS_44_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) -#define ASRC_FS_RATIO_88_48 (uint32_t)((unsigned long long)(((double)ASRC_FS_88_F / ASRC_FS_48_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_88_48_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_88_F / ASRC_FS_48_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) -#define ASRC_FS_RATIO_88_88 (uint32_t)((unsigned long long)(((double)ASRC_FS_88_F / ASRC_FS_88_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_88_88_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_88_F / ASRC_FS_88_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) -#define ASRC_FS_RATIO_88_96 (uint32_t)((unsigned long long)(((double)ASRC_FS_88_F / ASRC_FS_96_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_88_96_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_88_F / ASRC_FS_96_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) -#define ASRC_FS_RATIO_88_176 (uint32_t)((unsigned long long)(((double)ASRC_FS_88_F / ASRC_FS_176_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_88_176_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_88_F / ASRC_FS_176_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) -#define ASRC_FS_RATIO_88_192 (uint32_t)((unsigned long long)(((double)ASRC_FS_88_F / ASRC_FS_192_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_88_192_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_88_F / ASRC_FS_192_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) - -#define ASRC_FS_RATIO_96_44 (uint32_t)((unsigned long long)(((double)ASRC_FS_96_F / ASRC_FS_44_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_96_44_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_96_F / ASRC_FS_44_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) -#define ASRC_FS_RATIO_96_48 (uint32_t)((unsigned long long)(((double)ASRC_FS_96_F / ASRC_FS_48_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_96_48_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_96_F / ASRC_FS_48_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) -#define ASRC_FS_RATIO_96_88 (uint32_t)((unsigned long long)(((double)ASRC_FS_96_F / ASRC_FS_88_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_96_88_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_96_F / ASRC_FS_88_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) -#define ASRC_FS_RATIO_96_96 (uint32_t)((unsigned long long)(((double)ASRC_FS_96_F / ASRC_FS_96_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_96_96_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_96_F / ASRC_FS_96_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) -#define ASRC_FS_RATIO_96_176 (uint32_t)((unsigned long long)(((double)ASRC_FS_96_F / ASRC_FS_176_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_96_176_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_96_F / ASRC_FS_176_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) -#define ASRC_FS_RATIO_96_192 (uint32_t)((unsigned long long)(((double)ASRC_FS_96_F / ASRC_FS_192_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_96_192_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_96_F / ASRC_FS_192_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) - -#define ASRC_FS_RATIO_176_44 (uint32_t)((unsigned long long)(((double)ASRC_FS_176_F / ASRC_FS_44_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_176_44_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_176_F / ASRC_FS_44_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) -#define ASRC_FS_RATIO_176_48 (uint32_t)((unsigned long long)(((double)ASRC_FS_176_F / ASRC_FS_48_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_176_48_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_176_F / ASRC_FS_48_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) -#define ASRC_FS_RATIO_176_88 (uint32_t)((unsigned long long)(((double)ASRC_FS_176_F / ASRC_FS_88_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_176_88_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_176_F / ASRC_FS_88_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) -#define ASRC_FS_RATIO_176_96 (uint32_t)((unsigned long long)(((double)ASRC_FS_176_F / ASRC_FS_96_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_176_96_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_176_F / ASRC_FS_96_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) -#define ASRC_FS_RATIO_176_176 (uint32_t)((unsigned long long)(((double)ASRC_FS_176_F / ASRC_FS_176_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_176_176_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_176_F / ASRC_FS_176_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) -#define ASRC_FS_RATIO_176_192 (uint32_t)((unsigned long long)(((double)ASRC_FS_176_F / ASRC_FS_192_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_176_192_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_176_F / ASRC_FS_192_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) - -#define ASRC_FS_RATIO_192_44 (uint32_t)((unsigned long long)(((double)ASRC_FS_192_F / ASRC_FS_44_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_192_44_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_192_F / ASRC_FS_44_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) -#define ASRC_FS_RATIO_192_48 (uint32_t)((unsigned long long)(((double)ASRC_FS_192_F / ASRC_FS_48_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_192_48_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_192_F / ASRC_FS_48_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) -#define ASRC_FS_RATIO_192_88 (uint32_t)((unsigned long long)(((double)ASRC_FS_192_F / ASRC_FS_88_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_192_88_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_192_F / ASRC_FS_88_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) -#define ASRC_FS_RATIO_192_96 (uint32_t)((unsigned long long)(((double)ASRC_FS_192_F / ASRC_FS_96_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_192_96_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_192_F / ASRC_FS_96_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) -#define ASRC_FS_RATIO_192_176 (uint32_t)((unsigned long long)(((double)ASRC_FS_192_F / ASRC_FS_176_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_192_176_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_192_F / ASRC_FS_176_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) -#define ASRC_FS_RATIO_192_192 (uint32_t)((unsigned long long)(((double)ASRC_FS_192_F / ASRC_FS_192_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) -#define ASRC_FS_RATIO_192_192_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_192_F / ASRC_FS_192_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) - - -#define ASRC_FS_RATIO_MIN_FACTOR (0.99) -#define ASRC_FS_RATIO_MAX_FACTOR (1.01) - -#define ASRC_ADFIR_INITIAL_PHASE 32 - -// Time ratio shift values -#define ASRC_FS_RATIO_UNIT_BIT 28 -#define ASRC_FS_RATIO_PHASE_N_BITS 7 -#define ASRC_FS_RATIO_BASE_SHIFT (ASRC_FS_RATIO_UNIT_BIT - ASRC_FS_RATIO_PHASE_N_BITS) -#define ASRC_FS_RATIO_SHIFT_M1 (ASRC_FS_RATIO_BASE_SHIFT + 1) -#define ASRC_FS_RATIO_SHIFT_0 (ASRC_FS_RATIO_BASE_SHIFT) -#define ASRC_FS_RATIO_SHIFT_P1 (ASRC_FS_RATIO_BASE_SHIFT - 1) - - -// Random number generator / dithering -#define ASRC_R_CONS 32767 -#define ASRC_R_BASE 1664525 -#define ASRC_RPDF_BITS_SHIFT 16 // Shift to select bits in pseudo-random number -#define ASRC_RPDF_MASK 0x0000007F // For dithering at 24bits (in 2.30) -#define ASRC_DATA24_MASK 0xFFFFFF00 // Mask for 24bits data (once rescaled to 1.31) -#define ASRC_DITHER_BIAS 0xFFFFFFC0 // TPDF dither bias for compensating masking at 24bits but expressed in 2.30 - - -// Cycle counters -#define ASRC_FIR_OS2_OVERHEAD_CYCLE_COUNT (15.0) -#define ASRC_FIR_OS2_TAP_CYCLE_COUNT (2.125) -#define ASRC_FIR_DS2_OVERHEAD_CYCLE_COUNT (15.0) -#define ASRC_FIR_DS2_TAP_CYCLE_COUNT (2.625) -#define ASRC_FIR_SYNC_OVERHEAD_CYCLE_COUNT (15.0) -#define ASRC_FIR_SYNC_TAP_CYCLE_COUNT (2.625) - -#define ASRC_ADFIR_IN_SPL_CYCLE_COUNT (8.0) -#define ASRC_ADFIR_TIME_CHECK_CYCLE_COUNT (5.0) -#define ASRC_ADFIR_TIME_SAMPLE_CYCLE_COUNT (15.0 + 15.0 + 90.0) -#define ASRC_ADFIR_MACC_OVERHEAD_CYCLE_COUNT (8.0) -#define ASRC_ADFIR_MACC_TAP_CYCLE_COUNT (2.5) - -#define ASRC_DITHER_SAMPLE_COUNT (20.0) - - - -// =========================================================================== -// -// Variables -// -// =========================================================================== - -ASRCFiltersIDs_t sFiltersIDs[ASRC_N_FS][ASRC_N_FS] = // Filter configuration table [Fsin][Fsout] -{ - { // Fsin = 44.1kHz - // F1 F2 - {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 44.1kHz - {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 48kHz - {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 88.2kHz - {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 96kHz - {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 176.4kHz - {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID} // Fsout = 192kHz - }, - { // Fsin = 48kHz - // F1 F2 - {FILTER_DEFS_FIR_UP4844_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 44.1kHz - {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 48kHz - {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 88.2kHz - {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 96kHz - {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 176.4kHz - {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID} // Fsout = 192kHz - }, - { // Fsin = 88.2kHz - // F1 F2 - {FILTER_DEFS_FIR_BL_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 44.1kHz - {FILTER_DEFS_FIR_BL8848_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 48kHz - {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 88.2kHz - {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 96kHz - {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 176.4kHz - {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID} // Fsout = 192kHz - }, - { // Fsin = 96kHz - // F1 F2 - {FILTER_DEFS_FIR_BL9644_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 44.1kHz - {FILTER_DEFS_FIR_BL_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 48kHz - {FILTER_DEFS_FIR_UP4844_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 88.2kHz - {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 96kHz - {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 176.4kHz - {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID} // Fsout = 192kHz - }, - { // Fsin = 176.4kHz - // F1 F2 - {FILTER_DEFS_FIR_DS_ID, FILTER_DEFS_FIR_BL_ID}, // Fsout = 44.1kHz - {FILTER_DEFS_FIR_DS_ID, FILTER_DEFS_FIR_BL8848_ID}, // Fsout = 48kHz - {FITLER_DEFS_FIR_BLF_ID, FILTER_DEFS_FIR_NONE_ID,}, // Fsout = 88.2kHz - {FILTER_DEFS_FIR_BL17696_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 96kHz - {FILTER_DEFS_FIR_UPF_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 176.4kHz - {FILTER_DEFS_FIR_UPF_ID, FILTER_DEFS_FIR_NONE_ID} // Fsout = 192kHz - }, - { // Fsin = 192kHz - // F1 F2 - {FILTER_DEFS_FIR_DS_ID, FILTER_DEFS_FIR_BL9644_ID}, // Fsout = 44.1kHz - {FILTER_DEFS_FIR_DS_ID, FILTER_DEFS_FIR_BL_ID}, // Fsout = 48kHz - {FITLER_DEFS_FIR_BL19288_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 88.2kHz - {FITLER_DEFS_FIR_BLF_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 96kHz - {FILTER_DEFS_FIR_UP192176_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 176.4kHz - {FILTER_DEFS_FIR_UPF_ID, FILTER_DEFS_FIR_NONE_ID} // Fsout = 192kHz - } -}; - -ASRCFsRatioConfigs_t sFsRatioConfigs[ASRC_N_FS][ASRC_N_FS] = // Fs ratio configuration table [Fsin][Fsout] -{ - { // Fsin = 44.1kHz - // Nominal Fs Ratio // Minimal Fs Ratio // Maximal Fs Ratio Shift for time step - {ASRC_FS_RATIO_44_44, ASRC_FS_RATIO_44_44_LO, (unsigned int)(ASRC_FS_RATIO_44_44 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_44_44 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 44.1kHz - {ASRC_FS_RATIO_44_48, ASRC_FS_RATIO_44_48_LO, (unsigned int)(ASRC_FS_RATIO_44_48 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_44_48 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 48kHz - {ASRC_FS_RATIO_44_88, ASRC_FS_RATIO_44_88_LO, (unsigned int)(ASRC_FS_RATIO_44_88 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_44_88 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 88.2kHz - {ASRC_FS_RATIO_44_96, ASRC_FS_RATIO_44_96_LO, (unsigned int)(ASRC_FS_RATIO_44_96 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_44_96 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 96kHz - {ASRC_FS_RATIO_44_176, ASRC_FS_RATIO_44_176_LO, (unsigned int)(ASRC_FS_RATIO_44_176 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_44_176 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 176.4kHz - {ASRC_FS_RATIO_44_192, ASRC_FS_RATIO_44_192_LO, (unsigned int)(ASRC_FS_RATIO_44_192 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_44_192 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1} // Fsout = 192kHz - }, - { // Fsin = 48kHz - // Nominal Fs Ratio // Minimal Fs Ratio // Maximal Fs Ratio Shift for time step - {ASRC_FS_RATIO_48_44, ASRC_FS_RATIO_48_44_LO, (unsigned int)(ASRC_FS_RATIO_48_44 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_48_44 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 44.1kHz - {ASRC_FS_RATIO_48_48, ASRC_FS_RATIO_48_48_LO, (unsigned int)(ASRC_FS_RATIO_48_48 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_48_48 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 48kHz - {ASRC_FS_RATIO_48_88, ASRC_FS_RATIO_48_88_LO, (unsigned int)(ASRC_FS_RATIO_48_88 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_48_88 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 88.2kHz - {ASRC_FS_RATIO_48_96, ASRC_FS_RATIO_48_96_LO, (unsigned int)(ASRC_FS_RATIO_48_96 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_48_96 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 96kHz - {ASRC_FS_RATIO_48_176, ASRC_FS_RATIO_48_176_LO, (unsigned int)(ASRC_FS_RATIO_48_176 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_48_176 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 176.4kHz - {ASRC_FS_RATIO_48_192, ASRC_FS_RATIO_48_192_LO, (unsigned int)(ASRC_FS_RATIO_48_192 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_48_192 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1} // Fsout = 192kHz - }, - { // Fsin = 88.2kHz - // Nominal Fs Ratio // Minimal Fs Ratio // Maximal Fs Ratio Shift for time step - {ASRC_FS_RATIO_88_44, ASRC_FS_RATIO_88_44_LO, (unsigned int)(ASRC_FS_RATIO_88_44 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_88_44 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_0}, // Fsout = 44.1kHz - {ASRC_FS_RATIO_88_48, ASRC_FS_RATIO_88_48_LO, (unsigned int)(ASRC_FS_RATIO_88_48 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_88_48 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_0}, // Fsout = 48kHz - {ASRC_FS_RATIO_88_88, ASRC_FS_RATIO_88_88_LO, (unsigned int)(ASRC_FS_RATIO_88_88 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_88_88 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 88.2kHz - {ASRC_FS_RATIO_88_96, ASRC_FS_RATIO_88_96_LO, (unsigned int)(ASRC_FS_RATIO_88_96 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_88_96 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 96kHz - {ASRC_FS_RATIO_88_176, ASRC_FS_RATIO_88_176_LO, (unsigned int)(ASRC_FS_RATIO_88_176 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_88_176 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 176.4kHz - {ASRC_FS_RATIO_88_192, ASRC_FS_RATIO_88_192_LO, (unsigned int)(ASRC_FS_RATIO_88_192 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_88_192 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1} // Fsout = 192kHz - }, - { // Fsin = 96kHz - // Nominal Fs Ratio // Minimal Fs Ratio // Maximal Fs Ratio Shift for time step - {ASRC_FS_RATIO_96_44, ASRC_FS_RATIO_96_44_LO, (unsigned int)(ASRC_FS_RATIO_96_44 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_96_44 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_0}, // Fsout = 44.1kHz - {ASRC_FS_RATIO_96_48, ASRC_FS_RATIO_96_48_LO, (unsigned int)(ASRC_FS_RATIO_96_48 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_96_48 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_0}, // Fsout = 48kHz - {ASRC_FS_RATIO_96_88, ASRC_FS_RATIO_96_88_LO, (unsigned int)(ASRC_FS_RATIO_96_88 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_96_88 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 88.2kHz - {ASRC_FS_RATIO_96_96, ASRC_FS_RATIO_96_96_LO, (unsigned int)(ASRC_FS_RATIO_96_96 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_96_96 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 96kHz - {ASRC_FS_RATIO_96_176, ASRC_FS_RATIO_96_176_LO, (unsigned int)(ASRC_FS_RATIO_96_176 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_96_176 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 176.4kHz - {ASRC_FS_RATIO_96_192, ASRC_FS_RATIO_96_192_LO, (unsigned int)(ASRC_FS_RATIO_96_192 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_96_192 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1} // Fsout = 192kHz - }, - { // Fsin = 176.4kHz - // Nominal Fs Ratio // Minimal Fs Ratio // Maximal Fs Ratio Shift for time step - {ASRC_FS_RATIO_176_44, ASRC_FS_RATIO_176_44_LO, (unsigned int)(ASRC_FS_RATIO_176_44 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_176_44 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_M1}, // Fsout = 44.1kHz - {ASRC_FS_RATIO_176_48, ASRC_FS_RATIO_176_48_LO, (unsigned int)(ASRC_FS_RATIO_176_48 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_176_48 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_M1}, // Fsout = 48kHz - {ASRC_FS_RATIO_176_88, ASRC_FS_RATIO_176_88_LO, (unsigned int)(ASRC_FS_RATIO_176_88 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_176_88 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_0}, // Fsout = 88.2kHz - {ASRC_FS_RATIO_176_96, ASRC_FS_RATIO_176_96_LO, (unsigned int)(ASRC_FS_RATIO_176_96 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_176_96 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_0}, // Fsout = 96kHz - {ASRC_FS_RATIO_176_176, ASRC_FS_RATIO_176_176_LO, (unsigned int)(ASRC_FS_RATIO_176_176 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_176_176 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 176.4kHz - {ASRC_FS_RATIO_176_192, ASRC_FS_RATIO_176_192_LO, (unsigned int)(ASRC_FS_RATIO_176_192 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_176_192 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1} // Fsout = 192kHz - }, - { // Fsin = 192kHz - // Nominal Fs Ratio // Minimal Fs Ratio // Maximal Fs Ratio Shift for time step - {ASRC_FS_RATIO_192_44, ASRC_FS_RATIO_192_44_LO, (unsigned int)(ASRC_FS_RATIO_192_44 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_192_44 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_M1}, // Fsout = 44.1kHz - {ASRC_FS_RATIO_192_48, ASRC_FS_RATIO_192_48_LO, (unsigned int)(ASRC_FS_RATIO_192_48 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_192_48 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_M1}, // Fsout = 48kHz - {ASRC_FS_RATIO_192_88, ASRC_FS_RATIO_192_88_LO, (unsigned int)(ASRC_FS_RATIO_192_88 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_192_88 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_0}, // Fsout = 88.2kHz - {ASRC_FS_RATIO_192_96, ASRC_FS_RATIO_192_96_LO, (unsigned int)(ASRC_FS_RATIO_192_96 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_192_96 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_0}, // Fsout = 96kHz - {ASRC_FS_RATIO_192_176, ASRC_FS_RATIO_192_176_LO, (unsigned int)(ASRC_FS_RATIO_192_176 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_192_176 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 176.4kHz - {ASRC_FS_RATIO_192_192, ASRC_FS_RATIO_192_192_LO, (unsigned int)(ASRC_FS_RATIO_192_192 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_192_192 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1} // Fsout = 192kHz - } -}; - - -// =========================================================================== -// -// Local Functions prototypes -// -// =========================================================================== - - - -// =========================================================================== -// -// Functions implementations -// -// =========================================================================== - - -// ==================================================================== // -// Function: ASRC_prepare_coefs // -// Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // -// Return values: ASRC_NO_ERROR on success // -// ASRC_ERROR on failure // -// Description: Prepares the ASRC coefficients from the prototype // -// Needs to be called only once // -// ==================================================================== // -ASRCReturnCodes_t ASRC_prepare_coefs(void) -{ - unsigned int ui, uj; - int* piPrototypeCoefs = iADFirPrototypeCoefs + FILTER_DEFS_ADFIR_PROTOTYPE_N_TAPS - FILTER_DEFS_ADFIR_N_PHASES; - - // First fill in the phases which start normally - for(ui = 0; ui < FILTER_DEFS_ADFIR_N_PHASES; ui++) - { - // Copy phase information - for(uj = 0; uj < FILTER_DEFS_ADFIR_PHASE_N_TAPS - 1; uj++) - iADFirCoefs[ui][uj] = *(piPrototypeCoefs + ui - uj * FILTER_DEFS_ADFIR_N_PHASES); - // Zero fill last coefficient - iADFirCoefs[ui][FILTER_DEFS_ADFIR_PHASE_N_TAPS - 1] = 0; - } - - // Then fill in the two phases which start delayed - for(ui = 0; ui < 2; ui++) - { - // Zero fill first coefficient - iADFirCoefs[FILTER_DEFS_ADFIR_N_PHASES + ui][0] = 0; - // Copy phase informaiton - for(uj = 0; uj < FILTER_DEFS_ADFIR_PHASE_N_TAPS - 1; uj++) - iADFirCoefs[FILTER_DEFS_ADFIR_N_PHASES + ui][uj + 1] = *(piPrototypeCoefs + ui - uj * FILTER_DEFS_ADFIR_N_PHASES); - } - - return ASRC_NO_ERROR; -} - -// ==================================================================== // -// Function: ASRC_init // -// Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // -// Return values: ASRC_NO_ERROR on success // -// ASRC_ERROR on failure // -// Description: Inits the ASRC passed as argument // -// ==================================================================== // -ASRCReturnCodes_t ASRC_init(ASRCCtrl_t* psASRCCtrl) -{ - ASRCFiltersIDs_t* psFiltersID; - FIRDescriptor_t* psFIRDescriptor; - ADFIRDescriptor_t* psADFIRDescriptor; - - - // Check if state is allocated - if(psASRCCtrl->psState == 0) - return ASRC_ERROR; - - // Check if stack is allocated - if(psASRCCtrl->piStack == 0) - return ASRC_ERROR; - - // Check if valid Fsin and Fsout have been provided - if( (psASRCCtrl->eInFs < ASRC_FS_MIN) || (psASRCCtrl->eInFs > ASRC_FS_MAX)) - return ASRC_ERROR; - if( (psASRCCtrl->eOutFs < ASRC_FS_MIN) || (psASRCCtrl->eOutFs > ASRC_FS_MAX)) - return ASRC_ERROR; - - // Set nominal fs ratio - psASRCCtrl->uiFsRatio = sFsRatioConfigs[psASRCCtrl->eInFs][psASRCCtrl->eOutFs].uiNominalFsRatio; - psASRCCtrl->uiFsRatio_lo = sFsRatioConfigs[psASRCCtrl->eInFs][psASRCCtrl->eOutFs].uiNominalFsRatio_lo; - - // Check that number of input samples is allocated and is a multiple of 4 - if(psASRCCtrl->uiNInSamples == 0) - return ASRC_ERROR; - if((psASRCCtrl->uiNInSamples & 0x3) != 0x0) - return ASRC_ERROR; - - // Load filters ID and number of samples - psFiltersID = &sFiltersIDs[psASRCCtrl->eInFs][psASRCCtrl->eOutFs]; - - - // Configure filters F1 and F2 from filters ID and number of samples - - // Filter F1 - // --------- - psFIRDescriptor = &sFirDescriptor[psFiltersID->uiFID[ASRC_F1_INDEX]]; - // Set number of input samples and input samples step - psASRCCtrl->sFIRF1Ctrl.uiNInSamples = psASRCCtrl->uiNInSamples; - psASRCCtrl->sFIRF1Ctrl.uiInStep = ASRC_N_IO_CHANNELS; - // Set delay line base pointer - if( psFiltersID->uiFID[ASRC_F1_INDEX] == FILTER_DEFS_FIR_DS_ID ) - psASRCCtrl->sFIRF1Ctrl.piDelayB = psASRCCtrl->psState->iDelayFIRShort; - else - psASRCCtrl->sFIRF1Ctrl.piDelayB = psASRCCtrl->psState->iDelayFIRLong; - // Set output buffer step - psASRCCtrl->sFIRF1Ctrl.uiOutStep = ASRC_N_CHANNELS; - - // Call init for FIR F1 - if(FIR_init_from_desc(&psASRCCtrl->sFIRF1Ctrl, psFIRDescriptor) != FIR_NO_ERROR) - return FIR_ERROR; - - // Update synchronous number of samples - if( psFiltersID->uiFID[ASRC_F1_INDEX] != FILTER_DEFS_FIR_NONE_ID ) - psASRCCtrl->uiNSyncSamples = psASRCCtrl->sFIRF1Ctrl.uiNOutSamples; - - - // Filter F2 - // --------- - psFIRDescriptor = &sFirDescriptor[psFiltersID->uiFID[ASRC_F2_INDEX]]; - // Set number of input samples and input samples step - psASRCCtrl->sFIRF2Ctrl.uiNInSamples = psASRCCtrl->sFIRF1Ctrl.uiNOutSamples; - psASRCCtrl->sFIRF2Ctrl.uiInStep = psASRCCtrl->sFIRF1Ctrl.uiOutStep; - // Set delay line base pointer (second filter is always long with ASRC) - psASRCCtrl->sFIRF2Ctrl.piDelayB = psASRCCtrl->psState->iDelayFIRLong; - // Set output buffer step - psASRCCtrl->sFIRF2Ctrl.uiOutStep = ASRC_N_CHANNELS; - - // Call init for FIR F2 - if(FIR_init_from_desc(&psASRCCtrl->sFIRF2Ctrl, psFIRDescriptor) != FIR_NO_ERROR) - return FIR_ERROR; - - // Update synchronous number of samples - if( psFiltersID->uiFID[ASRC_F2_INDEX] != FILTER_DEFS_FIR_NONE_ID ) - psASRCCtrl->uiNSyncSamples = psASRCCtrl->sFIRF2Ctrl.uiNOutSamples; - - - // Setup fixed input/output buffers for F1 and F2 - // ---------------------------------------------- - // We set all fixed items (to stack base) - // F1 input is never from stack, so don't set it - psASRCCtrl->sFIRF2Ctrl.piIn = psASRCCtrl->piStack; - psASRCCtrl->sFIRF1Ctrl.piOut = psASRCCtrl->piStack; - psASRCCtrl->sFIRF2Ctrl.piOut = psASRCCtrl->piStack; - - - - // Filter F3 - // --------- - psADFIRDescriptor = &sADFirDescriptor; - - // Set delay line base pointer - psASRCCtrl->sADFIRF3Ctrl.piDelayB = psASRCCtrl->psState->iDelayADFIR; - // Set AD coefficients pointer - psASRCCtrl->sADFIRF3Ctrl.piADCoefs = psASRCCtrl->piADCoefs; - - // Call init for ADFIR F3 - if(ADFIR_init_from_desc(&psASRCCtrl->sADFIRF3Ctrl, psADFIRDescriptor) != FIR_NO_ERROR) - return FIR_ERROR; - - // Call sync function - if(ASRC_sync(psASRCCtrl) != ASRC_NO_ERROR) - return ASRC_ERROR; - - return ASRC_NO_ERROR; -} - - -// ==================================================================== // -// Function: ASRC_sync // -// Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // -// Return values: ASRC_NO_ERROR on success // -// ASRC_ERROR on failure // -// Description: Syncs the ASRC passed as argument // -// ==================================================================== // -ASRCReturnCodes_t ASRC_sync(ASRCCtrl_t* psASRCCtrl) -{ - // Sync the FIR and ADFIR - if(FIR_sync(&psASRCCtrl->sFIRF1Ctrl) != FIR_NO_ERROR) - return ASRC_ERROR; - if(FIR_sync(&psASRCCtrl->sFIRF2Ctrl) != FIR_NO_ERROR) - return ASRC_ERROR; - - if(ADFIR_sync(&psASRCCtrl->sADFIRF3Ctrl) != FIR_NO_ERROR) - return ASRC_ERROR; - - // Reset time - psASRCCtrl->iTimeInt = FILTER_DEFS_ADFIR_N_PHASES + ASRC_ADFIR_INITIAL_PHASE; - psASRCCtrl->uiTimeFract = 0; - - // Reset random seeds to initial values - psASRCCtrl->psState->uiRndSeed = psASRCCtrl->uiRndSeedInit; - - // Update time step based on Fs ratio - if(ASRC_update_fs_ratio(psASRCCtrl) != ASRC_NO_ERROR) - return ASRC_ERROR; - - // Reset cycle counters - psASRCCtrl->fCycleCountF1F2 = 0; - psASRCCtrl->fCycleCountF3AdaptiveCoefs = 0; - psASRCCtrl->fCycleCountF3 = 0; - psASRCCtrl->fCycleCountDither = 0; - - return ASRC_NO_ERROR; -} - - -// ==================================================================== // -// Function: ASRC_proc_F1_F2 // -// Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // -// Return values: ASRC_NO_ERROR on success // -// ASRC_ERROR on failure // -// Description: Processes F1 and F2 for a channel // -// ==================================================================== // -ASRCReturnCodes_t ASRC_proc_F1_F2(ASRCCtrl_t* psASRCCtrl) -{ - int* piIn = psASRCCtrl->piIn; - int* piOut = psASRCCtrl->piOut; - unsigned int ui; - - - // Setup variable input / output buffers - psASRCCtrl->sFIRF1Ctrl.piIn = psASRCCtrl->piIn; - - // F1 is always enabled, so call F1 - if(psASRCCtrl->sFIRF1Ctrl.pvProc(&psASRCCtrl->sFIRF1Ctrl) != FIR_NO_ERROR) - return ASRC_ERROR; - - // Cycle counter estimation - if(psASRCCtrl->sFIRF1Ctrl.pvProc == FIR_proc_os2) - psASRCCtrl->fCycleCountF1F2 += psASRCCtrl->uiNInSamples * ( ASRC_FIR_OS2_OVERHEAD_CYCLE_COUNT + - (psASRCCtrl->sFIRF1Ctrl.uiNCoefs * ASRC_FIR_OS2_TAP_CYCLE_COUNT) ); - - if(psASRCCtrl->sFIRF1Ctrl.pvProc == FIR_proc_ds2) - psASRCCtrl->fCycleCountF1F2 += (psASRCCtrl->uiNInSamples>>1) * ( ASRC_FIR_DS2_OVERHEAD_CYCLE_COUNT + - (psASRCCtrl->sFIRF1Ctrl.uiNCoefs * ASRC_FIR_DS2_TAP_CYCLE_COUNT) ); - - if(psASRCCtrl->sFIRF1Ctrl.pvProc == FIR_proc_sync) - psASRCCtrl->fCycleCountF1F2 += psASRCCtrl->uiNInSamples * ( ASRC_FIR_SYNC_OVERHEAD_CYCLE_COUNT + - (psASRCCtrl->sFIRF1Ctrl.uiNCoefs * ASRC_FIR_SYNC_TAP_CYCLE_COUNT) ); - - - // Check if F2 is enabled - if(psASRCCtrl->sFIRF2Ctrl.eEnable == FIR_ON) - { - // F2 is enabled, so call F2 - if(psASRCCtrl->sFIRF2Ctrl.pvProc(&psASRCCtrl->sFIRF2Ctrl) != FIR_NO_ERROR) - return ASRC_ERROR; - - // Cycle counter estimation - if(psASRCCtrl->sFIRF2Ctrl.pvProc == FIR_proc_os2) - psASRCCtrl->fCycleCountF1F2 += psASRCCtrl->sFIRF2Ctrl.uiNInSamples * ( ASRC_FIR_OS2_OVERHEAD_CYCLE_COUNT + - (psASRCCtrl->sFIRF2Ctrl.uiNCoefs * ASRC_FIR_OS2_TAP_CYCLE_COUNT) ); - - if(psASRCCtrl->sFIRF2Ctrl.pvProc == FIR_proc_ds2) - psASRCCtrl->fCycleCountF1F2 += (psASRCCtrl->sFIRF2Ctrl.uiNInSamples>>1) * ( ASRC_FIR_DS2_OVERHEAD_CYCLE_COUNT + - (psASRCCtrl->sFIRF2Ctrl.uiNCoefs * ASRC_FIR_DS2_TAP_CYCLE_COUNT) ); - - if(psASRCCtrl->sFIRF2Ctrl.pvProc == FIR_proc_sync) - psASRCCtrl->fCycleCountF1F2 += psASRCCtrl->sFIRF2Ctrl.uiNInSamples * ( ASRC_FIR_SYNC_OVERHEAD_CYCLE_COUNT + - (psASRCCtrl->sFIRF2Ctrl.uiNCoefs * ASRC_FIR_SYNC_TAP_CYCLE_COUNT) ); - } - - return ASRC_NO_ERROR; -} - - -// ==================================================================== // -// Function: ASRC_update_fs_ratio // -// Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // -// Return values: ASRC_NO_ERROR on success // -// ASRC_ERROR on failure // -// Description: Updates the ASRC with the new Fs ratio // -// ==================================================================== // -ASRCReturnCodes_t ASRC_update_fs_ratio(ASRCCtrl_t* psASRCCtrl) -{ - unsigned int uiFsRatio = psASRCCtrl->uiFsRatio; - unsigned int uiFsRatio_low = psASRCCtrl->uiFsRatio_lo; - - // Check for bounds of new Fs ratio - if( (uiFsRatio < sFsRatioConfigs[psASRCCtrl->eInFs][psASRCCtrl->eOutFs].uiMinFsRatio) || - (uiFsRatio > sFsRatioConfigs[psASRCCtrl->eInFs][psASRCCtrl->eOutFs].uiMaxFsRatio) ) - return ASRC_ERROR; - - // Apply shift to time ratio to build integer and fractional parts of time step - psASRCCtrl->iTimeStepInt = uiFsRatio >> (sFsRatioConfigs[psASRCCtrl->eInFs][psASRCCtrl->eOutFs].iFsRatioShift); - psASRCCtrl->uiTimeStepFract = uiFsRatio << (32 - sFsRatioConfigs[psASRCCtrl->eInFs][psASRCCtrl->eOutFs].iFsRatioShift); - psASRCCtrl->uiTimeStepFract |= (uint32_t)(uiFsRatio_low >> sFsRatioConfigs[psASRCCtrl->eInFs][psASRCCtrl->eOutFs].iFsRatioShift); - - return ASRC_NO_ERROR; -} - - -// ==================================================================== // -// Function: ASRC_proc_F3_in_spl // -// Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // -// int iInSample: new input sample // -// Return values: ASRC_NO_ERROR on success // -// ASRC_ERROR on failure // -// Description: Writes new input sample to F3 delay line // -// ==================================================================== // -ASRCReturnCodes_t ASRC_proc_F3_in_spl(ASRCCtrl_t* psASRCCtrl, int iInSample) -{ - psASRCCtrl->sADFIRF3Ctrl.iIn = iInSample; - if(ADFIR_proc_in_spl(&psASRCCtrl->sADFIRF3Ctrl) != FIR_NO_ERROR) - return ASRC_ERROR; - - // Decrease next output time (this is an integer value, so no influence on fractional part) - psASRCCtrl->iTimeInt -= FILTER_DEFS_ADFIR_N_PHASES; - - // Update cycle count - psASRCCtrl->fCycleCountF3 += ASRC_ADFIR_IN_SPL_CYCLE_COUNT; - - return ASRC_NO_ERROR; -} - - -// ==================================================================== // -// Function: ASRC_proc_F3_time // -// Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // -// Return values: ASRC_NO_ERROR if an output sample must be produced // -// ASRC_ERROR if no output sample needs to be produced // -// Description: Processes F3 time // -// ==================================================================== // -ASRCReturnCodes_t ASRC_proc_F3_time(ASRCCtrl_t* psASRCCtrl) -{ - unsigned int uiTemp; - int iAlpha; - unsigned int ui; - - int iH0, iH1, iH2; - int iZero; - int iData0; - int iData1; - __int64 i64Acc0; - __int64 i64Acc1; - int* piPhase0; - int* piPhase1; - int* piPhase2; - int* piADCoefs; - - // Check if the next output time instant is in the current time slot - // ----------------------------------------------------------------- - // if not return value showing that no output sample needs to be produced - // Update cycle count - psASRCCtrl->fCycleCountF3AdaptiveCoefs += ASRC_ADFIR_TIME_CHECK_CYCLE_COUNT; - if(psASRCCtrl->iTimeInt >= FILTER_DEFS_ADFIR_N_PHASES) - return ASRC_ERROR; - - - // Update adaptive filter coefficients - // ----------------------------------- - // Coefficients computation load approximation: 15 instructions - // Loop load approximation: 5.625 instructions per sample + 15 instructions overhead => 5.625 * 16 + 15 = 90 + 15 = 105 Instructions - // Total: 125 instructions - - // Set register to zero - iZero = 0; - - // Compute adative coefficients spline factors - // The fractional part of time gives alpha - iAlpha = psASRCCtrl->uiTimeFract>>1; // Now alpha can be seen as a signed number - LMUL(&i64Acc0, iAlpha, iAlpha, iZero, iZero); // Compte H0 = 0.5 * alpha * alpha - iH0 = (int)(i64Acc0>>32); - iH2 = 0x40000000; // Load H2 with 0.5; - iH1 = iH2 - iH0; // H1 = 0.5 - 0.5 * alpha * alpha; - iH1 = iH1 - iH0; // H1 = 0.5 - alpha * alpha - iH1 = iH1 + iAlpha; // H1 = 0.5 + alpha - alpha * alpha; - iH2 = iH2 - iAlpha; // H2 = 0.5 - alpha - iH2 = iH2 + iH0; // H2 = 0.5 - alpha + 0.5 * alpha * alpha - - // The integer part of time gives the phase - piPhase0 = iADFirCoefs[psASRCCtrl->iTimeInt]; - piPhase1 = piPhase0 + FILTER_DEFS_ADFIR_PHASE_N_TAPS; - piPhase2 = piPhase1 + FILTER_DEFS_ADFIR_PHASE_N_TAPS; - piADCoefs = psASRCCtrl->piADCoefs; // Given limited number of registers, this should be DP - - // Apply spline coefficients to filter coefficients - for(ui = 0; ui < FILTER_DEFS_ADFIR_PHASE_N_TAPS; ui += 2) - { - // Double read (long word) - iData0 = *(piPhase0 + ui); - iData1 = *(piPhase0 + ui + 1); - // MACC for each word - LMUL(&i64Acc0, iH2, iData0, iZero, iZero); - LMUL(&i64Acc1, iH2, iData1, iZero, iZero); - - // Double read (long word) - iData0 = *(piPhase1 + ui); - iData1 = *(piPhase1 + ui + 1); - // Double read (long word) - MACC(&i64Acc0, iH1, iData0); - MACC(&i64Acc1, iH1, iData1); - - // Double read (long word) - iData0 = *(piPhase2 + ui); - iData1 = *(piPhase2 + ui + 1); - // Double read (long word) - MACC(&i64Acc0, iH0, iData0); - MACC(&i64Acc1, iH0, iData1); - - // Adaptive coefficient is given by upper part of accumulator - // Use two writes of single word (as we don't have enough registers, piADCoefs should be DP - *(piADCoefs + ui) = (int)(i64Acc0>>32); - *(piADCoefs + ui + 1) = (int)(i64Acc1>>32); - } - - // Step time for next output sample - // -------------------------------- - // Step to next output time (add integer and fractional parts) - psASRCCtrl->iTimeInt += psASRCCtrl->iTimeStepInt; - // For fractional part, this can be optimized using the add with carry instruction of XS2 - uiTemp = psASRCCtrl->uiTimeFract; - psASRCCtrl->uiTimeFract += psASRCCtrl->uiTimeStepFract; - if(psASRCCtrl->uiTimeFract < uiTemp) - psASRCCtrl->iTimeInt++; - - //printf("TimeInt = %x TimeFract = %x", psASRCCtrl->iTimeInt, psASRCCtrl->uiTimeFract); - - // Increase cycle counter - psASRCCtrl->fCycleCountF3AdaptiveCoefs += ASRC_ADFIR_TIME_SAMPLE_CYCLE_COUNT; - - // Return value showing that an output sample must be produced - return ASRC_NO_ERROR; -} - - -// ==================================================================== // -// Function: ASRC_proc_F3_macc // -// Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // -// int* piOutSample: Address of output sample // -// Return values: ASRC_NO_ERROR on success // -// ASRC_ERROR on failure // -// Description: Processes F3 for a channel // -// ==================================================================== // -ASRCReturnCodes_t ASRC_proc_F3_macc(ASRCCtrl_t* psASRCCtrl, int* piOutSample) -{ - psASRCCtrl->sADFIRF3Ctrl.piOut = piOutSample; - // Call processing function - if(ADFIR_proc_macc(&psASRCCtrl->sADFIRF3Ctrl) != FIR_NO_ERROR) - return ASRC_ERROR; - - psASRCCtrl->uiNASRCOutSamples++; - - // Cycle counter estimation - psASRCCtrl->fCycleCountF3 += ASRC_ADFIR_MACC_OVERHEAD_CYCLE_COUNT + (psASRCCtrl->sADFIRF3Ctrl.uiNLoops * 2.0 * ASRC_ADFIR_MACC_TAP_CYCLE_COUNT); - - return ASRC_NO_ERROR; -} - - -// ==================================================================== // -// Function: ASRC_proc_dither // -// Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // -// Return values: ASRC_NO_ERROR on success // -// ASRC_ERROR on failure // -// Description: Processes dither for a channel // -// ==================================================================== // -ASRCReturnCodes_t ASRC_proc_dither(ASRCCtrl_t* psASRCCtrl) -{ - int* piData; - unsigned int uiR; - int iDither; - __int64 i64Acc; - unsigned int ui; - - - // Apply dither if required - if(psASRCCtrl->uiDitherOnOff == ASRC_DITHER_ON) - { - // Get data buffer - piData = psASRCCtrl->piOut; - // Get random seed - uiR = psASRCCtrl->psState->uiRndSeed; - - // Loop through samples - for(ui = 0; ui < psASRCCtrl->uiNASRCOutSamples * ASRC_N_IO_CHANNELS; ui += ASRC_N_IO_CHANNELS) - { - // Compute dither sample (TPDF) - iDither = ASRC_DITHER_BIAS; - - uiR = (unsigned int)(ASRC_R_BASE * uiR); - uiR = (unsigned int)(ASRC_R_CONS + uiR); - iDither += ((uiR>>ASRC_RPDF_BITS_SHIFT) & ASRC_RPDF_MASK); - - uiR = (unsigned int)(ASRC_R_BASE * uiR); - uiR = (unsigned int)(ASRC_R_CONS + uiR); - iDither += ((uiR>>ASRC_RPDF_BITS_SHIFT) & ASRC_RPDF_MASK); - - // Use MACC instruction to saturate and dither + signal - i64Acc = ((__int64)iDither <<32); // On XMOS this is not necessary, just load dither in the top word of the ACC register - MACC(&i64Acc, piData[ui], 0x7FFFFFFF); - LSAT30(&i64Acc); - // Extract 32bits result - EXT30(&piData[ui], i64Acc); - - // Mask to 24bits - piData[ui] &= ASRC_DATA24_MASK; - - // Increase cycle counter - psASRCCtrl->fCycleCountDither += ASRC_DITHER_SAMPLE_COUNT; - } - - // Write random seed back - psASRCCtrl->psState->uiRndSeed = uiR; - } - - return ASRC_NO_ERROR; -} +// Copyright 2023 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +// =========================================================================== +// =========================================================================== +// +// File: ASRC.c +// +// Top level implementation file for the ASRC +// +// Target: MS Windows +// Version: 1.0 +// +// =========================================================================== +// =========================================================================== + + +// =========================================================================== +// +// Includes +// +// =========================================================================== +#include +#include +#include +#include + +// Integer arithmetic include +#include "IntArithmetic.h" +// ASRC include +#include "ASRC.h" + +// =========================================================================== +// +// Defines +// +// =========================================================================== + + +// State init value +#define ASRC_STATE_INIT 0 + +// Nominal Fs Ratio values +#define ASRC_FS_44_F (44100.0) +#define ASRC_FS_48_F (48000.0) +#define ASRC_FS_88_F (88200.0) +#define ASRC_FS_96_F (96000.0) +#define ASRC_FS_176_F (176400.0) +#define ASRC_FS_192_F (192000.0) + +#define ASRC_FS_RATIO_44_44 (uint32_t)((unsigned long long)(((double)ASRC_FS_44_F / ASRC_FS_44_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_44_44_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_44_F / ASRC_FS_44_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) +#define ASRC_FS_RATIO_44_48 (uint32_t)((unsigned long long)(((double)ASRC_FS_44_F / ASRC_FS_48_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_44_48_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_44_F / ASRC_FS_48_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) +#define ASRC_FS_RATIO_44_88 (uint32_t)((unsigned long long)(((double)ASRC_FS_44_F / ASRC_FS_88_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_44_88_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_44_F / ASRC_FS_88_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) +#define ASRC_FS_RATIO_44_96 (uint32_t)((unsigned long long)(((double)ASRC_FS_44_F / ASRC_FS_96_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_44_96_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_44_F / ASRC_FS_96_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) +#define ASRC_FS_RATIO_44_176 (uint32_t)((unsigned long long)(((double)ASRC_FS_44_F / ASRC_FS_176_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_44_176_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_44_F / ASRC_FS_176_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) +#define ASRC_FS_RATIO_44_192 (uint32_t)((unsigned long long)(((double)ASRC_FS_44_F / ASRC_FS_192_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_44_192_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_44_F / ASRC_FS_192_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) + +#define ASRC_FS_RATIO_48_44 (uint32_t)((unsigned long long)(((double)ASRC_FS_48_F / ASRC_FS_44_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_48_44_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_48_F / ASRC_FS_44_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) +#define ASRC_FS_RATIO_48_48 (uint32_t)((unsigned long long)(((double)ASRC_FS_48_F / ASRC_FS_48_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_48_48_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_48_F / ASRC_FS_48_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) +#define ASRC_FS_RATIO_48_88 (uint32_t)((unsigned long long)(((double)ASRC_FS_48_F / ASRC_FS_88_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_48_88_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_48_F / ASRC_FS_88_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) +#define ASRC_FS_RATIO_48_96 (uint32_t)((unsigned long long)(((double)ASRC_FS_48_F / ASRC_FS_96_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_48_96_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_48_F / ASRC_FS_96_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) +#define ASRC_FS_RATIO_48_176 (uint32_t)((unsigned long long)(((double)ASRC_FS_48_F / ASRC_FS_176_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_48_176_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_48_F / ASRC_FS_176_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) +#define ASRC_FS_RATIO_48_192 (uint32_t)((unsigned long long)(((double)ASRC_FS_48_F / ASRC_FS_192_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_48_192_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_48_F / ASRC_FS_192_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) + +#define ASRC_FS_RATIO_88_44 (uint32_t)((unsigned long long)(((double)ASRC_FS_88_F / ASRC_FS_44_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_88_44_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_88_F / ASRC_FS_44_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) +#define ASRC_FS_RATIO_88_48 (uint32_t)((unsigned long long)(((double)ASRC_FS_88_F / ASRC_FS_48_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_88_48_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_88_F / ASRC_FS_48_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) +#define ASRC_FS_RATIO_88_88 (uint32_t)((unsigned long long)(((double)ASRC_FS_88_F / ASRC_FS_88_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_88_88_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_88_F / ASRC_FS_88_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) +#define ASRC_FS_RATIO_88_96 (uint32_t)((unsigned long long)(((double)ASRC_FS_88_F / ASRC_FS_96_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_88_96_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_88_F / ASRC_FS_96_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) +#define ASRC_FS_RATIO_88_176 (uint32_t)((unsigned long long)(((double)ASRC_FS_88_F / ASRC_FS_176_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_88_176_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_88_F / ASRC_FS_176_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) +#define ASRC_FS_RATIO_88_192 (uint32_t)((unsigned long long)(((double)ASRC_FS_88_F / ASRC_FS_192_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_88_192_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_88_F / ASRC_FS_192_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) + +#define ASRC_FS_RATIO_96_44 (uint32_t)((unsigned long long)(((double)ASRC_FS_96_F / ASRC_FS_44_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_96_44_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_96_F / ASRC_FS_44_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) +#define ASRC_FS_RATIO_96_48 (uint32_t)((unsigned long long)(((double)ASRC_FS_96_F / ASRC_FS_48_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_96_48_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_96_F / ASRC_FS_48_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) +#define ASRC_FS_RATIO_96_88 (uint32_t)((unsigned long long)(((double)ASRC_FS_96_F / ASRC_FS_88_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_96_88_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_96_F / ASRC_FS_88_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) +#define ASRC_FS_RATIO_96_96 (uint32_t)((unsigned long long)(((double)ASRC_FS_96_F / ASRC_FS_96_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_96_96_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_96_F / ASRC_FS_96_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) +#define ASRC_FS_RATIO_96_176 (uint32_t)((unsigned long long)(((double)ASRC_FS_96_F / ASRC_FS_176_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_96_176_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_96_F / ASRC_FS_176_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) +#define ASRC_FS_RATIO_96_192 (uint32_t)((unsigned long long)(((double)ASRC_FS_96_F / ASRC_FS_192_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_96_192_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_96_F / ASRC_FS_192_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) + +#define ASRC_FS_RATIO_176_44 (uint32_t)((unsigned long long)(((double)ASRC_FS_176_F / ASRC_FS_44_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_176_44_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_176_F / ASRC_FS_44_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) +#define ASRC_FS_RATIO_176_48 (uint32_t)((unsigned long long)(((double)ASRC_FS_176_F / ASRC_FS_48_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_176_48_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_176_F / ASRC_FS_48_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) +#define ASRC_FS_RATIO_176_88 (uint32_t)((unsigned long long)(((double)ASRC_FS_176_F / ASRC_FS_88_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_176_88_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_176_F / ASRC_FS_88_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) +#define ASRC_FS_RATIO_176_96 (uint32_t)((unsigned long long)(((double)ASRC_FS_176_F / ASRC_FS_96_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_176_96_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_176_F / ASRC_FS_96_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) +#define ASRC_FS_RATIO_176_176 (uint32_t)((unsigned long long)(((double)ASRC_FS_176_F / ASRC_FS_176_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_176_176_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_176_F / ASRC_FS_176_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) +#define ASRC_FS_RATIO_176_192 (uint32_t)((unsigned long long)(((double)ASRC_FS_176_F / ASRC_FS_192_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_176_192_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_176_F / ASRC_FS_192_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) + +#define ASRC_FS_RATIO_192_44 (uint32_t)((unsigned long long)(((double)ASRC_FS_192_F / ASRC_FS_44_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_192_44_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_192_F / ASRC_FS_44_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) +#define ASRC_FS_RATIO_192_48 (uint32_t)((unsigned long long)(((double)ASRC_FS_192_F / ASRC_FS_48_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_192_48_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_192_F / ASRC_FS_48_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) +#define ASRC_FS_RATIO_192_88 (uint32_t)((unsigned long long)(((double)ASRC_FS_192_F / ASRC_FS_88_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_192_88_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_192_F / ASRC_FS_88_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) +#define ASRC_FS_RATIO_192_96 (uint32_t)((unsigned long long)(((double)ASRC_FS_192_F / ASRC_FS_96_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_192_96_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_192_F / ASRC_FS_96_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) +#define ASRC_FS_RATIO_192_176 (uint32_t)((unsigned long long)(((double)ASRC_FS_192_F / ASRC_FS_176_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_192_176_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_192_F / ASRC_FS_176_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) +#define ASRC_FS_RATIO_192_192 (uint32_t)((unsigned long long)(((double)ASRC_FS_192_F / ASRC_FS_192_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32))) >> 32) +#define ASRC_FS_RATIO_192_192_LO (uint32_t)((unsigned long long)(((double)ASRC_FS_192_F / ASRC_FS_192_F) * ((unsigned long long)1 << (ASRC_FS_RATIO_UNIT_BIT + 32)))) + + +#define ASRC_FS_RATIO_MIN_FACTOR (0.99) +#define ASRC_FS_RATIO_MAX_FACTOR (1.01) + +#define ASRC_ADFIR_INITIAL_PHASE 32 + +// Time ratio shift values +#define ASRC_FS_RATIO_UNIT_BIT 28 +#define ASRC_FS_RATIO_PHASE_N_BITS 7 +#define ASRC_FS_RATIO_BASE_SHIFT (ASRC_FS_RATIO_UNIT_BIT - ASRC_FS_RATIO_PHASE_N_BITS) +#define ASRC_FS_RATIO_SHIFT_M1 (ASRC_FS_RATIO_BASE_SHIFT + 1) +#define ASRC_FS_RATIO_SHIFT_0 (ASRC_FS_RATIO_BASE_SHIFT) +#define ASRC_FS_RATIO_SHIFT_P1 (ASRC_FS_RATIO_BASE_SHIFT - 1) + + +// Random number generator / dithering +#define ASRC_R_CONS 32767 +#define ASRC_R_BASE 1664525 +#define ASRC_RPDF_BITS_SHIFT 16 // Shift to select bits in pseudo-random number +#define ASRC_RPDF_MASK 0x0000007F // For dithering at 24bits (in 2.30) +#define ASRC_DATA24_MASK 0xFFFFFF00 // Mask for 24bits data (once rescaled to 1.31) +#define ASRC_DITHER_BIAS 0xFFFFFFC0 // TPDF dither bias for compensating masking at 24bits but expressed in 2.30 + + +// Cycle counters +#define ASRC_FIR_OS2_OVERHEAD_CYCLE_COUNT (15.0) +#define ASRC_FIR_OS2_TAP_CYCLE_COUNT (2.125) +#define ASRC_FIR_DS2_OVERHEAD_CYCLE_COUNT (15.0) +#define ASRC_FIR_DS2_TAP_CYCLE_COUNT (2.625) +#define ASRC_FIR_SYNC_OVERHEAD_CYCLE_COUNT (15.0) +#define ASRC_FIR_SYNC_TAP_CYCLE_COUNT (2.625) + +#define ASRC_ADFIR_IN_SPL_CYCLE_COUNT (8.0) +#define ASRC_ADFIR_TIME_CHECK_CYCLE_COUNT (5.0) +#define ASRC_ADFIR_TIME_SAMPLE_CYCLE_COUNT (15.0 + 15.0 + 90.0) +#define ASRC_ADFIR_MACC_OVERHEAD_CYCLE_COUNT (8.0) +#define ASRC_ADFIR_MACC_TAP_CYCLE_COUNT (2.5) + +#define ASRC_DITHER_SAMPLE_COUNT (20.0) + + + +// =========================================================================== +// +// Variables +// +// =========================================================================== + +ASRCFiltersIDs_t sFiltersIDs[ASRC_N_FS][ASRC_N_FS] = // Filter configuration table [Fsin][Fsout] +{ + { // Fsin = 44.1kHz + // F1 F2 + {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 44.1kHz + {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 48kHz + {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 88.2kHz + {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 96kHz + {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 176.4kHz + {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID} // Fsout = 192kHz + }, + { // Fsin = 48kHz + // F1 F2 + {FILTER_DEFS_FIR_UP4844_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 44.1kHz + {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 48kHz + {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 88.2kHz + {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 96kHz + {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 176.4kHz + {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID} // Fsout = 192kHz + }, + { // Fsin = 88.2kHz + // F1 F2 + {FILTER_DEFS_FIR_BL_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 44.1kHz + {FILTER_DEFS_FIR_BL8848_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 48kHz + {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 88.2kHz + {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 96kHz + {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 176.4kHz + {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID} // Fsout = 192kHz + }, + { // Fsin = 96kHz + // F1 F2 + {FILTER_DEFS_FIR_BL9644_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 44.1kHz + {FILTER_DEFS_FIR_BL_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 48kHz + {FILTER_DEFS_FIR_UP4844_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 88.2kHz + {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 96kHz + {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 176.4kHz + {FILTER_DEFS_FIR_UP_ID, FILTER_DEFS_FIR_NONE_ID} // Fsout = 192kHz + }, + { // Fsin = 176.4kHz + // F1 F2 + {FILTER_DEFS_FIR_DS_ID, FILTER_DEFS_FIR_BL_ID}, // Fsout = 44.1kHz + {FILTER_DEFS_FIR_DS_ID, FILTER_DEFS_FIR_BL8848_ID}, // Fsout = 48kHz + {FITLER_DEFS_FIR_BLF_ID, FILTER_DEFS_FIR_NONE_ID,}, // Fsout = 88.2kHz + {FILTER_DEFS_FIR_BL17696_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 96kHz + {FILTER_DEFS_FIR_UPF_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 176.4kHz + {FILTER_DEFS_FIR_UPF_ID, FILTER_DEFS_FIR_NONE_ID} // Fsout = 192kHz + }, + { // Fsin = 192kHz + // F1 F2 + {FILTER_DEFS_FIR_DS_ID, FILTER_DEFS_FIR_BL9644_ID}, // Fsout = 44.1kHz + {FILTER_DEFS_FIR_DS_ID, FILTER_DEFS_FIR_BL_ID}, // Fsout = 48kHz + {FITLER_DEFS_FIR_BL19288_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 88.2kHz + {FITLER_DEFS_FIR_BLF_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 96kHz + {FILTER_DEFS_FIR_UP192176_ID, FILTER_DEFS_FIR_NONE_ID}, // Fsout = 176.4kHz + {FILTER_DEFS_FIR_UPF_ID, FILTER_DEFS_FIR_NONE_ID} // Fsout = 192kHz + } +}; + +ASRCFsRatioConfigs_t sFsRatioConfigs[ASRC_N_FS][ASRC_N_FS] = // Fs ratio configuration table [Fsin][Fsout] +{ + { // Fsin = 44.1kHz + // Nominal Fs Ratio // Minimal Fs Ratio // Maximal Fs Ratio Shift for time step + {ASRC_FS_RATIO_44_44, ASRC_FS_RATIO_44_44_LO, (unsigned int)(ASRC_FS_RATIO_44_44 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_44_44 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 44.1kHz + {ASRC_FS_RATIO_44_48, ASRC_FS_RATIO_44_48_LO, (unsigned int)(ASRC_FS_RATIO_44_48 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_44_48 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 48kHz + {ASRC_FS_RATIO_44_88, ASRC_FS_RATIO_44_88_LO, (unsigned int)(ASRC_FS_RATIO_44_88 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_44_88 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 88.2kHz + {ASRC_FS_RATIO_44_96, ASRC_FS_RATIO_44_96_LO, (unsigned int)(ASRC_FS_RATIO_44_96 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_44_96 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 96kHz + {ASRC_FS_RATIO_44_176, ASRC_FS_RATIO_44_176_LO, (unsigned int)(ASRC_FS_RATIO_44_176 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_44_176 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 176.4kHz + {ASRC_FS_RATIO_44_192, ASRC_FS_RATIO_44_192_LO, (unsigned int)(ASRC_FS_RATIO_44_192 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_44_192 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1} // Fsout = 192kHz + }, + { // Fsin = 48kHz + // Nominal Fs Ratio // Minimal Fs Ratio // Maximal Fs Ratio Shift for time step + {ASRC_FS_RATIO_48_44, ASRC_FS_RATIO_48_44_LO, (unsigned int)(ASRC_FS_RATIO_48_44 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_48_44 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 44.1kHz + {ASRC_FS_RATIO_48_48, ASRC_FS_RATIO_48_48_LO, (unsigned int)(ASRC_FS_RATIO_48_48 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_48_48 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 48kHz + {ASRC_FS_RATIO_48_88, ASRC_FS_RATIO_48_88_LO, (unsigned int)(ASRC_FS_RATIO_48_88 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_48_88 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 88.2kHz + {ASRC_FS_RATIO_48_96, ASRC_FS_RATIO_48_96_LO, (unsigned int)(ASRC_FS_RATIO_48_96 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_48_96 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 96kHz + {ASRC_FS_RATIO_48_176, ASRC_FS_RATIO_48_176_LO, (unsigned int)(ASRC_FS_RATIO_48_176 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_48_176 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 176.4kHz + {ASRC_FS_RATIO_48_192, ASRC_FS_RATIO_48_192_LO, (unsigned int)(ASRC_FS_RATIO_48_192 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_48_192 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1} // Fsout = 192kHz + }, + { // Fsin = 88.2kHz + // Nominal Fs Ratio // Minimal Fs Ratio // Maximal Fs Ratio Shift for time step + {ASRC_FS_RATIO_88_44, ASRC_FS_RATIO_88_44_LO, (unsigned int)(ASRC_FS_RATIO_88_44 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_88_44 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_0}, // Fsout = 44.1kHz + {ASRC_FS_RATIO_88_48, ASRC_FS_RATIO_88_48_LO, (unsigned int)(ASRC_FS_RATIO_88_48 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_88_48 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_0}, // Fsout = 48kHz + {ASRC_FS_RATIO_88_88, ASRC_FS_RATIO_88_88_LO, (unsigned int)(ASRC_FS_RATIO_88_88 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_88_88 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 88.2kHz + {ASRC_FS_RATIO_88_96, ASRC_FS_RATIO_88_96_LO, (unsigned int)(ASRC_FS_RATIO_88_96 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_88_96 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 96kHz + {ASRC_FS_RATIO_88_176, ASRC_FS_RATIO_88_176_LO, (unsigned int)(ASRC_FS_RATIO_88_176 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_88_176 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 176.4kHz + {ASRC_FS_RATIO_88_192, ASRC_FS_RATIO_88_192_LO, (unsigned int)(ASRC_FS_RATIO_88_192 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_88_192 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1} // Fsout = 192kHz + }, + { // Fsin = 96kHz + // Nominal Fs Ratio // Minimal Fs Ratio // Maximal Fs Ratio Shift for time step + {ASRC_FS_RATIO_96_44, ASRC_FS_RATIO_96_44_LO, (unsigned int)(ASRC_FS_RATIO_96_44 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_96_44 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_0}, // Fsout = 44.1kHz + {ASRC_FS_RATIO_96_48, ASRC_FS_RATIO_96_48_LO, (unsigned int)(ASRC_FS_RATIO_96_48 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_96_48 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_0}, // Fsout = 48kHz + {ASRC_FS_RATIO_96_88, ASRC_FS_RATIO_96_88_LO, (unsigned int)(ASRC_FS_RATIO_96_88 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_96_88 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 88.2kHz + {ASRC_FS_RATIO_96_96, ASRC_FS_RATIO_96_96_LO, (unsigned int)(ASRC_FS_RATIO_96_96 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_96_96 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 96kHz + {ASRC_FS_RATIO_96_176, ASRC_FS_RATIO_96_176_LO, (unsigned int)(ASRC_FS_RATIO_96_176 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_96_176 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 176.4kHz + {ASRC_FS_RATIO_96_192, ASRC_FS_RATIO_96_192_LO, (unsigned int)(ASRC_FS_RATIO_96_192 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_96_192 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1} // Fsout = 192kHz + }, + { // Fsin = 176.4kHz + // Nominal Fs Ratio // Minimal Fs Ratio // Maximal Fs Ratio Shift for time step + {ASRC_FS_RATIO_176_44, ASRC_FS_RATIO_176_44_LO, (unsigned int)(ASRC_FS_RATIO_176_44 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_176_44 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_M1}, // Fsout = 44.1kHz + {ASRC_FS_RATIO_176_48, ASRC_FS_RATIO_176_48_LO, (unsigned int)(ASRC_FS_RATIO_176_48 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_176_48 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_M1}, // Fsout = 48kHz + {ASRC_FS_RATIO_176_88, ASRC_FS_RATIO_176_88_LO, (unsigned int)(ASRC_FS_RATIO_176_88 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_176_88 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_0}, // Fsout = 88.2kHz + {ASRC_FS_RATIO_176_96, ASRC_FS_RATIO_176_96_LO, (unsigned int)(ASRC_FS_RATIO_176_96 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_176_96 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_0}, // Fsout = 96kHz + {ASRC_FS_RATIO_176_176, ASRC_FS_RATIO_176_176_LO, (unsigned int)(ASRC_FS_RATIO_176_176 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_176_176 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 176.4kHz + {ASRC_FS_RATIO_176_192, ASRC_FS_RATIO_176_192_LO, (unsigned int)(ASRC_FS_RATIO_176_192 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_176_192 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1} // Fsout = 192kHz + }, + { // Fsin = 192kHz + // Nominal Fs Ratio // Minimal Fs Ratio // Maximal Fs Ratio Shift for time step + {ASRC_FS_RATIO_192_44, ASRC_FS_RATIO_192_44_LO, (unsigned int)(ASRC_FS_RATIO_192_44 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_192_44 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_M1}, // Fsout = 44.1kHz + {ASRC_FS_RATIO_192_48, ASRC_FS_RATIO_192_48_LO, (unsigned int)(ASRC_FS_RATIO_192_48 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_192_48 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_M1}, // Fsout = 48kHz + {ASRC_FS_RATIO_192_88, ASRC_FS_RATIO_192_88_LO, (unsigned int)(ASRC_FS_RATIO_192_88 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_192_88 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_0}, // Fsout = 88.2kHz + {ASRC_FS_RATIO_192_96, ASRC_FS_RATIO_192_96_LO, (unsigned int)(ASRC_FS_RATIO_192_96 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_192_96 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_0}, // Fsout = 96kHz + {ASRC_FS_RATIO_192_176, ASRC_FS_RATIO_192_176_LO, (unsigned int)(ASRC_FS_RATIO_192_176 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_192_176 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1}, // Fsout = 176.4kHz + {ASRC_FS_RATIO_192_192, ASRC_FS_RATIO_192_192_LO, (unsigned int)(ASRC_FS_RATIO_192_192 * ASRC_FS_RATIO_MIN_FACTOR), (unsigned int)(ASRC_FS_RATIO_192_192 * ASRC_FS_RATIO_MAX_FACTOR), ASRC_FS_RATIO_SHIFT_P1} // Fsout = 192kHz + } +}; + + +// =========================================================================== +// +// Local Functions prototypes +// +// =========================================================================== + + + +// =========================================================================== +// +// Functions implementations +// +// =========================================================================== + + +// ==================================================================== // +// Function: ASRC_prepare_coefs // +// Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // +// Return values: ASRC_NO_ERROR on success // +// ASRC_ERROR on failure // +// Description: Prepares the ASRC coefficients from the prototype // +// Needs to be called only once // +// ==================================================================== // +ASRCReturnCodes_t ASRC_prepare_coefs(void) +{ + unsigned int ui, uj; + int* piPrototypeCoefs = iADFirPrototypeCoefs + FILTER_DEFS_ADFIR_PROTOTYPE_N_TAPS - FILTER_DEFS_ADFIR_N_PHASES; + + // First fill in the phases which start normally + for(ui = 0; ui < FILTER_DEFS_ADFIR_N_PHASES; ui++) + { + // Copy phase information + for(uj = 0; uj < FILTER_DEFS_ADFIR_PHASE_N_TAPS - 1; uj++) + iADFirCoefs[ui][uj] = *(piPrototypeCoefs + ui - uj * FILTER_DEFS_ADFIR_N_PHASES); + // Zero fill last coefficient + iADFirCoefs[ui][FILTER_DEFS_ADFIR_PHASE_N_TAPS - 1] = 0; + } + + // Then fill in the two phases which start delayed + for(ui = 0; ui < 2; ui++) + { + // Zero fill first coefficient + iADFirCoefs[FILTER_DEFS_ADFIR_N_PHASES + ui][0] = 0; + // Copy phase informaiton + for(uj = 0; uj < FILTER_DEFS_ADFIR_PHASE_N_TAPS - 1; uj++) + iADFirCoefs[FILTER_DEFS_ADFIR_N_PHASES + ui][uj + 1] = *(piPrototypeCoefs + ui - uj * FILTER_DEFS_ADFIR_N_PHASES); + } + + return ASRC_NO_ERROR; +} + +// ==================================================================== // +// Function: ASRC_init // +// Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // +// Return values: ASRC_NO_ERROR on success // +// ASRC_ERROR on failure // +// Description: Inits the ASRC passed as argument // +// ==================================================================== // +ASRCReturnCodes_t ASRC_init(ASRCCtrl_t* psASRCCtrl) +{ + ASRCFiltersIDs_t* psFiltersID; + FIRDescriptor_t* psFIRDescriptor; + ADFIRDescriptor_t* psADFIRDescriptor; + + + // Check if state is allocated + if(psASRCCtrl->psState == 0) + return ASRC_ERROR; + + // Check if stack is allocated + if(psASRCCtrl->piStack == 0) + return ASRC_ERROR; + + // Check if valid Fsin and Fsout have been provided + if( (psASRCCtrl->eInFs < ASRC_FS_MIN) || (psASRCCtrl->eInFs > ASRC_FS_MAX)) + return ASRC_ERROR; + if( (psASRCCtrl->eOutFs < ASRC_FS_MIN) || (psASRCCtrl->eOutFs > ASRC_FS_MAX)) + return ASRC_ERROR; + + // Set nominal fs ratio + psASRCCtrl->uiFsRatio = sFsRatioConfigs[psASRCCtrl->eInFs][psASRCCtrl->eOutFs].uiNominalFsRatio; + psASRCCtrl->uiFsRatio_lo = sFsRatioConfigs[psASRCCtrl->eInFs][psASRCCtrl->eOutFs].uiNominalFsRatio_lo; + + // Check that number of input samples is allocated and is a multiple of 4 + if(psASRCCtrl->uiNInSamples == 0) + return ASRC_ERROR; + if((psASRCCtrl->uiNInSamples & 0x3) != 0x0) + return ASRC_ERROR; + + // Load filters ID and number of samples + psFiltersID = &sFiltersIDs[psASRCCtrl->eInFs][psASRCCtrl->eOutFs]; + + + // Configure filters F1 and F2 from filters ID and number of samples + + // Filter F1 + // --------- + psFIRDescriptor = &sFirDescriptor[psFiltersID->uiFID[ASRC_F1_INDEX]]; + // Set number of input samples and input samples step + psASRCCtrl->sFIRF1Ctrl.uiNInSamples = psASRCCtrl->uiNInSamples; + psASRCCtrl->sFIRF1Ctrl.uiInStep = psASRCCtrl->uiInStep; + // Set delay line base pointer + if( psFiltersID->uiFID[ASRC_F1_INDEX] == FILTER_DEFS_FIR_DS_ID ) + psASRCCtrl->sFIRF1Ctrl.piDelayB = psASRCCtrl->psState->iDelayFIRShort; + else + psASRCCtrl->sFIRF1Ctrl.piDelayB = psASRCCtrl->psState->iDelayFIRLong; + // Set output buffer step + psASRCCtrl->sFIRF1Ctrl.uiOutStep = psASRCCtrl->uiOutStep; + + // Call init for FIR F1 + if(FIR_init_from_desc(&psASRCCtrl->sFIRF1Ctrl, psFIRDescriptor) != FIR_NO_ERROR) + return FIR_ERROR; + + // Update synchronous number of samples + if( psFiltersID->uiFID[ASRC_F1_INDEX] != FILTER_DEFS_FIR_NONE_ID ) + psASRCCtrl->uiNSyncSamples = psASRCCtrl->sFIRF1Ctrl.uiNOutSamples; + + + // Filter F2 + // --------- + psFIRDescriptor = &sFirDescriptor[psFiltersID->uiFID[ASRC_F2_INDEX]]; + // Set number of input samples and input samples step + psASRCCtrl->sFIRF2Ctrl.uiNInSamples = psASRCCtrl->sFIRF1Ctrl.uiNOutSamples; + psASRCCtrl->sFIRF2Ctrl.uiInStep = psASRCCtrl->sFIRF1Ctrl.uiOutStep; + // Set delay line base pointer (second filter is always long with ASRC) + psASRCCtrl->sFIRF2Ctrl.piDelayB = psASRCCtrl->psState->iDelayFIRLong; + // Set output buffer step + psASRCCtrl->sFIRF2Ctrl.uiOutStep = psASRCCtrl->uiOutStep; + + // Call init for FIR F2 + if(FIR_init_from_desc(&psASRCCtrl->sFIRF2Ctrl, psFIRDescriptor) != FIR_NO_ERROR) + return FIR_ERROR; + + // Update synchronous number of samples + if( psFiltersID->uiFID[ASRC_F2_INDEX] != FILTER_DEFS_FIR_NONE_ID ) + psASRCCtrl->uiNSyncSamples = psASRCCtrl->sFIRF2Ctrl.uiNOutSamples; + + + // Setup fixed input/output buffers for F1 and F2 + // ---------------------------------------------- + // We set all fixed items (to stack base) + // F1 input is never from stack, so don't set it + psASRCCtrl->sFIRF2Ctrl.piIn = psASRCCtrl->piStack; + psASRCCtrl->sFIRF1Ctrl.piOut = psASRCCtrl->piStack; + psASRCCtrl->sFIRF2Ctrl.piOut = psASRCCtrl->piStack; + + + + // Filter F3 + // --------- + psADFIRDescriptor = &sADFirDescriptor; + + // Set delay line base pointer + psASRCCtrl->sADFIRF3Ctrl.piDelayB = psASRCCtrl->psState->iDelayADFIR; + // Set AD coefficients pointer + psASRCCtrl->sADFIRF3Ctrl.piADCoefs = psASRCCtrl->piADCoefs; + + // Call init for ADFIR F3 + if(ADFIR_init_from_desc(&psASRCCtrl->sADFIRF3Ctrl, psADFIRDescriptor) != FIR_NO_ERROR) + return FIR_ERROR; + + // Call sync function + if(ASRC_sync(psASRCCtrl) != ASRC_NO_ERROR) + return ASRC_ERROR; + + return ASRC_NO_ERROR; +} + + +// ==================================================================== // +// Function: ASRC_sync // +// Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // +// Return values: ASRC_NO_ERROR on success // +// ASRC_ERROR on failure // +// Description: Syncs the ASRC passed as argument // +// ==================================================================== // +ASRCReturnCodes_t ASRC_sync(ASRCCtrl_t* psASRCCtrl) +{ + // Sync the FIR and ADFIR + if(FIR_sync(&psASRCCtrl->sFIRF1Ctrl) != FIR_NO_ERROR) + return ASRC_ERROR; + if(FIR_sync(&psASRCCtrl->sFIRF2Ctrl) != FIR_NO_ERROR) + return ASRC_ERROR; + + if(ADFIR_sync(&psASRCCtrl->sADFIRF3Ctrl) != FIR_NO_ERROR) + return ASRC_ERROR; + + // Reset time + psASRCCtrl->iTimeInt = FILTER_DEFS_ADFIR_N_PHASES + ASRC_ADFIR_INITIAL_PHASE; + psASRCCtrl->uiTimeFract = 0; + + // Reset random seeds to initial values + psASRCCtrl->psState->uiRndSeed = psASRCCtrl->uiRndSeedInit; + + // Update time step based on Fs ratio + if(ASRC_update_fs_ratio(psASRCCtrl) != ASRC_NO_ERROR) + return ASRC_ERROR; + + // Reset cycle counters + psASRCCtrl->sProfilingInfo.fCycleCountF1F2 = 0; + psASRCCtrl->sProfilingInfo.fCycleCountF3AdaptiveCoefs = 0; + psASRCCtrl->sProfilingInfo.fCycleCountF3 = 0; + psASRCCtrl->sProfilingInfo.fCycleCountDither = 0; + + return ASRC_NO_ERROR; +} + + +// ==================================================================== // +// Function: ASRC_proc_F1_F2 // +// Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // +// Return values: ASRC_NO_ERROR on success // +// ASRC_ERROR on failure // +// Description: Processes F1 and F2 for a channel // +// ==================================================================== // +ASRCReturnCodes_t ASRC_proc_F1_F2(ASRCCtrl_t* psASRCCtrl) +{ + int* piIn = psASRCCtrl->piIn; + int* piOut = psASRCCtrl->piOut; + unsigned int ui; + + + // Setup variable input / output buffers + psASRCCtrl->sFIRF1Ctrl.piIn = psASRCCtrl->piIn; + + // F1 is always enabled, so call F1 + if(psASRCCtrl->sFIRF1Ctrl.pvProc(&psASRCCtrl->sFIRF1Ctrl) != FIR_NO_ERROR) + return ASRC_ERROR; + + // Cycle counter estimation + if(psASRCCtrl->sFIRF1Ctrl.pvProc == FIR_proc_os2) + psASRCCtrl->sProfilingInfo.fCycleCountF1F2 += psASRCCtrl->uiNInSamples * ( ASRC_FIR_OS2_OVERHEAD_CYCLE_COUNT + + (psASRCCtrl->sFIRF1Ctrl.uiNCoefs * ASRC_FIR_OS2_TAP_CYCLE_COUNT) ); + + if(psASRCCtrl->sFIRF1Ctrl.pvProc == FIR_proc_ds2) + psASRCCtrl->sProfilingInfo.fCycleCountF1F2 += (psASRCCtrl->uiNInSamples>>1) * ( ASRC_FIR_DS2_OVERHEAD_CYCLE_COUNT + + (psASRCCtrl->sFIRF1Ctrl.uiNCoefs * ASRC_FIR_DS2_TAP_CYCLE_COUNT) ); + + if(psASRCCtrl->sFIRF1Ctrl.pvProc == FIR_proc_sync) + psASRCCtrl->sProfilingInfo.fCycleCountF1F2 += psASRCCtrl->uiNInSamples * ( ASRC_FIR_SYNC_OVERHEAD_CYCLE_COUNT + + (psASRCCtrl->sFIRF1Ctrl.uiNCoefs * ASRC_FIR_SYNC_TAP_CYCLE_COUNT) ); + + + // Check if F2 is enabled + if(psASRCCtrl->sFIRF2Ctrl.eEnable == FIR_ON) + { + // F2 is enabled, so call F2 + if(psASRCCtrl->sFIRF2Ctrl.pvProc(&psASRCCtrl->sFIRF2Ctrl) != FIR_NO_ERROR) + return ASRC_ERROR; + + // Cycle counter estimation + if(psASRCCtrl->sFIRF2Ctrl.pvProc == FIR_proc_os2) + psASRCCtrl->sProfilingInfo.fCycleCountF1F2 += psASRCCtrl->sFIRF2Ctrl.uiNInSamples * ( ASRC_FIR_OS2_OVERHEAD_CYCLE_COUNT + + (psASRCCtrl->sFIRF2Ctrl.uiNCoefs * ASRC_FIR_OS2_TAP_CYCLE_COUNT) ); + + if(psASRCCtrl->sFIRF2Ctrl.pvProc == FIR_proc_ds2) + psASRCCtrl->sProfilingInfo.fCycleCountF1F2 += (psASRCCtrl->sFIRF2Ctrl.uiNInSamples>>1) * ( ASRC_FIR_DS2_OVERHEAD_CYCLE_COUNT + + (psASRCCtrl->sFIRF2Ctrl.uiNCoefs * ASRC_FIR_DS2_TAP_CYCLE_COUNT) ); + + if(psASRCCtrl->sFIRF2Ctrl.pvProc == FIR_proc_sync) + psASRCCtrl->sProfilingInfo.fCycleCountF1F2 += psASRCCtrl->sFIRF2Ctrl.uiNInSamples * ( ASRC_FIR_SYNC_OVERHEAD_CYCLE_COUNT + + (psASRCCtrl->sFIRF2Ctrl.uiNCoefs * ASRC_FIR_SYNC_TAP_CYCLE_COUNT) ); + } + + return ASRC_NO_ERROR; +} + + +// ==================================================================== // +// Function: ASRC_update_fs_ratio // +// Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // +// Return values: ASRC_NO_ERROR on success // +// ASRC_ERROR on failure // +// Description: Updates the ASRC with the new Fs ratio // +// ==================================================================== // +ASRCReturnCodes_t ASRC_update_fs_ratio(ASRCCtrl_t* psASRCCtrl) +{ + unsigned int uiFsRatio = psASRCCtrl->uiFsRatio; + unsigned int uiFsRatio_low = psASRCCtrl->uiFsRatio_lo; + + // Check for bounds of new Fs ratio + if( (uiFsRatio < sFsRatioConfigs[psASRCCtrl->eInFs][psASRCCtrl->eOutFs].uiMinFsRatio) || + (uiFsRatio > sFsRatioConfigs[psASRCCtrl->eInFs][psASRCCtrl->eOutFs].uiMaxFsRatio) ) + return ASRC_ERROR; + + // Apply shift to time ratio to build integer and fractional parts of time step + psASRCCtrl->iTimeStepInt = uiFsRatio >> (sFsRatioConfigs[psASRCCtrl->eInFs][psASRCCtrl->eOutFs].iFsRatioShift); + psASRCCtrl->uiTimeStepFract = uiFsRatio << (32 - sFsRatioConfigs[psASRCCtrl->eInFs][psASRCCtrl->eOutFs].iFsRatioShift); + psASRCCtrl->uiTimeStepFract |= (uint32_t)(uiFsRatio_low >> sFsRatioConfigs[psASRCCtrl->eInFs][psASRCCtrl->eOutFs].iFsRatioShift); + + return ASRC_NO_ERROR; +} + + +// ==================================================================== // +// Function: ASRC_proc_F3_in_spl // +// Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // +// int iInSample: new input sample // +// Return values: ASRC_NO_ERROR on success // +// ASRC_ERROR on failure // +// Description: Writes new input sample to F3 delay line // +// ==================================================================== // +ASRCReturnCodes_t ASRC_proc_F3_in_spl(ASRCCtrl_t* psASRCCtrl, int iInSample) +{ + psASRCCtrl->sADFIRF3Ctrl.iIn = iInSample; + if(ADFIR_proc_in_spl(&psASRCCtrl->sADFIRF3Ctrl) != FIR_NO_ERROR) + return ASRC_ERROR; + + // Decrease next output time (this is an integer value, so no influence on fractional part) + psASRCCtrl->iTimeInt -= FILTER_DEFS_ADFIR_N_PHASES; + + // Update cycle count + psASRCCtrl->sProfilingInfo.fCycleCountF3 += ASRC_ADFIR_IN_SPL_CYCLE_COUNT; + + return ASRC_NO_ERROR; +} + + +// ==================================================================== // +// Function: ASRC_proc_F3_time // +// Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // +// Return values: ASRC_NO_ERROR if an output sample must be produced // +// ASRC_ERROR if no output sample needs to be produced // +// Description: Processes F3 time // +// ==================================================================== // +ASRCReturnCodes_t ASRC_proc_F3_time(ASRCCtrl_t* psASRCCtrl) +{ + unsigned int uiTemp; + int iAlpha; + unsigned int ui; + + int iH0, iH1, iH2; + int iZero; + int iData0; + int iData1; + __int64 i64Acc0; + __int64 i64Acc1; + int* piPhase0; + int* piPhase1; + int* piPhase2; + int* piADCoefs; + + // Check if the next output time instant is in the current time slot + // ----------------------------------------------------------------- + // if not return value showing that no output sample needs to be produced + // Update cycle count + psASRCCtrl->sProfilingInfo.fCycleCountF3AdaptiveCoefs += ASRC_ADFIR_TIME_CHECK_CYCLE_COUNT; + if(psASRCCtrl->iTimeInt >= FILTER_DEFS_ADFIR_N_PHASES) + return ASRC_ERROR; + + + // Update adaptive filter coefficients + // ----------------------------------- + // Coefficients computation load approximation: 15 instructions + // Loop load approximation: 5.625 instructions per sample + 15 instructions overhead => 5.625 * 16 + 15 = 90 + 15 = 105 Instructions + // Total: 125 instructions + + // Set register to zero + iZero = 0; + + // Compute adative coefficients spline factors + // The fractional part of time gives alpha + iAlpha = psASRCCtrl->uiTimeFract>>1; // Now alpha can be seen as a signed number + LMUL(&i64Acc0, iAlpha, iAlpha, iZero, iZero); // Compte H0 = 0.5 * alpha * alpha + iH0 = (int)(i64Acc0>>32); + iH2 = 0x40000000; // Load H2 with 0.5; + iH1 = iH2 - iH0; // H1 = 0.5 - 0.5 * alpha * alpha; + iH1 = iH1 - iH0; // H1 = 0.5 - alpha * alpha + iH1 = iH1 + iAlpha; // H1 = 0.5 + alpha - alpha * alpha; + iH2 = iH2 - iAlpha; // H2 = 0.5 - alpha + iH2 = iH2 + iH0; // H2 = 0.5 - alpha + 0.5 * alpha * alpha + + // The integer part of time gives the phase + piPhase0 = iADFirCoefs[psASRCCtrl->iTimeInt]; + piPhase1 = piPhase0 + FILTER_DEFS_ADFIR_PHASE_N_TAPS; + piPhase2 = piPhase1 + FILTER_DEFS_ADFIR_PHASE_N_TAPS; + piADCoefs = psASRCCtrl->piADCoefs; // Given limited number of registers, this should be DP + + // Apply spline coefficients to filter coefficients + for(ui = 0; ui < FILTER_DEFS_ADFIR_PHASE_N_TAPS; ui += 2) + { + // Double read (long word) + iData0 = *(piPhase0 + ui); + iData1 = *(piPhase0 + ui + 1); + // MACC for each word + LMUL(&i64Acc0, iH2, iData0, iZero, iZero); + LMUL(&i64Acc1, iH2, iData1, iZero, iZero); + + // Double read (long word) + iData0 = *(piPhase1 + ui); + iData1 = *(piPhase1 + ui + 1); + // Double read (long word) + MACC(&i64Acc0, iH1, iData0); + MACC(&i64Acc1, iH1, iData1); + + // Double read (long word) + iData0 = *(piPhase2 + ui); + iData1 = *(piPhase2 + ui + 1); + // Double read (long word) + MACC(&i64Acc0, iH0, iData0); + MACC(&i64Acc1, iH0, iData1); + + // Adaptive coefficient is given by upper part of accumulator + // Use two writes of single word (as we don't have enough registers, piADCoefs should be DP + *(piADCoefs + ui) = (int)(i64Acc0>>32); + *(piADCoefs + ui + 1) = (int)(i64Acc1>>32); + } + + // Step time for next output sample + // -------------------------------- + // Step to next output time (add integer and fractional parts) + psASRCCtrl->iTimeInt += psASRCCtrl->iTimeStepInt; + // For fractional part, this can be optimized using the add with carry instruction of XS2 + uiTemp = psASRCCtrl->uiTimeFract; + psASRCCtrl->uiTimeFract += psASRCCtrl->uiTimeStepFract; + if(psASRCCtrl->uiTimeFract < uiTemp) + psASRCCtrl->iTimeInt++; + + //printf("TimeInt = %x TimeFract = %x", psASRCCtrl->iTimeInt, psASRCCtrl->uiTimeFract); + + // Increase cycle counter + psASRCCtrl->sProfilingInfo.fCycleCountF3AdaptiveCoefs += ASRC_ADFIR_TIME_SAMPLE_CYCLE_COUNT; + + // Return value showing that an output sample must be produced + return ASRC_NO_ERROR; +} + + +// ==================================================================== // +// Function: ASRC_proc_F3_macc // +// Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // +// int* piOutSample: Address of output sample // +// Return values: ASRC_NO_ERROR on success // +// ASRC_ERROR on failure // +// Description: Processes F3 for a channel // +// ==================================================================== // +ASRCReturnCodes_t ASRC_proc_F3_macc(ASRCCtrl_t* psASRCCtrl, int* piOutSample) +{ + psASRCCtrl->sADFIRF3Ctrl.piOut = piOutSample; + // Call processing function + if(ADFIR_proc_macc(&psASRCCtrl->sADFIRF3Ctrl) != FIR_NO_ERROR) + return ASRC_ERROR; + + psASRCCtrl->uiNASRCOutSamples++; + + // Cycle counter estimation + psASRCCtrl->sProfilingInfo.fCycleCountF3 += ASRC_ADFIR_MACC_OVERHEAD_CYCLE_COUNT + (psASRCCtrl->sADFIRF3Ctrl.uiNLoops * 2.0 * ASRC_ADFIR_MACC_TAP_CYCLE_COUNT); + + return ASRC_NO_ERROR; +} + + +// ==================================================================== // +// Function: ASRC_proc_dither // +// Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // +// Return values: ASRC_NO_ERROR on success // +// ASRC_ERROR on failure // +// Description: Processes dither for a channel // +// ==================================================================== // +ASRCReturnCodes_t ASRC_proc_dither(ASRCCtrl_t* psASRCCtrl) +{ + int* piData; + unsigned int uiR; + int iDither; + __int64 i64Acc; + unsigned int ui; + + + // Apply dither if required + if(psASRCCtrl->uiDitherOnOff == ASRC_DITHER_ON) + { + // Get data buffer + piData = psASRCCtrl->piOut; + // Get random seed + uiR = psASRCCtrl->psState->uiRndSeed; + + // Loop through samples + for(ui = 0; ui < psASRCCtrl->uiNASRCOutSamples * psASRCCtrl->uiInStep; ui += psASRCCtrl->uiInStep) + { + // Compute dither sample (TPDF) + iDither = ASRC_DITHER_BIAS; + + uiR = (unsigned int)(ASRC_R_BASE * uiR); + uiR = (unsigned int)(ASRC_R_CONS + uiR); + iDither += ((uiR>>ASRC_RPDF_BITS_SHIFT) & ASRC_RPDF_MASK); + + uiR = (unsigned int)(ASRC_R_BASE * uiR); + uiR = (unsigned int)(ASRC_R_CONS + uiR); + iDither += ((uiR>>ASRC_RPDF_BITS_SHIFT) & ASRC_RPDF_MASK); + + // Use MACC instruction to saturate and dither + signal + i64Acc = ((__int64)iDither <<32); // On XMOS this is not necessary, just load dither in the top word of the ACC register + MACC(&i64Acc, piData[ui], 0x7FFFFFFF); + LSAT30(&i64Acc); + // Extract 32bits result + EXT30(&piData[ui], i64Acc); + + // Mask to 24bits + piData[ui] &= ASRC_DATA24_MASK; + + // Increase cycle counter + psASRCCtrl->sProfilingInfo.fCycleCountDither += ASRC_DITHER_SAMPLE_COUNT; + } + + // Write random seed back + psASRCCtrl->psState->uiRndSeed = uiR; + } + + return ASRC_NO_ERROR; +} diff --git a/tests/asrc_test/model/src/ASRC.h b/tests/asrc_test/model/src/ASRC.h new file mode 100644 index 00000000..6c8b44c4 --- /dev/null +++ b/tests/asrc_test/model/src/ASRC.h @@ -0,0 +1,228 @@ +// Copyright 2023 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +// =========================================================================== +// =========================================================================== +// +// File: ASRC.h +// +// Top level definition file for the ASRC +// +// Target: MS Windows +// Version: 1.0 +// +// =========================================================================== +// =========================================================================== + +#ifndef _ASRC_H_ +#define _ASRC_H_ + +// =========================================================================== +// +// Includes +// +// =========================================================================== +#include "FIR.h" +#include "FilterDefs.h" +#include "ASRC_wrapper.h" + +// =========================================================================== +// +// Defines +// +// =========================================================================== + +// General defines +// --------------- +#define ASRC_MAX_N_CHANNELS 1 // Max Number of channels processed by ASRC instance + +#define ASRC_STACK_LENGTH_MULT (ASRC_MAX_N_CHANNELS * 4) // Multiplier for stack length (stack length = this value x the number of input samples to process) +#define ASRC_ADFIR_COEFS_LENGTH FILTER_DEFS_ADFIR_PHASE_N_TAPS // Length of AD FIR coefficients buffer + +// Nominal Fs Ratio scale value +#define ASRC_NOMINAL_FS_SCALE (268435456.0) // 2^28 + +// =========================================================================== +// +// Defines +// +// =========================================================================== + +// =========================================================================== +// +// TypeDefs +// +// =========================================================================== + +// To avoid C type definitions when including this file from assembler +#ifndef INCLUDE_FROM_ASM + +// ASRC Return Codes +// ----------------- +typedef enum _ASRCReturnCodes +{ + ASRC_NO_ERROR = 0, + ASRC_ERROR = 1 +} ASRCReturnCodes_t; + +// ASRC Filters IDs structure +// -------------------------- +#define ASRC_F1_INDEX 0 +#define ASRC_F2_INDEX 1 +#define ASRC_N_F (ASRC_F2_INDEX + 1) + +typedef struct _ASRCFiltersIDs +{ + unsigned int uiFID[ASRC_N_F]; +} ASRCFiltersIDs_t; + +// ASRC time ratio configurations +// ------------------------------ +typedef struct _ASRCFsRatioConfigs +{ + unsigned int uiNominalFsRatio; + unsigned int uiNominalFsRatio_lo; + unsigned int uiMinFsRatio; + unsigned int uiMaxFsRatio; + int iFsRatioShift; +} ASRCFsRatioConfigs_t; + +// ASRC State structure +// -------------------- +typedef struct _ASRCState +{ + unsigned int uiRndSeed; // Dither random seeds current values + int iDelayFIRLong[2 * FILTER_DEFS_FIR_MAX_TAPS_LONG]; // Doubled length for circular buffer simulation + int iDelayFIRShort[2 * FILTER_DEFS_FIR_MAX_TAPS_SHORT]; // Doubled length for circular buffer simulation + int iDelayADFIR[2 * FILTER_DEFS_ADFIR_PHASE_N_TAPS]; // Doubled length for circular buffer simulation +} ASRCState_t; + +// ASRC Control structure +// ---------------------- +typedef struct _ASRCCtrl +{ + int *piIn; // Input buffer pointer (PCM, 32bits, 2 channels time domain interleaved data) + unsigned int uiNInSamples; // Number of input samples to process in one call to the processing function + unsigned int uiNSyncSamples; // Number of synchronous samples produced in one call to the processing function + ASRCFs_t eInFs; // Input sampling rate code + int *piOut; // Output buffer poin ter (PCM, 32bits, 2 channels time domain interleaved data) + unsigned int uiNASRCOutSamples; // Number of output samples produced during last call to the asynchronous processing function + ASRCFs_t eOutFs; // Output sampling rate code + + FIRCtrl_t sFIRF1Ctrl; // F1 FIR controller + FIRCtrl_t sFIRF2Ctrl; // F2 FIR controller + ADFIRCtrl_t sADFIRF3Ctrl; // F3 ADFIR controller + + unsigned int uiFsRatio; // Fs ratio: Fsin / Fsout + unsigned int uiFsRatio_lo; + + int iTimeInt; // Integer part of time + unsigned int uiTimeFract; // Fractional part of time + int iTimeStepInt; // Integer part of time step + unsigned int uiTimeStepFract; // Fractional part of time step + + unsigned int uiDitherOnOff; // Dither on/off flag + unsigned int uiRndSeedInit; // Dither random seed initial value + + ASRCState_t *psState; // Pointer to state structure + int *piStack; // Pointer to stack buffer + int *piADCoefs; // Pointer to AD coefficients + + unsigned int uiInStep; + unsigned int uiOutStep; + + ASRCCtrl_profile_only_t sProfilingInfo; +} ASRCCtrl_t; + +// =========================================================================== +// +// Function prototypes +// +// =========================================================================== + +// ==================================================================== // +// Function: ASRC_prepare_coefs // +// Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // +// Return values: ASRC_NO_ERROR on success // +// ASRC_ERROR on failure // +// Description: Prepares the ASRC coefficients from the prototype // +// Needs to be called only once // +// ==================================================================== // +ASRCReturnCodes_t ASRC_prepare_coefs(void); + +// ==================================================================== // +// Function: ASRC_init // +// Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // +// Return values: ASRC_NO_ERROR on success // +// ASRC_ERROR on failure // +// Description: Inits the ASRC passed as argument // +// ==================================================================== // +ASRCReturnCodes_t ASRC_init(ASRCCtrl_t *psASRCCtrl); + +// ==================================================================== // +// Function: ASRC_sync // +// Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // +// Return values: ASRC_NO_ERROR on success // +// ASRC_ERROR on failure // +// Description: Syncs the ASRC passed as argument // +// ==================================================================== // +ASRCReturnCodes_t ASRC_sync(ASRCCtrl_t *psASRCCtrl); + +// ==================================================================== // +// Function: ASRC_proc_F1_F2 // +// Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // +// Return values: ASRC_NO_ERROR on success // +// ASRC_ERROR on failure // +// Description: Processes F1 and F2 for a channel // +// ==================================================================== // +ASRCReturnCodes_t ASRC_proc_F1_F2(ASRCCtrl_t *psASRCCtrl); + +// ==================================================================== // +// Function: ASRC_update_fs_ratio // +// Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // +// Return values: ASRC_NO_ERROR on success // +// ASRC_ERROR on failure // +// Description: Updates the ASRC with the new Fs ratio // +// ==================================================================== // +ASRCReturnCodes_t ASRC_update_fs_ratio(ASRCCtrl_t *psASRCCtrl); + +// ==================================================================== // +// Function: ASRC_proc_F3_in_spl // +// Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // +// int iInSample: new input sample // +// Return values: ASRC_NO_ERROR on success // +// ASRC_ERROR on failure // +// Description: Writes new input sample to F3 delay line // +// ==================================================================== // +ASRCReturnCodes_t ASRC_proc_F3_in_spl(ASRCCtrl_t *psASRCCtrl, int iInSample); + +// ==================================================================== // +// Function: ASRC_proc_F3_time // +// Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // +// Return values: ASRC_NO_ERROR if an output sample must be produced // +// ASRC_ERROR if no output sample needs to be produced // +// Description: Processes F3 time // +// ==================================================================== // +ASRCReturnCodes_t ASRC_proc_F3_time(ASRCCtrl_t *psASRCCtrl); + +// ==================================================================== // +// Function: ASRC_proc_F3_macc // +// Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // +// int* piOutSample: Address of output sample // +// Return values: ASRC_NO_ERROR on success // +// ASRC_ERROR on failure // +// Description: Processes F3 for a channel // +// ==================================================================== // +ASRCReturnCodes_t ASRC_proc_F3_macc(ASRCCtrl_t *psASRCCtrl, int *piOutSample); + +// ==================================================================== // +// Function: ASRC_proc_dither // +// Arguments: ASRCCtrl_t *psASRCCtrl: Ctrl strct. // +// Return values: ASRC_NO_ERROR on success // +// ASRC_ERROR on failure // +// Description: Processes dither for a channel // +// ==================================================================== // +ASRCReturnCodes_t ASRC_proc_dither(ASRCCtrl_t *psASRCCtrl); + +#endif // nINCLUDE_FROM_ASM + +#endif // _ASRC_H_ diff --git a/tests/asrc_test/model/src/ASRC_wrapper.c b/tests/asrc_test/model/src/ASRC_wrapper.c new file mode 100644 index 00000000..f58a3372 --- /dev/null +++ b/tests/asrc_test/model/src/ASRC_wrapper.c @@ -0,0 +1,213 @@ +// Copyright 2023 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +// General includes +#include +#include +#include +#include +#include +#include + +// ASRC includes +#include "ASRC_wrapper.h" +#include "ASRC.h" + +#define N_IN_SAMPLES_MAX (1024) +// State, Stack, Coefs and Control structures (one for each channel) +static ASRCState_t sASRCState[MAX_ASRC_N_IO_CHANNELS]; +static int iASRCStack[MAX_ASRC_N_IO_CHANNELS][ASRC_STACK_LENGTH_MULT * N_IN_SAMPLES_MAX]; +static ASRCCtrl_t sASRCCtrl[MAX_ASRC_N_IO_CHANNELS]; +static int iASRCADFIRCoefs[ASRC_ADFIR_COEFS_LENGTH]; + +//Helper function for converting sample to fs index value +unsigned samp_rate_to_code(unsigned samp_rate){ + unsigned samp_code = 0xdead; + switch (samp_rate){ + case 44100: + samp_code = ASRC_FS_44; + break; + case 48000: + samp_code = ASRC_FS_48; + break; + case 88200: + samp_code = ASRC_FS_88; + break; + case 96000: + samp_code = ASRC_FS_96; + break; + case 176400: + samp_code = ASRC_FS_176; + break; + case 192000: + samp_code = ASRC_FS_192; + break; + } + return samp_code; +} + +static unsigned g_num_io_channels; +static unsigned g_num_channels_per_asrc_instance; + + +uint64_t wrapper_asrc_init( + ASRCCtrl_profile_only_t* (*profile_info_ptr)[MAX_ASRC_N_IO_CHANNELS], + unsigned uiInFs, + unsigned uiOutFs, + unsigned uiNInSamples, + unsigned num_io_channels, + unsigned num_channels_per_asrc_instance, + unsigned dither_on_off, + unsigned *rand_seed) +{ + assert(num_io_channels <= MAX_ASRC_N_IO_CHANNELS); + assert(num_channels_per_asrc_instance <= MAX_ASRC_N_IO_CHANNELS); + + g_num_io_channels = num_io_channels; + g_num_channels_per_asrc_instance = num_channels_per_asrc_instance; + + unsigned int ui, uj; + + // Prepare the ASRC coefficients + if(ASRC_prepare_coefs() != ASRC_NO_ERROR) + { + printf("Error at ASRC coefficients preparation"); + assert(0); + } + + for(ui = 0; ui < num_io_channels; ui++) + { + // Set state, stack and coefs into ctrl structure + sASRCCtrl[ui].psState = &sASRCState[ui]; + sASRCCtrl[ui].piStack = iASRCStack[ui]; + sASRCCtrl[ui].piADCoefs = iASRCADFIRCoefs; + + // Set input/output sampling rate codes + sASRCCtrl[ui].eInFs = samp_rate_to_code(uiInFs); + sASRCCtrl[ui].eOutFs = samp_rate_to_code(uiOutFs); + + // Set number of samples + sASRCCtrl[ui].uiNInSamples = uiNInSamples; + + // Set dither flag and random seeds + sASRCCtrl[ui].uiDitherOnOff = dither_on_off; + sASRCCtrl[ui].uiRndSeedInit = rand_seed[ui]; + + sASRCCtrl[ui].uiInStep = num_io_channels / num_channels_per_asrc_instance; + sASRCCtrl[ui].uiOutStep = num_channels_per_asrc_instance; + + profile_info_ptr[0][ui] = &sASRCCtrl[ui].sProfilingInfo; + + // Init ASRC instances + if(ASRC_init(&sASRCCtrl[ui]) != ASRC_NO_ERROR) + { + printf("Error at ASRC initialization"); + assert(0); + } + } + + // Sync + // ---- + // Sync ASRC. This is just to show that the function works and returns success + for(ui = 0; ui < num_io_channels; ui++) + { + if(ASRC_sync(&sASRCCtrl[ui]) != ASRC_NO_ERROR) + { + printf("Error at ASRC sync"); + assert(0); + } + } + return (uint64_t)(((uint64_t)sASRCCtrl[0].uiFsRatio << 32) | (uint64_t)sASRCCtrl[0].uiFsRatio_lo); +} + + +unsigned wrapper_asrc_process( + int *piIn, + int *piOut, + uint64_t fs_ratio) +{ + unsigned int ui, uj; + int uiSplCntr; + + for(ui = 0; ui < g_num_io_channels; ui++) + { + // Make Fs Ratio deviate + sASRCCtrl[ui].uiFsRatio = (unsigned int)(fs_ratio >> 32); + sASRCCtrl[ui].uiFsRatio_lo = (unsigned int)fs_ratio; + if(ASRC_update_fs_ratio(&sASRCCtrl[ui]) != ASRC_NO_ERROR) + { + printf("Error at ASRC update fs ratio"); + assert(0); + } + // Set input and output data pointers + sASRCCtrl[ui].piIn = piIn + ui; + sASRCCtrl[ui].piOut = piOut + ui; + } + + // Process synchronous part (F1 + F2) + // ================================== + for(ui = 0; ui < g_num_io_channels; ui++) + // Note: this is block based similar to SSRC, output will be on stack + // and there will be sASRCCtrl[0].uiNSyncSamples samples per channel produced + if(ASRC_proc_F1_F2(&sASRCCtrl[ui]) != ASRC_NO_ERROR) + { + printf("Error at ASRC F1 F2 process"); + assert(0); + } + + + // Run the asynchronous part (F3) + // ============================== + // Clear number of output samples (note that this sample counter would actually not be needed if all was sample by sampe) + for(ui = 0; ui < g_num_io_channels; ui++) + sASRCCtrl[ui].uiNASRCOutSamples = 0; + + uiSplCntr = 0; // This is actually only used because of the bizarre mix of block and sample based processing + + // Driven by samples produced during the synchronous phase + for(ui = 0; ui < sASRCCtrl[0].uiNSyncSamples; ui++) + { + // Push new samples into F3 delay line (input from stack) for each new "synchronous" sample (i.e. output of F1, respectively F2) + for(uj = 0; uj < g_num_io_channels; uj++) + if(ASRC_proc_F3_in_spl(&sASRCCtrl[uj], sASRCCtrl[uj].piStack[ui]) != ASRC_NO_ERROR) + { + printf("Error at ASRC F3 in sample process"); + assert(0); + } + + // Run macc loop for F3 + // Check if a new output sample needs to be produced + // Note that this will also update the adaptive filter coefficients + // These must be computed for one channel only and reused in the macc loop of other channels + while(ASRC_proc_F3_time(&sASRCCtrl[0]) == ASRC_NO_ERROR) + { + // Apply filter F3 with just computed adaptive coefficients + for(uj = 0; uj < g_num_io_channels; uj++) + if(ASRC_proc_F3_macc(&sASRCCtrl[uj], sASRCCtrl[uj].piOut + g_num_io_channels * uiSplCntr) != ASRC_NO_ERROR) + { + printf("Error at ASRC F3 in sample process"); + assert(0); + } + + uiSplCntr++; // This is actually only used because of the bizarre mix of block and sample based processing + } + } + + + // Process dither part + // =================== + // We are back to block based processing. This is where the number of ASRC output samples is required again + // (would not be used if sample by sample based (on output samples)) + for(ui = 0; ui < g_num_io_channels; ui++) + { + // Note: this is block based similar to SSRC + if(ASRC_proc_dither(&sASRCCtrl[ui]) != ASRC_NO_ERROR) + { + printf("Error at ASRC F1 F2 process"); + assert(0); + } + } + + unsigned n_samps_out = sASRCCtrl[0].uiNASRCOutSamples; + return n_samps_out; + +} diff --git a/tests/asrc_test/model/FIR.c b/tests/asrc_test/model/src/FIR.c similarity index 96% rename from tests/asrc_test/model/FIR.c rename to tests/asrc_test/model/src/FIR.c index c0d32b83..6c7042b7 100644 --- a/tests/asrc_test/model/FIR.c +++ b/tests/asrc_test/model/src/FIR.c @@ -1,569 +1,571 @@ -// =========================================================================== -// =========================================================================== -// -// File: FIR.c -// -// FIR functions implementation file for the ASRC -// -// Target: MS Windows -// Version: 1.0 -// -// =========================================================================== -// =========================================================================== - - -// =========================================================================== -// -// Includes -// -// =========================================================================== -#include -#include -#include -#include - -// Integer arithmetic include -#include "IntArithmetic.h" -// FIR includes -#include "FIR.h" - -// =========================================================================== -// -// Defines -// -// =========================================================================== - - -// State init value -#define FIR_STATE_INIT 0 - - - - - -// =========================================================================== -// -// Variables -// -// =========================================================================== - - - -// =========================================================================== -// -// Local Functions prototypes -// -// =========================================================================== - - - -// =========================================================================== -// -// Functions implementations -// -// =========================================================================== - -// ==================================================================== // -// Function: FIR_init_from_desc // -// Arguments: FIRCtrl_t *psFIRCtrl: Ctrl strct. // -// FIRDescriptor_t *psFIRDescriptor: Desc. strct. // -// Return values: FIR_NO_ERROR on success // -// FIR_ERROR on failure // -// Description: Inits the FIR from the Descriptor // -// ==================================================================== // -FIRReturnCodes_t FIR_init_from_desc(FIRCtrl_t* psFIRCtrl, FIRDescriptor_t* psFIRDescriptor) -{ - - // Check if FIR is disabled (this is given by the number of coefficients being zero) - if( psFIRDescriptor->uiNCoefs == 0) - { - // Number of coefficients is zero, so disable FIR - psFIRCtrl->eEnable = FIR_OFF; - psFIRCtrl->uiNOutSamples = 0; - psFIRCtrl->pvProc = 0; - psFIRCtrl->uiDelayL = 0; - psFIRCtrl->piDelayW = 0; - psFIRCtrl->uiDelayO = 0; - psFIRCtrl->uiNLoops = 0; - psFIRCtrl->uiNCoefs = 0; - psFIRCtrl->piCoefs = 0; - - return FIR_NO_ERROR; - } - - // FIR is not disabled - // Check that delay line base has been set - if(psFIRCtrl->piDelayB == 0) - return FIR_ERROR; - - // Check that number of samples has been set and is a multiple of 2 - if(psFIRCtrl->uiNInSamples == 0) - return FIR_ERROR; - if((psFIRCtrl->uiNInSamples & 0x1) != 0x0) - return FIR_ERROR; - - // Check the input and output samples steps have been set - if(psFIRCtrl->uiInStep == 0) - return FIR_ERROR; - if(psFIRCtrl->uiOutStep == 0) - return FIR_ERROR; - - // Setup depending on FIR descriptor - switch(psFIRDescriptor->eType) - { - // Over-sampler by 2 type - case FIR_TYPE_OS2: - if( (psFIRDescriptor->uiNCoefs & 0x3) != 0x0) // Check that number of coefficients is a multiple of 4 - return FIR_ERROR; - psFIRCtrl->eEnable = FIR_ON; - psFIRCtrl->uiNOutSamples = (psFIRCtrl->uiNInSamples)<<1; // Os2 FIR doubles the number of samples - psFIRCtrl->pvProc = FIR_proc_os2; - psFIRCtrl->uiDelayL = psFIRDescriptor->uiNCoefs; // Double length for circular buffer simulation, but only half length due to OS2 - psFIRCtrl->piDelayW = psFIRCtrl->piDelayB + (psFIRDescriptor->uiNCoefs>>1); - psFIRCtrl->uiDelayO = psFIRDescriptor->uiNCoefs>>1; - psFIRCtrl->uiNLoops = psFIRDescriptor->uiNCoefs>>2; // Due to 2 x 32bits read for data and 4 x 32bits for coefs per inner loop - psFIRCtrl->uiNCoefs = psFIRDescriptor->uiNCoefs; - psFIRCtrl->piCoefs = psFIRDescriptor->piCoefs; - break; - - // Asynchronous type - case FIR_TYPE_SYNC: - if( (psFIRDescriptor->uiNCoefs & 0x1) != 0x0) // Check that number of coefficients is a multiple of 2 - return FIR_ERROR; - // Non zero coefficients number, so it is a true filter - psFIRCtrl->eEnable = FIR_ON; - psFIRCtrl->uiNOutSamples = psFIRCtrl->uiNInSamples; // Sync FIR does not change number of samples - psFIRCtrl->pvProc = FIR_proc_sync; - psFIRCtrl->uiDelayL = psFIRDescriptor->uiNCoefs<<1; // Double length for circular buffer simulation - psFIRCtrl->piDelayW = psFIRCtrl->piDelayB + psFIRDescriptor->uiNCoefs; - psFIRCtrl->uiDelayO = psFIRDescriptor->uiNCoefs; - psFIRCtrl->uiNLoops = psFIRDescriptor->uiNCoefs>>1; // Due to 2 x 32bits read for data and coefs per inner loop - psFIRCtrl->uiNCoefs = psFIRDescriptor->uiNCoefs; - psFIRCtrl->piCoefs = psFIRDescriptor->piCoefs; - break; - - // Down-sample by 2 type - case FIR_TYPE_DS2: - if( (psFIRDescriptor->uiNCoefs & 0x1) != 0x0) // Check that number of coefficients is a multiple of 2 - return FIR_ERROR; - psFIRCtrl->eEnable = FIR_ON; - psFIRCtrl->uiNOutSamples = psFIRCtrl->uiNInSamples>>1; // Ds2 FIR divides the number of samples by two - psFIRCtrl->pvProc = FIR_proc_ds2; - psFIRCtrl->uiDelayL = psFIRDescriptor->uiNCoefs<<1; // Double length for circular buffer simulation - psFIRCtrl->piDelayW = psFIRCtrl->piDelayB + psFIRDescriptor->uiNCoefs; - psFIRCtrl->uiDelayO = psFIRDescriptor->uiNCoefs; - psFIRCtrl->uiNLoops = psFIRDescriptor->uiNCoefs>>1; // Due to 2 x 32bits read for data and coefs per inner loop - psFIRCtrl->uiNCoefs = psFIRDescriptor->uiNCoefs; - psFIRCtrl->piCoefs = psFIRDescriptor->piCoefs; - break; - - // Unrecognized type - default: - return FIR_ERROR; - } - - // Sync the FIR - if(FIR_sync(psFIRCtrl) != FIR_NO_ERROR) - return FIR_ERROR; - - return FIR_NO_ERROR; -} - - -// ==================================================================== // -// Function: FIR_sync // -// Arguments: FIRCtrl_t *psFIRCtrl: Ctrl strct. // -// Return values: FIR_NO_ERROR on success // -// FIR_ERROR on failure // -// Description: Syncs the FIR // -// ==================================================================== // -FIRReturnCodes_t FIR_sync(FIRCtrl_t* psFIRCtrl) -{ - unsigned int ui; - - if(psFIRCtrl->eEnable == FIR_ON) - { - // Set delay line index back to base - psFIRCtrl->piDelayI = psFIRCtrl->piDelayB; - - // Clear delay line - for(ui = 0; ui < psFIRCtrl->uiDelayL; ui++) - psFIRCtrl->piDelayB[ui] = FIR_STATE_INIT; - } - - return FIR_NO_ERROR; -} - - -// ==================================================================== // -// Function: FIR_proc_os2 // -// Arguments: FIRCtrl_t *psFIRCtrl: Ctrl strct. // -// Return values: FIR_NO_ERROR on success // -// FIR_ERROR on failure // -// Description: Processes the FIR in over-sample by 2 mode // -// ==================================================================== // -FIRReturnCodes_t FIR_proc_os2(FIRCtrl_t* psFIRCtrl) -{ - int* piIn = psFIRCtrl->piIn; - unsigned int uiInStep = psFIRCtrl->uiInStep; - int* piOut = psFIRCtrl->piOut; - unsigned int uiOutStep = psFIRCtrl->uiOutStep; - int* piDelayB = psFIRCtrl->piDelayB; - int* piDelayI = psFIRCtrl->piDelayI; - int* piDelayW = psFIRCtrl->piDelayW; - unsigned int uiDelayO = psFIRCtrl->uiDelayO; - int* piCoefsB = psFIRCtrl->piCoefs; - unsigned int uiNLoops = psFIRCtrl->uiNLoops; - int* piData; - int* piCoefs; - int iData0, iData1; - int iCoef0, iCoef1; - __int64 i64Acc0, i64Acc1; - unsigned ui, uj; - - - for(ui = 0; ui < psFIRCtrl->uiNInSamples; ui++) - { - // Get new data sample to delay line (double write for circular buffer simulation) with step - iData0 = *piIn; - piIn += uiInStep; - // Double write to simulate circular buffer - *piDelayI = iData0; - *(piDelayI + uiDelayO) = iData0; - // Step delay (with circular simulation) - piDelayI++; - if(piDelayI >= piDelayW) - piDelayI = piDelayB; - - // Clear accumulators and set access pointers - piData = piDelayI; - piCoefs = piCoefsB; - i64Acc0 = 0; - i64Acc1 = 0; - for(uj = 0; uj < uiNLoops; uj++) - { - // data read - iData0 = *piData++; - iData1 = *piData++; - // DUAL coefs read (two phases for first data) - iCoef0 = *piCoefs++; - iCoef1 = *piCoefs++; - // MACCs (for first data) - MACC(&i64Acc0, iData0, iCoef0); - MACC(&i64Acc1, iData0, iCoef1); - // DUAL coefs read (two phases for second data) - iCoef0 = *piCoefs++; - iCoef1 = *piCoefs++; - // MACCs (for second data) - MACC(&i64Acc0, iData1, iCoef0); - MACC(&i64Acc1, iData1, iCoef1); - } - - // Saturate MACCs result - LSAT30(&i64Acc0); - LSAT30(&i64Acc1); - // Extract 32bits result - EXT30(&iData0, i64Acc0); - EXT30(&iData1, i64Acc1); - - - // Write output with step - // NOTE OUTPUT WRITE ORDER: First iData1, then iData0 - *piOut = iData1; - piOut += uiOutStep; - *piOut = iData0; - piOut += uiOutStep; - } - - // Write delay line index back for next round - psFIRCtrl->piDelayI = piDelayI; - - return FIR_NO_ERROR; -} - - -// ==================================================================== // -// Function: FIR_proc_sync // -// Arguments: FIRCtrl_t *psFIRCtrl: Ctrl strct. // -// Return values: FIR_NO_ERROR on success // -// FIR_ERROR on failure // -// Description: Processes the FIR in asynchronous mode // -// ==================================================================== // -FIRReturnCodes_t FIR_proc_sync(FIRCtrl_t* psFIRCtrl) -{ - int* piIn = psFIRCtrl->piIn; - unsigned int uiInStep = psFIRCtrl->uiInStep; - int* piOut = psFIRCtrl->piOut; - unsigned int uiOutStep = psFIRCtrl->uiOutStep; - int* piDelayB = psFIRCtrl->piDelayB; - int* piDelayI = psFIRCtrl->piDelayI; - int* piDelayW = psFIRCtrl->piDelayW; - unsigned int uiDelayO = psFIRCtrl->uiDelayO; - int* piCoefsB = psFIRCtrl->piCoefs; - unsigned int uiNLoops = psFIRCtrl->uiNLoops; - int* piData; - int* piCoefs; - int iData0, iData1; - int iCoef0, iCoef1; - __int64 i64Acc; - unsigned ui, uj; - - for(ui = 0; ui < psFIRCtrl->uiNInSamples; ui++) - { - // Get new data sample to delay line (double write for circular buffer simulation) with step - iData0 = *piIn; - piIn += uiInStep; - // Double write to simulate circular buffer - *piDelayI = iData0; - *(piDelayI + uiDelayO) = iData0; - // Step delay (with circular simulation) - piDelayI++; - if(piDelayI >= piDelayW) - piDelayI = piDelayB; - - // Clear accumulator and set access pointers - piData = piDelayI; - piCoefs = piCoefsB; - i64Acc = 0; - for(uj = 0; uj < uiNLoops; uj++) - { - // DUAL data read - iData0 = *piData++; - iData1 = *piData++; - // DUAL coefs read - iCoef0 = *piCoefs++; - iCoef1 = *piCoefs++; - // MACCs - MACC(&i64Acc, iData0, iCoef0); - MACC(&i64Acc, iData1, iCoef1); - } - - // Saturate MACCs result - LSAT30(&i64Acc); - // Extract 32bits result - EXT30(&iData0, i64Acc); - - // Write output with step - *piOut = iData0; - piOut += uiOutStep; - } - - // Write delay line index back for next round - psFIRCtrl->piDelayI = piDelayI; - - return FIR_NO_ERROR; -} - - -// ==================================================================== // -// Function: FIR_proc_ds2 // -// Arguments: FIRCtrl_t *psFIRCtrl: Ctrl strct. // -// Return values: FIR_NO_ERROR on success // -// FIR_ERROR on failure // -// Description: Processes the FIR in down-sample by 2 mode // -// ==================================================================== // -FIRReturnCodes_t FIR_proc_ds2(FIRCtrl_t* psFIRCtrl) -{ - int* piIn = psFIRCtrl->piIn; - unsigned int uiInStep = psFIRCtrl->uiInStep; - int* piOut = psFIRCtrl->piOut; - unsigned int uiOutStep = psFIRCtrl->uiOutStep; - int* piDelayB = psFIRCtrl->piDelayB; - int* piDelayI = psFIRCtrl->piDelayI; - int* piDelayW = psFIRCtrl->piDelayW; - unsigned int uiDelayO = psFIRCtrl->uiDelayO; - int* piCoefsB = psFIRCtrl->piCoefs; - unsigned int uiNLoops = psFIRCtrl->uiNLoops; - int* piData; - int* piCoefs; - int iData0, iData1; - int iCoef0, iCoef1; - __int64 i64Acc; - unsigned ui, uj; - - for(ui = 0; ui < psFIRCtrl->uiNInSamples>>1; ui++) - { - // Get two new data samples to delay line (double write for circular buffer simulation), with input buffer step - iData0 = *piIn; - piIn += uiInStep; - iData1 = *piIn; - piIn += uiInStep; - // Double write to simulate circular buffer with DUAL store instruction - *piDelayI = iData0; - *(piDelayI + 1) = iData1; - *(piDelayI + uiDelayO) = iData0; - *(piDelayI + uiDelayO + 1) = iData1; - // Step delay with circular simulation - piDelayI += 2; - if(piDelayI >= piDelayW) - piDelayI = piDelayB; - - // Clear accumulator and set access pointers - piData = piDelayI; - piCoefs = piCoefsB; - i64Acc = 0; - for(uj = 0; uj < uiNLoops; uj++) - { - // DUAL data read - iData0 = *piData++; - iData1 = *piData++; - // DUAL coefs read - iCoef0 = *piCoefs++; - iCoef1 = *piCoefs++; - // MACCs - MACC(&i64Acc, iData0, iCoef0); - MACC(&i64Acc, iData1, iCoef1); - } - - // Saturate MACCs result - LSAT30(&i64Acc); - // Extract 32bits result - EXT30(&iData0, i64Acc); - - // Write output with step - *piOut = iData0; - piOut += uiOutStep; - } - - // Write delay line index back for next round - psFIRCtrl->piDelayI = piDelayI; - - return FIR_NO_ERROR; -} - - -// ==================================================================== // -// Function: ADFIR_init_from_desc // -// Arguments: ADFIRCtrl_t *psADFIRCtrl: Ctrl strct. // -// ADFIRDescriptor_t *psADFIRDescriptor: Desc. strct. // -// Return values: FIR_NO_ERROR on success // -// FIR_ERROR on failure // -// Description: Inits the ADFIR from the Descriptor // -// ==================================================================== // -FIRReturnCodes_t ADFIR_init_from_desc(ADFIRCtrl_t* psADFIRCtrl, ADFIRDescriptor_t* psADFIRDescriptor) -{ - unsigned int uiPhaseLength; - - // Check that delay line base has been set - if(psADFIRCtrl->piDelayB == 0) - return FIR_ERROR; - - // Check that adaptive coefs buffer has been set - if(psADFIRCtrl->piADCoefs == 0) - return FIR_ERROR; - - // Check that number of phases and number of coefficients per phaseare set - if(psADFIRDescriptor->uiNPhases == 0) - return FIR_ERROR; - if(psADFIRDescriptor->uiNCoefsPerPhase == 0) - return FIR_ERROR; - - uiPhaseLength = psADFIRDescriptor->uiNCoefsPerPhase; - // Setup ADFIR - psADFIRCtrl->uiDelayL = uiPhaseLength<<1; // Double length for circular buffer simulation - psADFIRCtrl->piDelayW = psADFIRCtrl->piDelayB + uiPhaseLength; - psADFIRCtrl->uiDelayO = uiPhaseLength; - psADFIRCtrl->uiNLoops = uiPhaseLength>>1; // Due to 2 x 32bits read for data and coefs per inner loop - - // Sync the ADFIR - if(ADFIR_sync(psADFIRCtrl) != FIR_NO_ERROR) - return FIR_ERROR; - - return FIR_NO_ERROR; -} - -// ==================================================================== // -// Function: ADFIR_snyc // -// Arguments: ADFIRCtrl_t *psADFIRCtrl: Ctrl strct. // -// Return values: FIR_NO_ERROR on success // -// FIR_ERROR on failure // -// Description: Syncs the ADFIR filter // -// ==================================================================== // -FIRReturnCodes_t ADFIR_sync(ADFIRCtrl_t* psADFIRCtrl) -{ - unsigned int ui; - - // Set delay line index back to base - psADFIRCtrl->piDelayI = psADFIRCtrl->piDelayB; - - // Clear delay line - for(ui = 0; ui < psADFIRCtrl->uiDelayL; ui++) - psADFIRCtrl->piDelayB[ui] = FIR_STATE_INIT; - - return FIR_NO_ERROR; -} - -// ==================================================================== // -// Function: ADFIR_proc_in_sample // -// Arguments: ADFIRCtrl_t *psADFIRCtrl: Ctrl strct. // -// Return values: FIR_NO_ERROR on success // -// FIR_ERROR on failure // -// Description: Gets new input sample to delay line with step // -// ==================================================================== // -FIRReturnCodes_t ADFIR_proc_in_spl(ADFIRCtrl_t* psADFIRCtrl) -{ - // Double write to simulate circular buffer - *psADFIRCtrl->piDelayI = psADFIRCtrl->iIn; - *(psADFIRCtrl->piDelayI + psADFIRCtrl->uiDelayO) = psADFIRCtrl->iIn; - // Step delay (with circular simulation) - psADFIRCtrl->piDelayI++; - if(psADFIRCtrl->piDelayI >= psADFIRCtrl->piDelayW) - psADFIRCtrl->piDelayI = psADFIRCtrl->piDelayB; - - return FIR_NO_ERROR; -} - -// ==================================================================== // -// Function: ADFIR_proc_macc // -// Arguments: ADFIRCtrl_t *psADFIRCtrl: Ctrl strct. // -// Return values: FIR_NO_ERROR on success // -// FIR_ERROR on failure // -// Description: Processes the macc loop for the ADFIR filter // -// ==================================================================== // -FIRReturnCodes_t ADFIR_proc_macc(ADFIRCtrl_t* psADFIRCtrl) -{ - int* piData; - int* piCoefs; - __int64 i64Acc; - int iData, iCoef0, iCoef1; - unsigned int uj; - - - //*(psADFIRCtrl->piOut) = *psADFIRCtrl->piDelayI; - - // Clear accumulator and set access pointers - piData = psADFIRCtrl->piDelayI; - piCoefs = psADFIRCtrl->piADCoefs; - i64Acc = 0; - - //for(uj = 0; uj < 2 * psADFIRCtrl->uiNLoops; uj++) - // printf("FIR Phase 0 Coef %i = %i \n", uj, *(piCoefs + uj)); - //getchar(); - - // MACC loop - for(uj = 0; uj < psADFIRCtrl->uiNLoops; uj++) - { - // First data read - iData = *piData++; - // DUAL coefs read - iCoef0 = *piCoefs++; - iCoef1 = *piCoefs++; - // First MACC - MACC(&i64Acc, iData, iCoef0); - // Second data read - iData = *piData++; - // Second MACC - MACC(&i64Acc, iData, iCoef1); - } - - // Saturate MACCs result - LSAT29(&i64Acc); - // Extract 32bits result - EXT29(&iData, i64Acc); - - - // Write output - *(psADFIRCtrl->piOut) = iData; - - return FIR_NO_ERROR; +// Copyright 2023 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +// =========================================================================== +// =========================================================================== +// +// File: FIR.c +// +// FIR functions implementation file for the ASRC +// +// Target: MS Windows +// Version: 1.0 +// +// =========================================================================== +// =========================================================================== + + +// =========================================================================== +// +// Includes +// +// =========================================================================== +#include +#include +#include +#include + +// Integer arithmetic include +#include "IntArithmetic.h" +// FIR includes +#include "FIR.h" + +// =========================================================================== +// +// Defines +// +// =========================================================================== + + +// State init value +#define FIR_STATE_INIT 0 + + + + + +// =========================================================================== +// +// Variables +// +// =========================================================================== + + + +// =========================================================================== +// +// Local Functions prototypes +// +// =========================================================================== + + + +// =========================================================================== +// +// Functions implementations +// +// =========================================================================== + +// ==================================================================== // +// Function: FIR_init_from_desc // +// Arguments: FIRCtrl_t *psFIRCtrl: Ctrl strct. // +// FIRDescriptor_t *psFIRDescriptor: Desc. strct. // +// Return values: FIR_NO_ERROR on success // +// FIR_ERROR on failure // +// Description: Inits the FIR from the Descriptor // +// ==================================================================== // +FIRReturnCodes_t FIR_init_from_desc(FIRCtrl_t* psFIRCtrl, FIRDescriptor_t* psFIRDescriptor) +{ + + // Check if FIR is disabled (this is given by the number of coefficients being zero) + if( psFIRDescriptor->uiNCoefs == 0) + { + // Number of coefficients is zero, so disable FIR + psFIRCtrl->eEnable = FIR_OFF; + psFIRCtrl->uiNOutSamples = 0; + psFIRCtrl->pvProc = 0; + psFIRCtrl->uiDelayL = 0; + psFIRCtrl->piDelayW = 0; + psFIRCtrl->uiDelayO = 0; + psFIRCtrl->uiNLoops = 0; + psFIRCtrl->uiNCoefs = 0; + psFIRCtrl->piCoefs = 0; + + return FIR_NO_ERROR; + } + + // FIR is not disabled + // Check that delay line base has been set + if(psFIRCtrl->piDelayB == 0) + return FIR_ERROR; + + // Check that number of samples has been set and is a multiple of 2 + if(psFIRCtrl->uiNInSamples == 0) + return FIR_ERROR; + if((psFIRCtrl->uiNInSamples & 0x1) != 0x0) + return FIR_ERROR; + + // Check the input and output samples steps have been set + if(psFIRCtrl->uiInStep == 0) + return FIR_ERROR; + if(psFIRCtrl->uiOutStep == 0) + return FIR_ERROR; + + // Setup depending on FIR descriptor + switch(psFIRDescriptor->eType) + { + // Over-sampler by 2 type + case FIR_TYPE_OS2: + if( (psFIRDescriptor->uiNCoefs & 0x3) != 0x0) // Check that number of coefficients is a multiple of 4 + return FIR_ERROR; + psFIRCtrl->eEnable = FIR_ON; + psFIRCtrl->uiNOutSamples = (psFIRCtrl->uiNInSamples)<<1; // Os2 FIR doubles the number of samples + psFIRCtrl->pvProc = FIR_proc_os2; + psFIRCtrl->uiDelayL = psFIRDescriptor->uiNCoefs; // Double length for circular buffer simulation, but only half length due to OS2 + psFIRCtrl->piDelayW = psFIRCtrl->piDelayB + (psFIRDescriptor->uiNCoefs>>1); + psFIRCtrl->uiDelayO = psFIRDescriptor->uiNCoefs>>1; + psFIRCtrl->uiNLoops = psFIRDescriptor->uiNCoefs>>2; // Due to 2 x 32bits read for data and 4 x 32bits for coefs per inner loop + psFIRCtrl->uiNCoefs = psFIRDescriptor->uiNCoefs; + psFIRCtrl->piCoefs = psFIRDescriptor->piCoefs; + break; + + // Asynchronous type + case FIR_TYPE_SYNC: + if( (psFIRDescriptor->uiNCoefs & 0x1) != 0x0) // Check that number of coefficients is a multiple of 2 + return FIR_ERROR; + // Non zero coefficients number, so it is a true filter + psFIRCtrl->eEnable = FIR_ON; + psFIRCtrl->uiNOutSamples = psFIRCtrl->uiNInSamples; // Sync FIR does not change number of samples + psFIRCtrl->pvProc = FIR_proc_sync; + psFIRCtrl->uiDelayL = psFIRDescriptor->uiNCoefs<<1; // Double length for circular buffer simulation + psFIRCtrl->piDelayW = psFIRCtrl->piDelayB + psFIRDescriptor->uiNCoefs; + psFIRCtrl->uiDelayO = psFIRDescriptor->uiNCoefs; + psFIRCtrl->uiNLoops = psFIRDescriptor->uiNCoefs>>1; // Due to 2 x 32bits read for data and coefs per inner loop + psFIRCtrl->uiNCoefs = psFIRDescriptor->uiNCoefs; + psFIRCtrl->piCoefs = psFIRDescriptor->piCoefs; + break; + + // Down-sample by 2 type + case FIR_TYPE_DS2: + if( (psFIRDescriptor->uiNCoefs & 0x1) != 0x0) // Check that number of coefficients is a multiple of 2 + return FIR_ERROR; + psFIRCtrl->eEnable = FIR_ON; + psFIRCtrl->uiNOutSamples = psFIRCtrl->uiNInSamples>>1; // Ds2 FIR divides the number of samples by two + psFIRCtrl->pvProc = FIR_proc_ds2; + psFIRCtrl->uiDelayL = psFIRDescriptor->uiNCoefs<<1; // Double length for circular buffer simulation + psFIRCtrl->piDelayW = psFIRCtrl->piDelayB + psFIRDescriptor->uiNCoefs; + psFIRCtrl->uiDelayO = psFIRDescriptor->uiNCoefs; + psFIRCtrl->uiNLoops = psFIRDescriptor->uiNCoefs>>1; // Due to 2 x 32bits read for data and coefs per inner loop + psFIRCtrl->uiNCoefs = psFIRDescriptor->uiNCoefs; + psFIRCtrl->piCoefs = psFIRDescriptor->piCoefs; + break; + + // Unrecognized type + default: + return FIR_ERROR; + } + + // Sync the FIR + if(FIR_sync(psFIRCtrl) != FIR_NO_ERROR) + return FIR_ERROR; + + return FIR_NO_ERROR; +} + + +// ==================================================================== // +// Function: FIR_sync // +// Arguments: FIRCtrl_t *psFIRCtrl: Ctrl strct. // +// Return values: FIR_NO_ERROR on success // +// FIR_ERROR on failure // +// Description: Syncs the FIR // +// ==================================================================== // +FIRReturnCodes_t FIR_sync(FIRCtrl_t* psFIRCtrl) +{ + unsigned int ui; + + if(psFIRCtrl->eEnable == FIR_ON) + { + // Set delay line index back to base + psFIRCtrl->piDelayI = psFIRCtrl->piDelayB; + + // Clear delay line + for(ui = 0; ui < psFIRCtrl->uiDelayL; ui++) + psFIRCtrl->piDelayB[ui] = FIR_STATE_INIT; + } + + return FIR_NO_ERROR; +} + + +// ==================================================================== // +// Function: FIR_proc_os2 // +// Arguments: FIRCtrl_t *psFIRCtrl: Ctrl strct. // +// Return values: FIR_NO_ERROR on success // +// FIR_ERROR on failure // +// Description: Processes the FIR in over-sample by 2 mode // +// ==================================================================== // +FIRReturnCodes_t FIR_proc_os2(FIRCtrl_t* psFIRCtrl) +{ + int* piIn = psFIRCtrl->piIn; + unsigned int uiInStep = psFIRCtrl->uiInStep; + int* piOut = psFIRCtrl->piOut; + unsigned int uiOutStep = psFIRCtrl->uiOutStep; + int* piDelayB = psFIRCtrl->piDelayB; + int* piDelayI = psFIRCtrl->piDelayI; + int* piDelayW = psFIRCtrl->piDelayW; + unsigned int uiDelayO = psFIRCtrl->uiDelayO; + int* piCoefsB = psFIRCtrl->piCoefs; + unsigned int uiNLoops = psFIRCtrl->uiNLoops; + int* piData; + int* piCoefs; + int iData0, iData1; + int iCoef0, iCoef1; + __int64 i64Acc0, i64Acc1; + unsigned ui, uj; + + + for(ui = 0; ui < psFIRCtrl->uiNInSamples; ui++) + { + // Get new data sample to delay line (double write for circular buffer simulation) with step + iData0 = *piIn; + piIn += uiInStep; + // Double write to simulate circular buffer + *piDelayI = iData0; + *(piDelayI + uiDelayO) = iData0; + // Step delay (with circular simulation) + piDelayI++; + if(piDelayI >= piDelayW) + piDelayI = piDelayB; + + // Clear accumulators and set access pointers + piData = piDelayI; + piCoefs = piCoefsB; + i64Acc0 = 0; + i64Acc1 = 0; + for(uj = 0; uj < uiNLoops; uj++) + { + // data read + iData0 = *piData++; + iData1 = *piData++; + // DUAL coefs read (two phases for first data) + iCoef0 = *piCoefs++; + iCoef1 = *piCoefs++; + // MACCs (for first data) + MACC(&i64Acc0, iData0, iCoef0); + MACC(&i64Acc1, iData0, iCoef1); + // DUAL coefs read (two phases for second data) + iCoef0 = *piCoefs++; + iCoef1 = *piCoefs++; + // MACCs (for second data) + MACC(&i64Acc0, iData1, iCoef0); + MACC(&i64Acc1, iData1, iCoef1); + } + + // Saturate MACCs result + LSAT30(&i64Acc0); + LSAT30(&i64Acc1); + // Extract 32bits result + EXT30(&iData0, i64Acc0); + EXT30(&iData1, i64Acc1); + + + // Write output with step + // NOTE OUTPUT WRITE ORDER: First iData1, then iData0 + *piOut = iData1; + piOut += uiOutStep; + *piOut = iData0; + piOut += uiOutStep; + } + + // Write delay line index back for next round + psFIRCtrl->piDelayI = piDelayI; + + return FIR_NO_ERROR; +} + + +// ==================================================================== // +// Function: FIR_proc_sync // +// Arguments: FIRCtrl_t *psFIRCtrl: Ctrl strct. // +// Return values: FIR_NO_ERROR on success // +// FIR_ERROR on failure // +// Description: Processes the FIR in asynchronous mode // +// ==================================================================== // +FIRReturnCodes_t FIR_proc_sync(FIRCtrl_t* psFIRCtrl) +{ + int* piIn = psFIRCtrl->piIn; + unsigned int uiInStep = psFIRCtrl->uiInStep; + int* piOut = psFIRCtrl->piOut; + unsigned int uiOutStep = psFIRCtrl->uiOutStep; + int* piDelayB = psFIRCtrl->piDelayB; + int* piDelayI = psFIRCtrl->piDelayI; + int* piDelayW = psFIRCtrl->piDelayW; + unsigned int uiDelayO = psFIRCtrl->uiDelayO; + int* piCoefsB = psFIRCtrl->piCoefs; + unsigned int uiNLoops = psFIRCtrl->uiNLoops; + int* piData; + int* piCoefs; + int iData0, iData1; + int iCoef0, iCoef1; + __int64 i64Acc; + unsigned ui, uj; + + for(ui = 0; ui < psFIRCtrl->uiNInSamples; ui++) + { + // Get new data sample to delay line (double write for circular buffer simulation) with step + iData0 = *piIn; + piIn += uiInStep; + // Double write to simulate circular buffer + *piDelayI = iData0; + *(piDelayI + uiDelayO) = iData0; + // Step delay (with circular simulation) + piDelayI++; + if(piDelayI >= piDelayW) + piDelayI = piDelayB; + + // Clear accumulator and set access pointers + piData = piDelayI; + piCoefs = piCoefsB; + i64Acc = 0; + for(uj = 0; uj < uiNLoops; uj++) + { + // DUAL data read + iData0 = *piData++; + iData1 = *piData++; + // DUAL coefs read + iCoef0 = *piCoefs++; + iCoef1 = *piCoefs++; + // MACCs + MACC(&i64Acc, iData0, iCoef0); + MACC(&i64Acc, iData1, iCoef1); + } + + // Saturate MACCs result + LSAT30(&i64Acc); + // Extract 32bits result + EXT30(&iData0, i64Acc); + + // Write output with step + *piOut = iData0; + piOut += uiOutStep; + } + + // Write delay line index back for next round + psFIRCtrl->piDelayI = piDelayI; + + return FIR_NO_ERROR; +} + + +// ==================================================================== // +// Function: FIR_proc_ds2 // +// Arguments: FIRCtrl_t *psFIRCtrl: Ctrl strct. // +// Return values: FIR_NO_ERROR on success // +// FIR_ERROR on failure // +// Description: Processes the FIR in down-sample by 2 mode // +// ==================================================================== // +FIRReturnCodes_t FIR_proc_ds2(FIRCtrl_t* psFIRCtrl) +{ + int* piIn = psFIRCtrl->piIn; + unsigned int uiInStep = psFIRCtrl->uiInStep; + int* piOut = psFIRCtrl->piOut; + unsigned int uiOutStep = psFIRCtrl->uiOutStep; + int* piDelayB = psFIRCtrl->piDelayB; + int* piDelayI = psFIRCtrl->piDelayI; + int* piDelayW = psFIRCtrl->piDelayW; + unsigned int uiDelayO = psFIRCtrl->uiDelayO; + int* piCoefsB = psFIRCtrl->piCoefs; + unsigned int uiNLoops = psFIRCtrl->uiNLoops; + int* piData; + int* piCoefs; + int iData0, iData1; + int iCoef0, iCoef1; + __int64 i64Acc; + unsigned ui, uj; + + for(ui = 0; ui < psFIRCtrl->uiNInSamples>>1; ui++) + { + // Get two new data samples to delay line (double write for circular buffer simulation), with input buffer step + iData0 = *piIn; + piIn += uiInStep; + iData1 = *piIn; + piIn += uiInStep; + // Double write to simulate circular buffer with DUAL store instruction + *piDelayI = iData0; + *(piDelayI + 1) = iData1; + *(piDelayI + uiDelayO) = iData0; + *(piDelayI + uiDelayO + 1) = iData1; + // Step delay with circular simulation + piDelayI += 2; + if(piDelayI >= piDelayW) + piDelayI = piDelayB; + + // Clear accumulator and set access pointers + piData = piDelayI; + piCoefs = piCoefsB; + i64Acc = 0; + for(uj = 0; uj < uiNLoops; uj++) + { + // DUAL data read + iData0 = *piData++; + iData1 = *piData++; + // DUAL coefs read + iCoef0 = *piCoefs++; + iCoef1 = *piCoefs++; + // MACCs + MACC(&i64Acc, iData0, iCoef0); + MACC(&i64Acc, iData1, iCoef1); + } + + // Saturate MACCs result + LSAT30(&i64Acc); + // Extract 32bits result + EXT30(&iData0, i64Acc); + + // Write output with step + *piOut = iData0; + piOut += uiOutStep; + } + + // Write delay line index back for next round + psFIRCtrl->piDelayI = piDelayI; + + return FIR_NO_ERROR; +} + + +// ==================================================================== // +// Function: ADFIR_init_from_desc // +// Arguments: ADFIRCtrl_t *psADFIRCtrl: Ctrl strct. // +// ADFIRDescriptor_t *psADFIRDescriptor: Desc. strct. // +// Return values: FIR_NO_ERROR on success // +// FIR_ERROR on failure // +// Description: Inits the ADFIR from the Descriptor // +// ==================================================================== // +FIRReturnCodes_t ADFIR_init_from_desc(ADFIRCtrl_t* psADFIRCtrl, ADFIRDescriptor_t* psADFIRDescriptor) +{ + unsigned int uiPhaseLength; + + // Check that delay line base has been set + if(psADFIRCtrl->piDelayB == 0) + return FIR_ERROR; + + // Check that adaptive coefs buffer has been set + if(psADFIRCtrl->piADCoefs == 0) + return FIR_ERROR; + + // Check that number of phases and number of coefficients per phaseare set + if(psADFIRDescriptor->uiNPhases == 0) + return FIR_ERROR; + if(psADFIRDescriptor->uiNCoefsPerPhase == 0) + return FIR_ERROR; + + uiPhaseLength = psADFIRDescriptor->uiNCoefsPerPhase; + // Setup ADFIR + psADFIRCtrl->uiDelayL = uiPhaseLength<<1; // Double length for circular buffer simulation + psADFIRCtrl->piDelayW = psADFIRCtrl->piDelayB + uiPhaseLength; + psADFIRCtrl->uiDelayO = uiPhaseLength; + psADFIRCtrl->uiNLoops = uiPhaseLength>>1; // Due to 2 x 32bits read for data and coefs per inner loop + + // Sync the ADFIR + if(ADFIR_sync(psADFIRCtrl) != FIR_NO_ERROR) + return FIR_ERROR; + + return FIR_NO_ERROR; +} + +// ==================================================================== // +// Function: ADFIR_snyc // +// Arguments: ADFIRCtrl_t *psADFIRCtrl: Ctrl strct. // +// Return values: FIR_NO_ERROR on success // +// FIR_ERROR on failure // +// Description: Syncs the ADFIR filter // +// ==================================================================== // +FIRReturnCodes_t ADFIR_sync(ADFIRCtrl_t* psADFIRCtrl) +{ + unsigned int ui; + + // Set delay line index back to base + psADFIRCtrl->piDelayI = psADFIRCtrl->piDelayB; + + // Clear delay line + for(ui = 0; ui < psADFIRCtrl->uiDelayL; ui++) + psADFIRCtrl->piDelayB[ui] = FIR_STATE_INIT; + + return FIR_NO_ERROR; +} + +// ==================================================================== // +// Function: ADFIR_proc_in_sample // +// Arguments: ADFIRCtrl_t *psADFIRCtrl: Ctrl strct. // +// Return values: FIR_NO_ERROR on success // +// FIR_ERROR on failure // +// Description: Gets new input sample to delay line with step // +// ==================================================================== // +FIRReturnCodes_t ADFIR_proc_in_spl(ADFIRCtrl_t* psADFIRCtrl) +{ + // Double write to simulate circular buffer + *psADFIRCtrl->piDelayI = psADFIRCtrl->iIn; + *(psADFIRCtrl->piDelayI + psADFIRCtrl->uiDelayO) = psADFIRCtrl->iIn; + // Step delay (with circular simulation) + psADFIRCtrl->piDelayI++; + if(psADFIRCtrl->piDelayI >= psADFIRCtrl->piDelayW) + psADFIRCtrl->piDelayI = psADFIRCtrl->piDelayB; + + return FIR_NO_ERROR; +} + +// ==================================================================== // +// Function: ADFIR_proc_macc // +// Arguments: ADFIRCtrl_t *psADFIRCtrl: Ctrl strct. // +// Return values: FIR_NO_ERROR on success // +// FIR_ERROR on failure // +// Description: Processes the macc loop for the ADFIR filter // +// ==================================================================== // +FIRReturnCodes_t ADFIR_proc_macc(ADFIRCtrl_t* psADFIRCtrl) +{ + int* piData; + int* piCoefs; + __int64 i64Acc; + int iData, iCoef0, iCoef1; + unsigned int uj; + + + //*(psADFIRCtrl->piOut) = *psADFIRCtrl->piDelayI; + + // Clear accumulator and set access pointers + piData = psADFIRCtrl->piDelayI; + piCoefs = psADFIRCtrl->piADCoefs; + i64Acc = 0; + + //for(uj = 0; uj < 2 * psADFIRCtrl->uiNLoops; uj++) + // printf("FIR Phase 0 Coef %i = %i \n", uj, *(piCoefs + uj)); + //getchar(); + + // MACC loop + for(uj = 0; uj < psADFIRCtrl->uiNLoops; uj++) + { + // First data read + iData = *piData++; + // DUAL coefs read + iCoef0 = *piCoefs++; + iCoef1 = *piCoefs++; + // First MACC + MACC(&i64Acc, iData, iCoef0); + // Second data read + iData = *piData++; + // Second MACC + MACC(&i64Acc, iData, iCoef1); + } + + // Saturate MACCs result + LSAT29(&i64Acc); + // Extract 32bits result + EXT29(&iData, i64Acc); + + + // Write output + *(psADFIRCtrl->piOut) = iData; + + return FIR_NO_ERROR; } \ No newline at end of file diff --git a/tests/asrc_test/model/FIR.h b/tests/asrc_test/model/src/FIR.h similarity index 97% rename from tests/asrc_test/model/FIR.h rename to tests/asrc_test/model/src/FIR.h index a2f86f22..52dd8b97 100644 --- a/tests/asrc_test/model/FIR.h +++ b/tests/asrc_test/model/src/FIR.h @@ -1,230 +1,232 @@ -// =========================================================================== -// =========================================================================== -// -// File: FIR.h -// -// FIR functions definition file for the ASRC -// -// Target: MS Windows -// Version: 1.0 -// -// =========================================================================== -// =========================================================================== - -#ifndef _FIR_H_ -#define _FIR_H_ - - // =========================================================================== - // - // Defines - // - // =========================================================================== - - // General defines - // --------------- - - - // Parameter values - // ---------------- - - - - - // =========================================================================== - // - // Defines - // - // =========================================================================== - - - // =========================================================================== - // - // TypeDefs - // - // =========================================================================== - - // To avoid C type definitions when including this file from assembler - #ifndef INCLUDE_FROM_ASM - - // FIR Return Codes - // ---------------- - typedef enum _FIRReturnCodes - { - FIR_NO_ERROR = 0, - FIR_ERROR = 1 - } FIRReturnCodes_t; - - // FIR On/Off Codes - // ---------------- - typedef enum _FIROnOffCodes - { - FIR_OFF = 0, - FIR_ON = 1 - } FIROnOffCodes_t; - - - // FIR Type Codes - // -------------- - typedef enum _FIRTypeCodes - { - FIR_TYPE_OS2 = 0, // Over-sampler by two FIR - FIR_TYPE_SYNC = 1, // Asynchronous (low-pass) FIR - FIR_TYPE_DS2 = 2, // Down-sampler by two FIR - } FIRTypeCodes_t; - - // FIR Descriptor - // -------------- - typedef struct _FIRDescriptor - { - FIRTypeCodes_t eType; // Type of filter - unsigned int uiNCoefs; // Number of coefficients - int* piCoefs; // Pointer to coefficients - } FIRDescriptor_t; - - - // FIR Ctrl - // -------- - typedef struct _FIRCtrl - { - FIROnOffCodes_t eEnable; // FIR on/off - - int* piIn; // Pointer to input data - unsigned int uiNInSamples; // Number of input samples to process - unsigned int uiInStep; // Step between input data samples - int* piOut; // Pointer to output data - unsigned int uiNOutSamples; // Number of output samples produced - unsigned int uiOutStep; // Step between output data samples - - FIRReturnCodes_t (*pvProc)(void*); // Processing function address - - int* piDelayB; // Pointer to delay line base - unsigned int uiDelayL; // Total length of delay line - int* piDelayI; // Pointer to current position in delay line - int* piDelayW; // Delay buffer wrap around address (for circular buffer simulation) - unsigned int uiDelayO; // Delay line offset for second write (for circular buffer simulation) - - unsigned int uiNLoops; // Number of inner loop iterations - unsigned int uiNCoefs; // Number of coefficients - int* piCoefs; // Pointer to coefficients - } FIRCtrl_t; - - - // ADFIR Descriptor - // ---------------- - typedef struct _ADFIRDescriptor - { - unsigned int uiNCoefsPerPhase; // Number of coefficients - unsigned int uiNPhases; // Number of phases - int* piCoefs; // Pointer to coefficients - } ADFIRDescriptor_t; - - // ADFIR Ctrl - // ---------- - typedef struct _ADFIRCtrl - { - int iIn; // Input sample - int* piOut; // Pointer to output sample - - int* piDelayB; // Pointer to delay line base - unsigned int uiDelayL; // Total length of delay line - int* piDelayI; // Pointer to current position in delay line - int* piDelayW; // Delay buffer wrap around address (for circular buffer simulation) - unsigned int uiDelayO; // Delay line offset for second write (for circular buffer simulation) - - unsigned int uiNLoops; // Number of inner loop iterations - int* piADCoefs; // Pointer to adaptive coefficients - } ADFIRCtrl_t; - - - // =========================================================================== - // - // Function prototypes - // - // =========================================================================== - - // ==================================================================== // - // Function: FIR_init_from_desc // - // Arguments: FIRCtrl_t *psFIRCtrl: Ctrl strct. // - // FIRDescriptor_t *psFIRDescriptor: Desc. strct. // - // Return values: FIR_NO_ERROR on success // - // FIR_ERROR on failure // - // Description: Inits the FIR from the Descriptor // - // ==================================================================== // - FIRReturnCodes_t FIR_init_from_desc(FIRCtrl_t* psFIRCtrl, FIRDescriptor_t* psFIRDescriptor); - - // ==================================================================== // - // Function: FIR_sync // - // Arguments: FIRCtrl_t *psFIRCtrl: Ctrl strct. // - // Return values: FIR_NO_ERROR on success // - // FIR_ERROR on failure // - // Description: Syncs the FIR // - // ==================================================================== // - FIRReturnCodes_t FIR_sync(FIRCtrl_t* psFIRCtrl); - - // ==================================================================== // - // Function: FIR_proc_os2 // - // Arguments: FIRCtrl_t *psFIRCtrl: Ctrl strct. // - // Return values: FIR_NO_ERROR on success // - // FIR_ERROR on failure // - // Description: Processes the FIR in over-sample by 2 mode // - // ==================================================================== // - FIRReturnCodes_t FIR_proc_os2(FIRCtrl_t* psFIRCtrl); - - // ==================================================================== // - // Function: FIR_proc_sync // - // Arguments: FIRCtrl_t *psFIRCtrl: Ctrl strct. // - // Return values: FIR_NO_ERROR on success // - // FIR_ERROR on failure // - // Description: Processes the FIR in asynchronous mode // - // ==================================================================== // - FIRReturnCodes_t FIR_proc_sync(FIRCtrl_t* psFIRCtrl); - - // ==================================================================== // - // Function: FIR_proc_ds2 // - // Arguments: FIRCtrl_t *psFIRCtrl: Ctrl strct. // - // Return values: FIR_NO_ERROR on success // - // FIR_ERROR on failure // - // Description: Processes the FIR in down-sample by 2 mode // - // ==================================================================== // - FIRReturnCodes_t FIR_proc_ds2(FIRCtrl_t* psFIRCtrl); - - // ==================================================================== // - // Function: ADFIR_init_from_desc // - // Arguments: ADFIRCtrl_t *psADFIRCtrl: Ctrl strct. // - // ADFIRDescriptor_t *psADFIRDescriptor: Desc. strct. // - // Return values: FIR_NO_ERROR on success // - // FIR_ERROR on failure // - // Description: Inits the ADFIR from the Descriptor // - // ==================================================================== // - FIRReturnCodes_t ADFIR_init_from_desc(ADFIRCtrl_t* psADFIRCtrl, ADFIRDescriptor_t* psADFIRDescriptor); - - // ==================================================================== // - // Function: ADFIR_snyc // - // Arguments: ADFIRCtrl_t *psADFIRCtrl: Ctrl strct. // - // Return values: FIR_NO_ERROR on success // - // FIR_ERROR on failure // - // Description: Syncs the ADFIR filter // - // ==================================================================== // - FIRReturnCodes_t ADFIR_sync(ADFIRCtrl_t* psADFIRCtrl); - - // ==================================================================== // - // Function: ADFIR_proc_in_sample // - // Arguments: ADFIRCtrl_t *psADFIRCtrl: Ctrl strct. // - // Return values: FIR_NO_ERROR on success // - // FIR_ERROR on failure // - // Description: Gets new input sample to delay line with step // - // ==================================================================== // - FIRReturnCodes_t ADFIR_proc_in_spl(ADFIRCtrl_t* psADFIRCtrl); - - // ==================================================================== // - // Function: ADFIR_proc_macc // - // Arguments: ADFIRCtrl_t *psADFIRCtrl: Ctrl strct. // - // Return values: FIR_NO_ERROR on success // - // FIR_ERROR on failure // - // Description: Processes the macc loop for the ADFIR filter // - // ==================================================================== // - FIRReturnCodes_t ADFIR_proc_macc(ADFIRCtrl_t* psADFIRCtrl); - - #endif // nINCLUDE_FROM_ASM - +// Copyright 2023 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +// =========================================================================== +// =========================================================================== +// +// File: FIR.h +// +// FIR functions definition file for the ASRC +// +// Target: MS Windows +// Version: 1.0 +// +// =========================================================================== +// =========================================================================== + +#ifndef _FIR_H_ +#define _FIR_H_ + + // =========================================================================== + // + // Defines + // + // =========================================================================== + + // General defines + // --------------- + + + // Parameter values + // ---------------- + + + + + // =========================================================================== + // + // Defines + // + // =========================================================================== + + + // =========================================================================== + // + // TypeDefs + // + // =========================================================================== + + // To avoid C type definitions when including this file from assembler + #ifndef INCLUDE_FROM_ASM + + // FIR Return Codes + // ---------------- + typedef enum _FIRReturnCodes + { + FIR_NO_ERROR = 0, + FIR_ERROR = 1 + } FIRReturnCodes_t; + + // FIR On/Off Codes + // ---------------- + typedef enum _FIROnOffCodes + { + FIR_OFF = 0, + FIR_ON = 1 + } FIROnOffCodes_t; + + + // FIR Type Codes + // -------------- + typedef enum _FIRTypeCodes + { + FIR_TYPE_OS2 = 0, // Over-sampler by two FIR + FIR_TYPE_SYNC = 1, // Asynchronous (low-pass) FIR + FIR_TYPE_DS2 = 2, // Down-sampler by two FIR + } FIRTypeCodes_t; + + // FIR Descriptor + // -------------- + typedef struct _FIRDescriptor + { + FIRTypeCodes_t eType; // Type of filter + unsigned int uiNCoefs; // Number of coefficients + int* piCoefs; // Pointer to coefficients + } FIRDescriptor_t; + + + // FIR Ctrl + // -------- + typedef struct _FIRCtrl + { + FIROnOffCodes_t eEnable; // FIR on/off + + int* piIn; // Pointer to input data + unsigned int uiNInSamples; // Number of input samples to process + unsigned int uiInStep; // Step between input data samples + int* piOut; // Pointer to output data + unsigned int uiNOutSamples; // Number of output samples produced + unsigned int uiOutStep; // Step between output data samples + + FIRReturnCodes_t (*pvProc)(void*); // Processing function address + + int* piDelayB; // Pointer to delay line base + unsigned int uiDelayL; // Total length of delay line + int* piDelayI; // Pointer to current position in delay line + int* piDelayW; // Delay buffer wrap around address (for circular buffer simulation) + unsigned int uiDelayO; // Delay line offset for second write (for circular buffer simulation) + + unsigned int uiNLoops; // Number of inner loop iterations + unsigned int uiNCoefs; // Number of coefficients + int* piCoefs; // Pointer to coefficients + } FIRCtrl_t; + + + // ADFIR Descriptor + // ---------------- + typedef struct _ADFIRDescriptor + { + unsigned int uiNCoefsPerPhase; // Number of coefficients + unsigned int uiNPhases; // Number of phases + int* piCoefs; // Pointer to coefficients + } ADFIRDescriptor_t; + + // ADFIR Ctrl + // ---------- + typedef struct _ADFIRCtrl + { + int iIn; // Input sample + int* piOut; // Pointer to output sample + + int* piDelayB; // Pointer to delay line base + unsigned int uiDelayL; // Total length of delay line + int* piDelayI; // Pointer to current position in delay line + int* piDelayW; // Delay buffer wrap around address (for circular buffer simulation) + unsigned int uiDelayO; // Delay line offset for second write (for circular buffer simulation) + + unsigned int uiNLoops; // Number of inner loop iterations + int* piADCoefs; // Pointer to adaptive coefficients + } ADFIRCtrl_t; + + + // =========================================================================== + // + // Function prototypes + // + // =========================================================================== + + // ==================================================================== // + // Function: FIR_init_from_desc // + // Arguments: FIRCtrl_t *psFIRCtrl: Ctrl strct. // + // FIRDescriptor_t *psFIRDescriptor: Desc. strct. // + // Return values: FIR_NO_ERROR on success // + // FIR_ERROR on failure // + // Description: Inits the FIR from the Descriptor // + // ==================================================================== // + FIRReturnCodes_t FIR_init_from_desc(FIRCtrl_t* psFIRCtrl, FIRDescriptor_t* psFIRDescriptor); + + // ==================================================================== // + // Function: FIR_sync // + // Arguments: FIRCtrl_t *psFIRCtrl: Ctrl strct. // + // Return values: FIR_NO_ERROR on success // + // FIR_ERROR on failure // + // Description: Syncs the FIR // + // ==================================================================== // + FIRReturnCodes_t FIR_sync(FIRCtrl_t* psFIRCtrl); + + // ==================================================================== // + // Function: FIR_proc_os2 // + // Arguments: FIRCtrl_t *psFIRCtrl: Ctrl strct. // + // Return values: FIR_NO_ERROR on success // + // FIR_ERROR on failure // + // Description: Processes the FIR in over-sample by 2 mode // + // ==================================================================== // + FIRReturnCodes_t FIR_proc_os2(FIRCtrl_t* psFIRCtrl); + + // ==================================================================== // + // Function: FIR_proc_sync // + // Arguments: FIRCtrl_t *psFIRCtrl: Ctrl strct. // + // Return values: FIR_NO_ERROR on success // + // FIR_ERROR on failure // + // Description: Processes the FIR in asynchronous mode // + // ==================================================================== // + FIRReturnCodes_t FIR_proc_sync(FIRCtrl_t* psFIRCtrl); + + // ==================================================================== // + // Function: FIR_proc_ds2 // + // Arguments: FIRCtrl_t *psFIRCtrl: Ctrl strct. // + // Return values: FIR_NO_ERROR on success // + // FIR_ERROR on failure // + // Description: Processes the FIR in down-sample by 2 mode // + // ==================================================================== // + FIRReturnCodes_t FIR_proc_ds2(FIRCtrl_t* psFIRCtrl); + + // ==================================================================== // + // Function: ADFIR_init_from_desc // + // Arguments: ADFIRCtrl_t *psADFIRCtrl: Ctrl strct. // + // ADFIRDescriptor_t *psADFIRDescriptor: Desc. strct. // + // Return values: FIR_NO_ERROR on success // + // FIR_ERROR on failure // + // Description: Inits the ADFIR from the Descriptor // + // ==================================================================== // + FIRReturnCodes_t ADFIR_init_from_desc(ADFIRCtrl_t* psADFIRCtrl, ADFIRDescriptor_t* psADFIRDescriptor); + + // ==================================================================== // + // Function: ADFIR_snyc // + // Arguments: ADFIRCtrl_t *psADFIRCtrl: Ctrl strct. // + // Return values: FIR_NO_ERROR on success // + // FIR_ERROR on failure // + // Description: Syncs the ADFIR filter // + // ==================================================================== // + FIRReturnCodes_t ADFIR_sync(ADFIRCtrl_t* psADFIRCtrl); + + // ==================================================================== // + // Function: ADFIR_proc_in_sample // + // Arguments: ADFIRCtrl_t *psADFIRCtrl: Ctrl strct. // + // Return values: FIR_NO_ERROR on success // + // FIR_ERROR on failure // + // Description: Gets new input sample to delay line with step // + // ==================================================================== // + FIRReturnCodes_t ADFIR_proc_in_spl(ADFIRCtrl_t* psADFIRCtrl); + + // ==================================================================== // + // Function: ADFIR_proc_macc // + // Arguments: ADFIRCtrl_t *psADFIRCtrl: Ctrl strct. // + // Return values: FIR_NO_ERROR on success // + // FIR_ERROR on failure // + // Description: Processes the macc loop for the ADFIR filter // + // ==================================================================== // + FIRReturnCodes_t ADFIR_proc_macc(ADFIRCtrl_t* psADFIRCtrl); + + #endif // nINCLUDE_FROM_ASM + #endif // _FIR_H_ \ No newline at end of file diff --git a/tests/asrc_test/model/FilterDefs.c b/tests/asrc_test/model/src/FilterDefs.c similarity index 96% rename from tests/asrc_test/model/FilterDefs.c rename to tests/asrc_test/model/src/FilterDefs.c index 8a6909f8..9ed03b6b 100644 --- a/tests/asrc_test/model/FilterDefs.c +++ b/tests/asrc_test/model/src/FilterDefs.c @@ -1,135 +1,137 @@ -// =========================================================================== -// =========================================================================== -// -// File: FilterDefs.c -// -// Filters Definitions implementation file for the ASRC -// -// Target: MS Windows -// Version: 1.0 -// -// =========================================================================== -// =========================================================================== - - -// =========================================================================== -// -// Includes -// -// =========================================================================== -#include -#include -#include -#include - -// Integer arithmetic include -#include "IntArithmetic.h" -// FIR and FilterDefs includes -#include "FIR.h" -#include "FilterDefs.h" - -// =========================================================================== -// -// Defines -// -// =========================================================================== - - - - -// =========================================================================== -// -// Variables -// -// =========================================================================== - -// FIR filters descriptors (ordered by ID) -FIRDescriptor_t sFirDescriptor[FILTER_DEFS_N_FIR_ID] = -{ - {FIR_TYPE_SYNC, FILTER_DEFS_FIR_BL_N_TAPS, iFirBLCoefs}, // FILTER_DEFS_FIR_BL_ID - {FIR_TYPE_SYNC, FILTER_DEFS_FIR_BL9644_N_TAPS, iFirBL9644Coefs}, // FILTER_DEFS_FIR_BL9644_ID - {FIR_TYPE_SYNC, FILTER_DEFS_FIR_BL8848_N_TAPS, iFirBL8848}, // FILTER_DEFS_FIR_BL8848_ID - {FIR_TYPE_SYNC, FILTER_DEFS_FIR_BLF_N_TAPS, iFirBLFCoefs}, // FILTER_DEFS_FIR_BLF_ID - {FIR_TYPE_SYNC, FILTER_DEFS_FIR_BL19288_N_TAPS, iFirBL19288Coefs}, // FILTER_DEFS_FIR_BL19288_ID - {FIR_TYPE_SYNC, FILTER_DEFS_FIR_BL17696_N_TAPS, iFirBL17696}, // FILTER_DEFS_FIR_BL17696_ID - {FIR_TYPE_OS2, FILTER_DEFS_FIR_UP_N_TAPS, iFirUPCoefs}, // FILTER_DEFS_FIR_UP_ID - {FIR_TYPE_OS2, FILTER_DEFS_FIR_UP4844_N_TAPS, iFirUP4844Coefs}, // FILTER_DEFS_FIR_UP4844_ID - {FIR_TYPE_OS2, FILTER_DEFS_FIR_UPF_N_TAPS, iFirUPFCoefs}, // FILTER_DEFS_FIR_UPF_ID - {FIR_TYPE_OS2, FILTER_DEFS_FIR_UP192176_N_TAPS, iFirUP192176Coefs}, // FILTER_DEFS_FIR_UP192176_ID - {FIR_TYPE_DS2, FILTER_DEFS_FIR_DS_N_TAPS, iFirDSCoefs}, // FILTER_DEFS_FIR_DS_ID - {FIR_TYPE_SYNC, 0, 0} // FILTER_DEFS_FIR_NONE_ID -}; - -// FIR filters coefficients -int iFirBLCoefs[FILTER_DEFS_FIR_BL_N_TAPS] = { - #include FILTER_DEFS_FIR_BL_FILE -}; -int iFirBL9644Coefs[FILTER_DEFS_FIR_BL9644_N_TAPS] = { - #include FILTER_DEFS_FIR_BL9644_FILE -}; -int iFirBL8848[FILTER_DEFS_FIR_BL8848_N_TAPS] = { - #include FILTER_DEFS_FIR_BL8848_FILE -}; -int iFirBLFCoefs[FILTER_DEFS_FIR_BLF_N_TAPS] = { - #include FILTER_DEFS_FIR_BLF_FILE -}; -int iFirBL19288Coefs[FILTER_DEFS_FIR_BL19288_N_TAPS] = { - #include FILTER_DEFS_FIR_BL19288_FILE -}; -int iFirBL17696[FILTER_DEFS_FIR_BL17696_N_TAPS] = { - #include FILTER_DEFS_FIR_BL17696_FILE -}; -int iFirUPCoefs[FILTER_DEFS_FIR_UP_N_TAPS] = { - #include FILTER_DEFS_FIR_UP_FILE -}; -int iFirUP4844Coefs[FILTER_DEFS_FIR_UP4844_N_TAPS] = { - #include FILTER_DEFS_FIR_UP4844_FILE -}; -int iFirUPFCoefs[FILTER_DEFS_FIR_UPF_N_TAPS] = { - #include FILTER_DEFS_FIR_UPF_FILE -}; -int iFirUP192176Coefs[FILTER_DEFS_FIR_UP192176_N_TAPS] = { - #include FILTER_DEFS_FIR_UP192176_FILE -}; -int iFirDSCoefs[FILTER_DEFS_FIR_DS_N_TAPS] = { - #include FILTER_DEFS_FIR_DS_FILE -}; - - - -// ADFIR filters descriptor -ADFIRDescriptor_t sADFirDescriptor = -{ - FILTER_DEFS_ADFIR_PHASE_N_TAPS, FILTER_DEFS_ADFIR_N_PHASES + 2, iADFirCoefs -}; - -// ADFIR filter coefficients -int iADFirPrototypeCoefs[FILTER_DEFS_ADFIR_PROTOTYPE_N_TAPS] = { - #include FILTER_DEFS_ADFIR_PROTOTYPE_FILE -}; -int iADFirCoefs[FILTER_DEFS_ADFIR_N_PHASES + 2][FILTER_DEFS_ADFIR_PHASE_N_TAPS]; - - - - - - -// =========================================================================== -// -// Local Functions prototypes -// -// =========================================================================== - - - - -// =========================================================================== -// -// Functions implementations -// -// =========================================================================== - - - - - +// Copyright 2023 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +// =========================================================================== +// =========================================================================== +// +// File: FilterDefs.c +// +// Filters Definitions implementation file for the ASRC +// +// Target: MS Windows +// Version: 1.0 +// +// =========================================================================== +// =========================================================================== + + +// =========================================================================== +// +// Includes +// +// =========================================================================== +#include +#include +#include +#include + +// Integer arithmetic include +#include "IntArithmetic.h" +// FIR and FilterDefs includes +#include "FIR.h" +#include "FilterDefs.h" + +// =========================================================================== +// +// Defines +// +// =========================================================================== + + + + +// =========================================================================== +// +// Variables +// +// =========================================================================== + +// FIR filters descriptors (ordered by ID) +FIRDescriptor_t sFirDescriptor[FILTER_DEFS_N_FIR_ID] = +{ + {FIR_TYPE_SYNC, FILTER_DEFS_FIR_BL_N_TAPS, iFirBLCoefs}, // FILTER_DEFS_FIR_BL_ID + {FIR_TYPE_SYNC, FILTER_DEFS_FIR_BL9644_N_TAPS, iFirBL9644Coefs}, // FILTER_DEFS_FIR_BL9644_ID + {FIR_TYPE_SYNC, FILTER_DEFS_FIR_BL8848_N_TAPS, iFirBL8848}, // FILTER_DEFS_FIR_BL8848_ID + {FIR_TYPE_SYNC, FILTER_DEFS_FIR_BLF_N_TAPS, iFirBLFCoefs}, // FILTER_DEFS_FIR_BLF_ID + {FIR_TYPE_SYNC, FILTER_DEFS_FIR_BL19288_N_TAPS, iFirBL19288Coefs}, // FILTER_DEFS_FIR_BL19288_ID + {FIR_TYPE_SYNC, FILTER_DEFS_FIR_BL17696_N_TAPS, iFirBL17696}, // FILTER_DEFS_FIR_BL17696_ID + {FIR_TYPE_OS2, FILTER_DEFS_FIR_UP_N_TAPS, iFirUPCoefs}, // FILTER_DEFS_FIR_UP_ID + {FIR_TYPE_OS2, FILTER_DEFS_FIR_UP4844_N_TAPS, iFirUP4844Coefs}, // FILTER_DEFS_FIR_UP4844_ID + {FIR_TYPE_OS2, FILTER_DEFS_FIR_UPF_N_TAPS, iFirUPFCoefs}, // FILTER_DEFS_FIR_UPF_ID + {FIR_TYPE_OS2, FILTER_DEFS_FIR_UP192176_N_TAPS, iFirUP192176Coefs}, // FILTER_DEFS_FIR_UP192176_ID + {FIR_TYPE_DS2, FILTER_DEFS_FIR_DS_N_TAPS, iFirDSCoefs}, // FILTER_DEFS_FIR_DS_ID + {FIR_TYPE_SYNC, 0, 0} // FILTER_DEFS_FIR_NONE_ID +}; + +// FIR filters coefficients +int iFirBLCoefs[FILTER_DEFS_FIR_BL_N_TAPS] = { + #include FILTER_DEFS_FIR_BL_FILE +}; +int iFirBL9644Coefs[FILTER_DEFS_FIR_BL9644_N_TAPS] = { + #include FILTER_DEFS_FIR_BL9644_FILE +}; +int iFirBL8848[FILTER_DEFS_FIR_BL8848_N_TAPS] = { + #include FILTER_DEFS_FIR_BL8848_FILE +}; +int iFirBLFCoefs[FILTER_DEFS_FIR_BLF_N_TAPS] = { + #include FILTER_DEFS_FIR_BLF_FILE +}; +int iFirBL19288Coefs[FILTER_DEFS_FIR_BL19288_N_TAPS] = { + #include FILTER_DEFS_FIR_BL19288_FILE +}; +int iFirBL17696[FILTER_DEFS_FIR_BL17696_N_TAPS] = { + #include FILTER_DEFS_FIR_BL17696_FILE +}; +int iFirUPCoefs[FILTER_DEFS_FIR_UP_N_TAPS] = { + #include FILTER_DEFS_FIR_UP_FILE +}; +int iFirUP4844Coefs[FILTER_DEFS_FIR_UP4844_N_TAPS] = { + #include FILTER_DEFS_FIR_UP4844_FILE +}; +int iFirUPFCoefs[FILTER_DEFS_FIR_UPF_N_TAPS] = { + #include FILTER_DEFS_FIR_UPF_FILE +}; +int iFirUP192176Coefs[FILTER_DEFS_FIR_UP192176_N_TAPS] = { + #include FILTER_DEFS_FIR_UP192176_FILE +}; +int iFirDSCoefs[FILTER_DEFS_FIR_DS_N_TAPS] = { + #include FILTER_DEFS_FIR_DS_FILE +}; + + + +// ADFIR filters descriptor +ADFIRDescriptor_t sADFirDescriptor = +{ + FILTER_DEFS_ADFIR_PHASE_N_TAPS, FILTER_DEFS_ADFIR_N_PHASES + 2, iADFirCoefs +}; + +// ADFIR filter coefficients +int iADFirPrototypeCoefs[FILTER_DEFS_ADFIR_PROTOTYPE_N_TAPS] = { + #include FILTER_DEFS_ADFIR_PROTOTYPE_FILE +}; +int iADFirCoefs[FILTER_DEFS_ADFIR_N_PHASES + 2][FILTER_DEFS_ADFIR_PHASE_N_TAPS]; + + + + + + +// =========================================================================== +// +// Local Functions prototypes +// +// =========================================================================== + + + + +// =========================================================================== +// +// Functions implementations +// +// =========================================================================== + + + + + diff --git a/tests/asrc_test/model/FilterDefs.h b/tests/asrc_test/model/src/FilterDefs.h similarity index 98% rename from tests/asrc_test/model/FilterDefs.h rename to tests/asrc_test/model/src/FilterDefs.h index eda1db31..c40e3c5d 100644 --- a/tests/asrc_test/model/FilterDefs.h +++ b/tests/asrc_test/model/src/FilterDefs.h @@ -1,117 +1,119 @@ -// =========================================================================== -// =========================================================================== -// -// File: FilterDefs.h -// -// Filters Definitions definition file for the ASRC -// -// Target: MS Windows -// Version: 1.0 -// -// =========================================================================== -// =========================================================================== - -#ifndef _FILTER_DEFS_H_ -#define _FILTER_DEFS_H_ - - // =========================================================================== - // - // Defines - // - // =========================================================================== - - // General defines - // --------------- - // FIR filters IDs - #define FILTER_DEFS_FIR_BL_ID 0 // ID of BL FIR filter - #define FILTER_DEFS_FIR_BL9644_ID 1 // ID of BL9644 FIR filter - #define FILTER_DEFS_FIR_BL8848_ID 2 // ID of BL8848 FIR filter - #define FITLER_DEFS_FIR_BLF_ID 3 // ID of BLF FIR filter - #define FITLER_DEFS_FIR_BL19288_ID 4 // ID of BL19288 FIR filter - #define FILTER_DEFS_FIR_BL17696_ID 5 // ID of BL17696 FIR filter - #define FILTER_DEFS_FIR_UP_ID 6 // ID of UP FIR filter - #define FILTER_DEFS_FIR_UP4844_ID 7 // ID of UP4844 FIR filter - #define FILTER_DEFS_FIR_UPF_ID 8 // ID of UPF FIR filter - #define FILTER_DEFS_FIR_UP192176_ID 9 // ID of UP192176 FIR filter - #define FILTER_DEFS_FIR_DS_ID 10 // ID of DS FIR filter - #define FILTER_DEFS_FIR_NONE_ID 11 // ID of disabled FIR filter - - #define FILTER_DEFS_N_FIR_ID (FILTER_DEFS_FIR_NONE_ID + 1) // Number of FIR IDs corresponding to real FIRs - - // FIR filter number of taps - #define FILTER_DEFS_FIR_BL_N_TAPS 144 // Number of taps of BL FIR filter - #define FILTER_DEFS_FIR_BL9644_N_TAPS 160 // Number of taps of BL9644 FIR filter - #define FILTER_DEFS_FIR_BL8848_N_TAPS 144 // Number of taps of BL8848 filter - #define FILTER_DEFS_FIR_BLF_N_TAPS 96 // Number of taps of BLF FIR filter - #define FILTER_DEFS_FIR_BL19288_N_TAPS 96 // Number of taps of BL19288 filter - #define FILTER_DEFS_FIR_BL17696_N_TAPS 96 // Number of taps of BL17696 filter - #define FILTER_DEFS_FIR_UP_N_TAPS 144 // Number of taps of UP filter - #define FILTER_DEFS_FIR_UP4844_N_TAPS 160 // Number of taps of UP4844 filter - #define FILTER_DEFS_FIR_UPF_N_TAPS 96 // Number of taps of UPF filter - #define FILTER_DEFS_FIR_UP192176_N_TAPS 96 // Number of taps of UP192176 filter - #define FILTER_DEFS_FIR_DS_N_TAPS 32 // Number of taps of DS filter - - #define FILTER_DEFS_FIR_MAX_TAPS_LONG FILTER_DEFS_FIR_BL9644_N_TAPS // Maximum number of taps for long FIR filter - #define FILTER_DEFS_FIR_MAX_TAPS_SHORT FILTER_DEFS_FIR_DS_N_TAPS // Maximum number of taps for short FIR filter - - // FIR filter coefficients files - #define FILTER_DEFS_FIR_BL_FILE "FilterData/BL.dat" // Coefficients file for BL FIR filter - #define FILTER_DEFS_FIR_BL9644_FILE "FilterData/BL9644.dat" // Coefficients file for BL9644 FIR filter - #define FILTER_DEFS_FIR_BL8848_FILE "FilterData/BL8848.dat" // Coefficients file for BL8848 filter - #define FILTER_DEFS_FIR_BLF_FILE "FilterData/BLF.dat" // Coefficients file for BLF FIR filter - #define FILTER_DEFS_FIR_BL19288_FILE "FilterData/BL19288.dat" // Coefficients file for BL19288 FIR filter - #define FILTER_DEFS_FIR_BL17696_FILE "FilterData/BL17696.dat" // Coefficients file for BL17696 filter - #define FILTER_DEFS_FIR_UP_FILE "FilterData/UP.dat" // Coefficients file for UP filter - #define FILTER_DEFS_FIR_UP4844_FILE "FilterData/UP4844.dat" // Coefficients file for UP4844 filter - #define FILTER_DEFS_FIR_UPF_FILE "FilterData/UPF.dat" // Coefficients file for UPF filter - #define FILTER_DEFS_FIR_UP192176_FILE "FilterData/UP192176.dat" // Coefficients file for UP192176 filter - #define FILTER_DEFS_FIR_DS_FILE "FilterData/DS.dat" // Coefficients file for DS filter - - // ADFIR filter number of taps and phases - #define FILTER_DEFS_ADFIR_PROTOTYPE_N_TAPS 1920 // Number of taps of ADFIR filter prototype - - #define FILTER_DEFS_ADFIR_N_PHASES 128 // Number of phases of ADFIR filter - #define FILTER_DEFS_ADFIR_PHASE_N_TAPS ((FILTER_DEFS_ADFIR_PROTOTYPE_N_TAPS / FILTER_DEFS_ADFIR_N_PHASES) + 1) // Number of taps per phase - #define FILTER_DEFS_ADFIR_N_TAPS (FILTER_DEFS_ADFIR_N_PHASES + 2) * FILTER_DEFS_ADFIR_PHASE_N_TAPS // Here 130 phases of 16 taps = 2080 coefs - - // ADFIR filter coefficients files - #define FILTER_DEFS_ADFIR_PROTOTYPE_FILE "FilterData/ADFir.dat" // Coefficients file for the ADFIR filter (prototype) - - - - // =========================================================================== - // - // Variables - // - // =========================================================================== - - // FIR filters descriptors (ordered by ID) - extern FIRDescriptor_t sFirDescriptor[FILTER_DEFS_N_FIR_ID]; - // FIR filters coefficients - extern int iFirBLCoefs[FILTER_DEFS_FIR_BL_N_TAPS]; - extern int iFirBL9644Coefs[FILTER_DEFS_FIR_BL9644_N_TAPS]; - extern int iFirBL8848[FILTER_DEFS_FIR_BL8848_N_TAPS]; - extern int iFirBLFCoefs[FILTER_DEFS_FIR_BLF_N_TAPS]; - extern int iFirBL19288Coefs[FILTER_DEFS_FIR_BL19288_N_TAPS]; - extern int iFirBL17696[FILTER_DEFS_FIR_BL17696_N_TAPS]; - extern int iFirUPCoefs[FILTER_DEFS_FIR_UP_N_TAPS]; - extern int iFirUP4844Coefs[FILTER_DEFS_FIR_UP4844_N_TAPS]; - extern int iFirUPFCoefs[FILTER_DEFS_FIR_UPF_N_TAPS]; - extern int iFirUP192176Coefs[FILTER_DEFS_FIR_UP192176_N_TAPS]; - extern int iFirDSCoefs[FILTER_DEFS_FIR_DS_N_TAPS]; - - // ADFIR filter descriptor - extern ADFIRDescriptor_t sADFirDescriptor; - // ADFIR filters coefficients - extern int iADFirPrototypeCoefs[FILTER_DEFS_ADFIR_PROTOTYPE_N_TAPS]; - extern int iADFirCoefs[FILTER_DEFS_ADFIR_N_PHASES + 2][FILTER_DEFS_ADFIR_PHASE_N_TAPS]; - - - // =========================================================================== - // - // TypeDefs - // - // =========================================================================== - - +// Copyright 2023 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +// =========================================================================== +// =========================================================================== +// +// File: FilterDefs.h +// +// Filters Definitions definition file for the ASRC +// +// Target: MS Windows +// Version: 1.0 +// +// =========================================================================== +// =========================================================================== + +#ifndef _FILTER_DEFS_H_ +#define _FILTER_DEFS_H_ + + // =========================================================================== + // + // Defines + // + // =========================================================================== + + // General defines + // --------------- + // FIR filters IDs + #define FILTER_DEFS_FIR_BL_ID 0 // ID of BL FIR filter + #define FILTER_DEFS_FIR_BL9644_ID 1 // ID of BL9644 FIR filter + #define FILTER_DEFS_FIR_BL8848_ID 2 // ID of BL8848 FIR filter + #define FITLER_DEFS_FIR_BLF_ID 3 // ID of BLF FIR filter + #define FITLER_DEFS_FIR_BL19288_ID 4 // ID of BL19288 FIR filter + #define FILTER_DEFS_FIR_BL17696_ID 5 // ID of BL17696 FIR filter + #define FILTER_DEFS_FIR_UP_ID 6 // ID of UP FIR filter + #define FILTER_DEFS_FIR_UP4844_ID 7 // ID of UP4844 FIR filter + #define FILTER_DEFS_FIR_UPF_ID 8 // ID of UPF FIR filter + #define FILTER_DEFS_FIR_UP192176_ID 9 // ID of UP192176 FIR filter + #define FILTER_DEFS_FIR_DS_ID 10 // ID of DS FIR filter + #define FILTER_DEFS_FIR_NONE_ID 11 // ID of disabled FIR filter + + #define FILTER_DEFS_N_FIR_ID (FILTER_DEFS_FIR_NONE_ID + 1) // Number of FIR IDs corresponding to real FIRs + + // FIR filter number of taps + #define FILTER_DEFS_FIR_BL_N_TAPS 144 // Number of taps of BL FIR filter + #define FILTER_DEFS_FIR_BL9644_N_TAPS 160 // Number of taps of BL9644 FIR filter + #define FILTER_DEFS_FIR_BL8848_N_TAPS 144 // Number of taps of BL8848 filter + #define FILTER_DEFS_FIR_BLF_N_TAPS 96 // Number of taps of BLF FIR filter + #define FILTER_DEFS_FIR_BL19288_N_TAPS 96 // Number of taps of BL19288 filter + #define FILTER_DEFS_FIR_BL17696_N_TAPS 96 // Number of taps of BL17696 filter + #define FILTER_DEFS_FIR_UP_N_TAPS 144 // Number of taps of UP filter + #define FILTER_DEFS_FIR_UP4844_N_TAPS 160 // Number of taps of UP4844 filter + #define FILTER_DEFS_FIR_UPF_N_TAPS 96 // Number of taps of UPF filter + #define FILTER_DEFS_FIR_UP192176_N_TAPS 96 // Number of taps of UP192176 filter + #define FILTER_DEFS_FIR_DS_N_TAPS 32 // Number of taps of DS filter + + #define FILTER_DEFS_FIR_MAX_TAPS_LONG FILTER_DEFS_FIR_BL9644_N_TAPS // Maximum number of taps for long FIR filter + #define FILTER_DEFS_FIR_MAX_TAPS_SHORT FILTER_DEFS_FIR_DS_N_TAPS // Maximum number of taps for short FIR filter + + // FIR filter coefficients files + #define FILTER_DEFS_FIR_BL_FILE "FilterData/BL.dat" // Coefficients file for BL FIR filter + #define FILTER_DEFS_FIR_BL9644_FILE "FilterData/BL9644.dat" // Coefficients file for BL9644 FIR filter + #define FILTER_DEFS_FIR_BL8848_FILE "FilterData/BL8848.dat" // Coefficients file for BL8848 filter + #define FILTER_DEFS_FIR_BLF_FILE "FilterData/BLF.dat" // Coefficients file for BLF FIR filter + #define FILTER_DEFS_FIR_BL19288_FILE "FilterData/BL19288.dat" // Coefficients file for BL19288 FIR filter + #define FILTER_DEFS_FIR_BL17696_FILE "FilterData/BL17696.dat" // Coefficients file for BL17696 filter + #define FILTER_DEFS_FIR_UP_FILE "FilterData/UP.dat" // Coefficients file for UP filter + #define FILTER_DEFS_FIR_UP4844_FILE "FilterData/UP4844.dat" // Coefficients file for UP4844 filter + #define FILTER_DEFS_FIR_UPF_FILE "FilterData/UPF.dat" // Coefficients file for UPF filter + #define FILTER_DEFS_FIR_UP192176_FILE "FilterData/UP192176.dat" // Coefficients file for UP192176 filter + #define FILTER_DEFS_FIR_DS_FILE "FilterData/DS.dat" // Coefficients file for DS filter + + // ADFIR filter number of taps and phases + #define FILTER_DEFS_ADFIR_PROTOTYPE_N_TAPS 1920 // Number of taps of ADFIR filter prototype + + #define FILTER_DEFS_ADFIR_N_PHASES 128 // Number of phases of ADFIR filter + #define FILTER_DEFS_ADFIR_PHASE_N_TAPS ((FILTER_DEFS_ADFIR_PROTOTYPE_N_TAPS / FILTER_DEFS_ADFIR_N_PHASES) + 1) // Number of taps per phase + #define FILTER_DEFS_ADFIR_N_TAPS (FILTER_DEFS_ADFIR_N_PHASES + 2) * FILTER_DEFS_ADFIR_PHASE_N_TAPS // Here 130 phases of 16 taps = 2080 coefs + + // ADFIR filter coefficients files + #define FILTER_DEFS_ADFIR_PROTOTYPE_FILE "FilterData/ADFir.dat" // Coefficients file for the ADFIR filter (prototype) + + + + // =========================================================================== + // + // Variables + // + // =========================================================================== + + // FIR filters descriptors (ordered by ID) + extern FIRDescriptor_t sFirDescriptor[FILTER_DEFS_N_FIR_ID]; + // FIR filters coefficients + extern int iFirBLCoefs[FILTER_DEFS_FIR_BL_N_TAPS]; + extern int iFirBL9644Coefs[FILTER_DEFS_FIR_BL9644_N_TAPS]; + extern int iFirBL8848[FILTER_DEFS_FIR_BL8848_N_TAPS]; + extern int iFirBLFCoefs[FILTER_DEFS_FIR_BLF_N_TAPS]; + extern int iFirBL19288Coefs[FILTER_DEFS_FIR_BL19288_N_TAPS]; + extern int iFirBL17696[FILTER_DEFS_FIR_BL17696_N_TAPS]; + extern int iFirUPCoefs[FILTER_DEFS_FIR_UP_N_TAPS]; + extern int iFirUP4844Coefs[FILTER_DEFS_FIR_UP4844_N_TAPS]; + extern int iFirUPFCoefs[FILTER_DEFS_FIR_UPF_N_TAPS]; + extern int iFirUP192176Coefs[FILTER_DEFS_FIR_UP192176_N_TAPS]; + extern int iFirDSCoefs[FILTER_DEFS_FIR_DS_N_TAPS]; + + // ADFIR filter descriptor + extern ADFIRDescriptor_t sADFirDescriptor; + // ADFIR filters coefficients + extern int iADFirPrototypeCoefs[FILTER_DEFS_ADFIR_PROTOTYPE_N_TAPS]; + extern int iADFirCoefs[FILTER_DEFS_ADFIR_N_PHASES + 2][FILTER_DEFS_ADFIR_PHASE_N_TAPS]; + + + // =========================================================================== + // + // TypeDefs + // + // =========================================================================== + + #endif // _FILTER_DEFS_H_ \ No newline at end of file diff --git a/tests/asrc_test/model/IntArithmetic.c b/tests/asrc_test/model/src/IntArithmetic.c similarity index 96% rename from tests/asrc_test/model/IntArithmetic.c rename to tests/asrc_test/model/src/IntArithmetic.c index 69df421d..fa2764a2 100644 --- a/tests/asrc_test/model/IntArithmetic.c +++ b/tests/asrc_test/model/src/IntArithmetic.c @@ -1,127 +1,129 @@ -// =========================================================================== -// =========================================================================== -// -// File: IntArithmetic.c -// -// Integer arithmetic implementation file for the ASRC -// -// Target: MS Windows -// Version: 1.0 -// -// =========================================================================== -// =========================================================================== - - -// =========================================================================== -// -// Includes -// -// =========================================================================== -#include -#include -#include -#include - -// Integer arithmetic includes -#include "IntArithmetic.h" - -// =========================================================================== -// -// Defines -// -// =========================================================================== - - - - -// =========================================================================== -// -// Variables -// -// =========================================================================== - - - -// =========================================================================== -// -// Local Functions prototypes -// -// =========================================================================== - - - - -// =========================================================================== -// -// Functions implementations -// -// =========================================================================== - -// ==================================================================== // -// Function: MACC // -// Description: 32i x 32i -> 64i Multiply-Accumulate // -// ==================================================================== // -void MACC(__int64* plAcc, int ix, int iy) -{ - *plAcc += (__int64)ix * (__int64)iy; -} - -// ==================================================================== // -// Function: LMUL // -// Description: 32i x 32i +32i + 32i -> 64i Multiply (and add) // -// ==================================================================== // -void LMUL(__int64* plAcc, int ix, int iy, int ic, int id) -{ - *plAcc = (__int64)ix * (__int64)iy; - *plAcc += (__int64)ic; - *plAcc += (__int64)id; -} - - -// ==================================================================== // -// Function: LATS30 // -// Description: Saturate 64i to 2^62-1, -2^62 // -// ==================================================================== // -void LSAT30(__int64 *plx) -{ - if(*plx > MAX_VAL64_30) - *plx = MAX_VAL64_30; - if(*plx < MIN_VAL64_30) - *plx = MIN_VAL64_30; -} - - -// ==================================================================== // -// Function: EXT30 // -// Description: Extracts bits [62-31] into 32i // -// ==================================================================== // -void EXT30(int *pr, __int64 lx) -{ - *pr = (int)(lx>>31); -} - - -// ==================================================================== // -// Function: LATS29 // -// Description: Saturate 64i to 2^61-1, -2^61 // -// ==================================================================== // -void LSAT29(__int64 *plx) -{ - if(*plx > MAX_VAL64_29) - *plx = MAX_VAL64_29; - if(*plx < MIN_VAL64_29) - *plx = MIN_VAL64_29; -} - - -// ==================================================================== // -// Function: EXT29 // -// Description: Extracts bits [61-30] into 32i // -// ==================================================================== // -void EXT29(int *pr, __int64 lx) -{ - *pr = (int)(lx>>30); -} - - - +// Copyright 2023 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +// =========================================================================== +// =========================================================================== +// +// File: IntArithmetic.c +// +// Integer arithmetic implementation file for the ASRC +// +// Target: MS Windows +// Version: 1.0 +// +// =========================================================================== +// =========================================================================== + + +// =========================================================================== +// +// Includes +// +// =========================================================================== +#include +#include +#include +#include + +// Integer arithmetic includes +#include "IntArithmetic.h" + +// =========================================================================== +// +// Defines +// +// =========================================================================== + + + + +// =========================================================================== +// +// Variables +// +// =========================================================================== + + + +// =========================================================================== +// +// Local Functions prototypes +// +// =========================================================================== + + + + +// =========================================================================== +// +// Functions implementations +// +// =========================================================================== + +// ==================================================================== // +// Function: MACC // +// Description: 32i x 32i -> 64i Multiply-Accumulate // +// ==================================================================== // +void MACC(__int64* plAcc, int ix, int iy) +{ + *plAcc += (__int64)ix * (__int64)iy; +} + +// ==================================================================== // +// Function: LMUL // +// Description: 32i x 32i +32i + 32i -> 64i Multiply (and add) // +// ==================================================================== // +void LMUL(__int64* plAcc, int ix, int iy, int ic, int id) +{ + *plAcc = (__int64)ix * (__int64)iy; + *plAcc += (__int64)ic; + *plAcc += (__int64)id; +} + + +// ==================================================================== // +// Function: LATS30 // +// Description: Saturate 64i to 2^62-1, -2^62 // +// ==================================================================== // +void LSAT30(__int64 *plx) +{ + if(*plx > MAX_VAL64_30) + *plx = MAX_VAL64_30; + if(*plx < MIN_VAL64_30) + *plx = MIN_VAL64_30; +} + + +// ==================================================================== // +// Function: EXT30 // +// Description: Extracts bits [62-31] into 32i // +// ==================================================================== // +void EXT30(int *pr, __int64 lx) +{ + *pr = (int)(lx>>31); +} + + +// ==================================================================== // +// Function: LATS29 // +// Description: Saturate 64i to 2^61-1, -2^61 // +// ==================================================================== // +void LSAT29(__int64 *plx) +{ + if(*plx > MAX_VAL64_29) + *plx = MAX_VAL64_29; + if(*plx < MIN_VAL64_29) + *plx = MIN_VAL64_29; +} + + +// ==================================================================== // +// Function: EXT29 // +// Description: Extracts bits [61-30] into 32i // +// ==================================================================== // +void EXT29(int *pr, __int64 lx) +{ + *pr = (int)(lx>>30); +} + + + diff --git a/tests/asrc_test/model/IntArithmetic.h b/tests/asrc_test/model/src/IntArithmetic.h similarity index 96% rename from tests/asrc_test/model/IntArithmetic.h rename to tests/asrc_test/model/src/IntArithmetic.h index 248b07d7..70a6c402 100644 --- a/tests/asrc_test/model/IntArithmetic.h +++ b/tests/asrc_test/model/src/IntArithmetic.h @@ -1,89 +1,91 @@ -// =========================================================================== -// =========================================================================== -// -// File: IntArithmetic.h -// -// Integer arithmetic definition file for the ASRC -// -// Target: MS Windows -// Version: 1.0 -// -// =========================================================================== -// =========================================================================== - -#ifndef _INT_ARITHMETIC_H_ -#define _INT_ARITHMETIC_H_ - - // =========================================================================== - // - // Defines - // - // =========================================================================== - - - - // =========================================================================== - // - // Variables - // - // =========================================================================== - - - // =========================================================================== - // - // TypeDefs - // - // =========================================================================== - // To avoid C type definitions when including this file from assembler - #ifndef INCLUDE_FROM_ASM - - - // =========================================================================== - // - // Function prototypes - // - // =========================================================================== - - // ==================================================================== // - // Function: MACC // - // Description: 32i x 32i -> 64i Multiply-Accumulate // - // ==================================================================== // - void MACC(__int64* plAcc, int ix, int iy); - - // ==================================================================== // - // Function: LMUL // - // Description: 32i x 32i +32i + 32i -> 64i Multiply (and add) // - // ==================================================================== // - void LMUL(__int64* plAcc, int ix, int iy, int ic, int id); - - // ==================================================================== // - // Function: LATS30 // - // Description: Saturate 64i to 2^62-1, -2^62 // - // ==================================================================== // - #define MAX_VAL64_30 (__int64)0x3FFFFFFFFFFFFFFF - #define MIN_VAL64_30 (__int64)0xC000000000000000 - void LSAT30(__int64 *plx); - - // ==================================================================== // - // Function: EXT30 // - // Description: Extracts bits [62-31] into 32i // - // ==================================================================== // - void EXT30(int *pr, __int64 lx); - - // ==================================================================== // - // Function: LATS29 // - // Description: Saturate 64i to 2^61-1, -2^61 // - // ==================================================================== // - #define MAX_VAL64_29 (__int64)0x1FFFFFFFFFFFFFFF - #define MIN_VAL64_29 (__int64)0xE000000000000000 - void LSAT29(__int64 *plx); - - // ==================================================================== // - // Function: EXT29 // - // Description: Extracts bits [61-30] into 32i // - // ==================================================================== // - void EXT29(int *pr, __int64 lx); - - #endif // nINCLUDE_FROM_ASM - +// Copyright 2023 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +// =========================================================================== +// =========================================================================== +// +// File: IntArithmetic.h +// +// Integer arithmetic definition file for the ASRC +// +// Target: MS Windows +// Version: 1.0 +// +// =========================================================================== +// =========================================================================== + +#ifndef _INT_ARITHMETIC_H_ +#define _INT_ARITHMETIC_H_ + + // =========================================================================== + // + // Defines + // + // =========================================================================== + + + + // =========================================================================== + // + // Variables + // + // =========================================================================== + + + // =========================================================================== + // + // TypeDefs + // + // =========================================================================== + // To avoid C type definitions when including this file from assembler + #ifndef INCLUDE_FROM_ASM + + + // =========================================================================== + // + // Function prototypes + // + // =========================================================================== + + // ==================================================================== // + // Function: MACC // + // Description: 32i x 32i -> 64i Multiply-Accumulate // + // ==================================================================== // + void MACC(__int64* plAcc, int ix, int iy); + + // ==================================================================== // + // Function: LMUL // + // Description: 32i x 32i +32i + 32i -> 64i Multiply (and add) // + // ==================================================================== // + void LMUL(__int64* plAcc, int ix, int iy, int ic, int id); + + // ==================================================================== // + // Function: LATS30 // + // Description: Saturate 64i to 2^62-1, -2^62 // + // ==================================================================== // + #define MAX_VAL64_30 (__int64)0x3FFFFFFFFFFFFFFF + #define MIN_VAL64_30 (__int64)0xC000000000000000 + void LSAT30(__int64 *plx); + + // ==================================================================== // + // Function: EXT30 // + // Description: Extracts bits [62-31] into 32i // + // ==================================================================== // + void EXT30(int *pr, __int64 lx); + + // ==================================================================== // + // Function: LATS29 // + // Description: Saturate 64i to 2^61-1, -2^61 // + // ==================================================================== // + #define MAX_VAL64_29 (__int64)0x1FFFFFFFFFFFFFFF + #define MIN_VAL64_29 (__int64)0xE000000000000000 + void LSAT29(__int64 *plx); + + // ==================================================================== // + // Function: EXT29 // + // Description: Extracts bits [61-30] into 32i // + // ==================================================================== // + void EXT29(int *pr, __int64 lx); + + #endif // nINCLUDE_FROM_ASM + #endif // _INT_ARITHMETIC_H_ \ No newline at end of file diff --git a/tests/test_lib_checks.py b/tests/test_lib_checks.py index 02413ad1..07787342 100644 --- a/tests/test_lib_checks.py +++ b/tests/test_lib_checks.py @@ -36,9 +36,9 @@ def test_version_matches(): """ Check docs version vs changelog """ - with open(Path(__file__).resolve().parent / "../settings.json") as sj: + with open(Path(__file__).resolve().parent / "../settings.yml") as sy: with open(Path(__file__).resolve().parent / "../CHANGELOG.rst") as cl: re_string = r"([0-9]*)\.([0-9]*)\.([0-9]*)" - sj_ver = re.search(re_string, sj.readlines()[3]).groups() + sy_ver = re.search(re_string, sy.readlines()[3]).groups() cl_ver = re.search(re_string, cl.readlines()[3]).groups() - assert sj_ver == cl_ver, f"Version match issue between settings.json and CHANGELOG.rst: {sj_ver} {cl_ver}" + assert sy_ver == cl_ver, f"Version match issue between settings.yml and CHANGELOG.rst: {sy_ver} {cl_ver}"