90 Commits

Author SHA1 Message Date
Ross Owen
cb379f5bfb 'Release: 3.4.0' 2023-03-15 13:02:04 +00:00
Ross Owen
e2c36a9a95 xpd: Cleaned up whitespace 2023-03-15 13:00:39 +00:00
Ross Owen
7e3ae59acc Changelog update and version bump 3.3.1 -> 3.4.0 2023-03-15 12:28:39 +00:00
Ross Owen
f1f453921b Merge pull request #320 from xross/fix/mixer_lock
Improve mixer control protocol to avoid deadlock
2023-03-14 12:52:44 +00:00
Ross Owen
7703fc1a7d Fixed build warning when mixer not enabled 2023-03-14 11:49:08 +00:00
Ross Owen
53a65344fc Improve mixer control protocol to avoid deadlock 2023-03-14 11:48:54 +00:00
Ross Owen
3b2814f8cb Merge pull request #319 from xross/fix/spdif_output_opts 2023-03-13 16:50:40 +00:00
Ross Owen
55a62cf589 - Fixed build issue with !FAST_MIXER
- Fixed issue with !OUT_VOLUME_AFTER_MIX not being respected
2023-03-13 15:50:45 +00:00
Ross Owen
4a84c3e1ec OUT_VOLUME_IN_MIXER enabled by default 2023-03-13 15:49:54 +00:00
Ross Owen
b17f585004 Decouple optimisation for when output slot size = 4 (note, trades off code size) 2023-03-13 14:33:22 +00:00
Ross Owen
57593bfea3 Fixed issue with STREAM_FORMAT_OUTPUT_RESOLUTION_32_BIT_USED be set incorrectly 2023-03-13 14:31:53 +00:00
Ross Owen
8dc77090bf Merge pull request #318 from xross/develop
Reinstate check for current samplerate before changing
2023-03-09 16:42:26 +00:00
Ross Owen
9c20fab216 Updated copyright comment 2023-03-09 15:32:15 +00:00
Ross Owen
cf1940245f Return value of XUD_DoSetRequestStatus in Samp freq change 2023-03-09 15:24:21 +00:00
Ross Owen
837b648bbc Reinstate check for current samplerate before changing 2023-03-09 15:22:58 +00:00
Ross Owen
c1159143ea Fastmix.S now uses defines from xua.h 2023-03-09 14:56:54 +00:00
Ross Owen
2964861b70 Fixed xua.h including in asm files 2023-03-09 14:51:32 +00:00
Ross Owen
208491fe51 Added mixer control unit tests (#316)
* Added test_mixer_routing_output_ctrl
* Added test_mixer_routing_input_ctrl 
* Some minor mixer test and code improvements
* Update lib_xud dep version requirement
2023-03-08 10:53:33 +00:00
TDW89
3fe4593b52 Mixer Host App: Multi device error handeling & device ID agnostic (#315)
Removed device product ID requirement from macOS and added error messages when multiple devices are connected
2023-03-02 16:01:17 +00:00
TDW89
49a116c705 Minor updates (#314)
* SDK location can now set in makefile
* Mixer requirements added to readme
* Uncommented previously broken mixer example from docs as this functionality is now working
* Fixed windows build & runtime info in README
2023-03-01 10:59:00 +00:00
Ross Owen
eee5b474a0 Merge pull request #312 from TDW89/unify_win_osx_code
Unify win osx code
2023-02-20 11:56:04 +00:00
Ross Owen
4655a07542 Merge pull request #313 from xross/develop 2023-02-17 14:04:14 +00:00
Ross Owen
c578bb92d5 Fix build issue with volume processing in mixer (missing xc_ptr func) 2023-02-17 12:10:51 +00:00
Tom Williams
d5a614df55 removed old usb_mixer files 2023-02-15 14:28:27 +00:00
Tom Williams
495140ab8d adding OSX\usb_mixer.cpp changes from mixer tests branch to the combined usb_mixer.cpp file 2023-02-15 13:12:28 +00:00
Tom Williams
f53c1bab09 fixed merge conflict in OSX makefile 2023-02-15 11:47:28 +00:00
Ross Owen
0c6d947e67 Merge pull request #307 from xross/feature/mixer_tests
Feature/mixer tests
2023-02-09 10:51:08 +00:00
Tom Williams
ee271e3769 fixed typos and build issues 2023-02-08 17:49:58 +00:00
Ross Owen
950beb55cb - Fixed ifndef check
- Fixed mix map update
2023-02-08 17:06:08 +00:00
Tom Williams
ca3276792a initial unified version of usb_mixer.cpp 2023-02-08 16:58:18 +00:00
Ross Owen
e26b934233 Conflicted merge from upstream/develop 2023-02-08 13:58:25 +00:00
Ross Owen
51629dba24 Merge pull request #310 from TDW89/host_usb_win
Host usb win pull_v2
2023-02-08 13:56:46 +00:00
Ross Owen
c5e944d73d Use of storeShort function in mixer weight read and comment updates 2023-02-08 13:18:28 +00:00
Ross Owen
22a3d5e043 Added array to size to extern to fix sizeof usage error 2023-02-08 12:20:42 +00:00
Ross Owen
58f691078d Added range checking when getting/setting mixer weights 2023-02-08 12:04:38 +00:00
Ross Owen
f80d7647e0 - Make usage (of lack of) of CS when setting/getting mixer weights more clear in the implementation
- Fix typo in IN_VOLUME_IN_MIXER define
2023-02-08 11:59:13 +00:00
Tom Williams
fe697929bc merge testing branch and osx updates into branch 2023-02-08 11:59:07 +00:00
Tom Williams
b265ccd8bf Merge remote-tracking branch 'xmos/develop' into develop
bringing personal fork up to date
2023-02-08 10:39:25 +00:00
Ross Owen
6c2e7e3042 Fixed issue setting mix maps (#308) 2023-02-08 10:02:32 +00:00
Ross Owen
15ca5ec281 Fixed unused var warning 2023-02-07 16:35:38 +00:00
Ross Owen
71aa64425d Added missing bounds checking when setting host and device maps in the mixer 2023-02-07 12:14:19 +00:00
Ross Owen
9080990234 Fixed typo in define check 2023-02-06 23:44:20 +00:00
Ross Owen
6d8cf9913f Increase timeout cycles for test_mixer_routing_output 2023-02-06 23:42:33 +00:00
Ross Owen
60040de58f Further build issue fixes and copyright comments 2023-02-06 21:08:47 +00:00
Ross Owen
27a59ab3bc - Fixed MIXER define usage in main
- Fixed unsafe pointer usage when MAX_MIX_COUNT=0
- Fixed syntax error building mixer when no features enabled relating to empty switch statement
2023-02-06 21:05:48 +00:00
Ross Owen
317e27e421 - Rationalised MIXER defines and usage
- Further removal of xc_ptr type usage in mixer in favour of native xc pointers
2023-02-06 20:28:29 +00:00
Ross Owen
035c20e01c Fixed build issue in mixer when FAST_MIXER not used 2023-02-06 18:14:48 +00:00
Ross Owen
ef97d667de Further moving of mixer from xc_ptr type to XC pointers 2023-02-06 18:09:33 +00:00
Ross Owen
0e07dc29bc Fix issue in mixer host app when retrieving mixer input strings. Also firmed up some usage error checking. 2023-02-03 11:09:43 +00:00
Ross Owen
43f77c177d Small updates to mixer control README 2023-02-03 11:08:23 +00:00
Ross Owen
6754f812c9 Fixed issues with setting mix map in mixer host app 2023-02-03 11:08:06 +00:00
Ross Owen
fc732b8512 Updated changelog 2023-02-03 11:06:40 +00:00
Ross Owen
39ed235476 Removed old module_description file 2023-02-03 11:06:23 +00:00
Ross Owen
0d7224bd6d Added -arch for to mixer host app makefile 2023-02-03 11:06:13 +00:00
Ross Owen
fd4dfd40a9 Tidy .gitignore 2023-02-03 11:05:21 +00:00
Ross Owen
3d50c96595 - Removed dead XTA pragmas and code from mixer
- Added some more asserts to mixer
2023-02-03 10:59:20 +00:00
Tom Williams
379e8eb54c changed jenkins file to use sh rather than runVS 2023-02-01 16:29:05 +00:00
Tom Williams
63763cf4f5 fixed progect file & typo in jenkinsfile 2023-02-01 15:50:42 +00:00
Tom Williams
b18c34fb0f OSX makefile changed to work on M1, VS project file changed to 2019 (toolset 142) and Jenkins file now builds and archives the binaries 2023-02-01 14:11:47 +00:00
Tom Williams
64d65afeaf project file fixes 2023-02-01 13:43:53 +00:00
Tom Williams
7a47d70229 Revert "added path variable to link project to sdk"
This reverts commit cffd35d146.
2023-02-01 13:40:54 +00:00
Ross Owen
ce8e5a6dbb Merge branch 'develop' into feature/mixer_tests 2023-02-01 12:01:47 +00:00
Ross Owen
bbed806aab Merge remote-tracking branch 'upstream' into develop 2023-02-01 12:01:21 +00:00
Ross Owen
9af31b8c70 Merge pull request #305 from TDW89/host_usb_mixer_control
host_usb_mixer_control_OSX_support
2023-02-01 12:00:45 +00:00
Ross Owen
73955c1a4c - Added mixer related defines to reduce use of magic numbers
- Increases debug output when DEBUG flag set
- Removed some dead code
- Increased alignment of asm mixer functions
- Removed some usages of “xc_ptr” type in favour of native pointers in XC
- Added some asserts to mixer
- Added test_mixer_routing_input
- Moved test_mixer_routing_output to use shared code
2023-02-01 11:54:48 +00:00
Tom Williams
cffd35d146 added path variable to link project to sdk 2023-01-25 16:31:05 +00:00
Tom Williams
ab535e0fb3 removed temporary debug code 2023-01-25 11:07:49 +00:00
Tom Williams
17b039dce8 licence updates 2023-01-25 10:48:44 +00:00
Tom Williams
7a0d0e1f97 Initial windows commit 2023-01-25 10:23:57 +00:00
Tom Williams
2f31260612 updated copyright headers 2023-01-18 16:51:47 +00:00
Tom Williams
9922190450 removed versioning and license files as they are now part of the top level. added the help option to the readme 2023-01-18 16:01:08 +00:00
Ross Owen
0ce91bec90 Copyright comment 2023-01-17 12:12:46 +00:00
Ross Owen
2404eaf35f Remove extern, mark static inline 2023-01-17 11:53:44 +00:00
Ross Owen
8966ad1bb9 Add external linkage to inline DoSampleTransfer 2023-01-17 11:39:22 +00:00
Ross Owen
513761ef5b Moved DoSampleTransfer to a header file so it can still be inlined (but also used in unit tests). Resolves fails in test_i2s_loopback 2023-01-17 11:33:52 +00:00
Ross Owen
da7c45500d - Added test_mixer_routing_output
- Various buffers no longer marked static to allow for easier unit testing 
- Added some comments and removed some dead code from the implementation
- Moved mixer control comms to functions for unit test convenience
2023-01-16 17:28:04 +00:00
Ross Owen
395c88cb22 - Removed unused mixer variable.
- Use of mixer defines rather than fixed values
2023-01-10 11:27:37 +00:00
Tom Williams
94e58edfaf documentation clarifications and some spelling / grammer fixes 2022-12-21 17:22:39 +00:00
Tom Williams
9f00f9159a improvements to documentation 2022-12-21 15:07:14 +00:00
Tom Williams
785a857ca8 moved mixer control to correct path 2022-12-01 09:37:20 +00:00
Tom Williams
3130088c91 Initial commit for host_usb_mixer_control 2022-11-23 17:53:47 +00:00
Ross Owen
c51ee0c460 Removed extra whitespace from example makefiles 2022-11-21 20:05:49 +00:00
danielpieczko
17ed636a74 Merge pull request #303 from danielpieczko/no_usb_chans_in
Avoid calling SetupZerosSendBuffer when there are no IN eps
2022-11-16 10:06:03 +00:00
Daniel Pieczko
0db1b08948 Add changelog entry 2022-11-11 08:56:20 +00:00
Daniel Pieczko
9c460f753f Avoid calling SetupZerosSendBuffer when there are no IN eps 2022-11-10 16:44:34 +00:00
danielpieczko
6a9537fb69 Merge pull request #302 from danielpieczko/develop
Revert TDM ADC clocking change from commit a1946f3
2022-11-07 14:29:16 +00:00
Daniel Pieczko
abfa3a2011 Revert TDM ADC clocking change from commit a1946f3 2022-11-07 08:59:56 +00:00
xross
a1946f340a Remove TDM ADC clocking on neg edge 2022-11-01 19:24:25 +00:00
xross
28be17282f 'Release: 3.3.1' 2022-10-26 18:33:46 +01:00
Ross Owen
a1082b1dfd Documentation
Documentation updates and version bump
2022-10-26 17:48:51 +01:00
107 changed files with 6471 additions and 936 deletions

56
.gitignore vendored
View File

@@ -1,29 +1,25 @@
*.log # XMOS bin files
*.dSYM
*/.build_*/*
*/bin/*
*.o
*.xe *.xe
*.vcd *.bin
*.swo */bin/*
# XMOS temp files
.build*
*.a
_build*
*.i
*.s *.s
*.xi *.xi
*.i *.o
*.bin */.build_*/*
*~
*.a # Temp files
*.swp
*.*~
*.pyc
.build*
.DS_Store .DS_Store
test_results.csv *.*~
_build* *.swp
**/.venv/** *.swn
**/.vscode/** *~
**.egg-info *.swo
*.pdf
*tests/logs/*
# waf build files # waf build files
.lock-waf_* .lock-waf_*
@@ -36,3 +32,19 @@ xscope.xmt
# Traces # Traces
*.gtkw *.gtkw
*.vcd *.vcd
# Host binaries
host_usb_mixer_control/xmos_mixer
# Documentation build
*.pdf
# Misc
*.log
*.dSYM
*.vcd
*.pyc
**/.venv/**
**/.vscode/**
**.egg-info
*tests/logs/*

View File

@@ -1,6 +1,34 @@
lib_xua Change Log lib_xua Change Log
================== ==================
3.4.0
-----
* ADDED: Unit tests for mixer functionality
* ADDED: Host mixer control applications (for Win/macOS)
* CHANGED: Small tidies to mixer implementation
* CHANGED: Improved mixer control channel communication protocol to avoid
deadlock situations
* CHANGED: By default, output volume processing occurs in mixer task, if
present. Previously occurred in decouple task
* CHANGED: Some optimisations in sample transfer from decouple task
* FIXED: Exception on startup when USB input disabled
* FIXED: Full 32bit volume processing only applied when required
* FIXED: Setting OUT_VOLUME_AFTER_MIX to zero now has the expected effect
* Changes to dependencies:
- lib_xud: 2.2.1 -> 2.2.2
3.3.1
-----
* CHANGED: Documentation updates
* Changes to dependencies:
- lib_spdif: 4.1.0 -> 4.2.1
3.3.0 3.3.0
----- -----

16
Jenkinsfile vendored
View File

@@ -1,4 +1,4 @@
@Library('xmos_jenkins_shared_library@v0.18.0') _ @Library('xmos_jenkins_shared_library@v0.21.0') _
getApproval() getApproval()
@@ -24,7 +24,7 @@ pipeline {
} }
stage('Library checks') { stage('Library checks') {
steps { steps {
xcoreLibraryChecks("${REPO}") xcoreLibraryChecks("${REPO}", false)
} }
} }
stage('Testing') { stage('Testing') {
@@ -106,6 +106,12 @@ pipeline {
dir("${REPO}/${REPO}/host/xmosdfu") { dir("${REPO}/${REPO}/host/xmosdfu") {
sh 'make -f Makefile.OSX64' sh 'make -f Makefile.OSX64'
} }
dir("${REPO}/host_usb_mixer_control") {
sh 'make -f Makefile.OSX'
sh 'mkdir OSX/x86'
sh 'mv xmos_mixer OSX/x86/xmos_mixer'
archiveArtifacts artifacts: "OSX/x86/xmos_mixer", fingerprint: true
}
} }
post { post {
cleanup { cleanup {
@@ -141,6 +147,12 @@ pipeline {
dir("${REPO}/host/xmosdfu") { dir("${REPO}/host/xmosdfu") {
runVS('nmake /f Makefile.Win32') runVS('nmake /f Makefile.Win32')
} }
dir("host_usb_mixer_control") {
runVS('msbuild host_usb_mixer_control.vcxproj /property:Configuration=Release /property:Platform=x64')
sh 'mkdir Win/x64'
sh 'mv bin/Release/x64/host_usb_mixer_control.exe Win/x64/xmos_mixer.exe'
archiveArtifacts artifacts: "Win/x64/xmos_mixer.exe", fingerprint: true
}
} }
} }
post { post {

View File

@@ -1,20 +1,21 @@
lib_xua lib_xua
======= #######
:Latest release: 3.3.0 :Version: 3.4.0
:Vendor: XMOS
:Scope: General Use :Scope: General Use
Summary Summary
------- *******
lib_xua contains shared components for use in the XMOS USB Audio (XUA) Reference Designs. lib_xua contains shared components for use in the XMOS USB Audio (XUA) Reference Designs.
These components enable the development of USB Audio devices on the XMOS xCORE architecture. These components enable the development of USB Audio devices on the XMOS xCORE architecture.
Features Features
~~~~~~~~ ========
Key features of the various components in this repository are as follows Key features of the various components in this repository are as follows
@@ -56,7 +57,7 @@ Note, not all features may be supported at all sample frequencies, simultaneousl
Some features may also require specific host driver support. Some features may also require specific host driver support.
Host System Requirements Host System Requirements
~~~~~~~~~~~~~~~~~~~~~~~~ ========================
USB Audio devices built using `lib_xua` have the following host system requirements. USB Audio devices built using `lib_xua` have the following host system requirements.
@@ -69,27 +70,33 @@ USB Audio devices built using `lib_xua` have the following host system requireme
Older versions of Windows are not guaranteed to operate as expected. Devices are also expected to operate with various Linux distributions including mobile variants. Older versions of Windows are not guaranteed to operate as expected. Devices are also expected to operate with various Linux distributions including mobile variants.
Related Application Notes Related Application Notes
~~~~~~~~~~~~~~~~~~~~~~~~~ =========================
The following application notes use this library: The following application notes use this library:
* AN000246 - Simple USB Audio Device using lib_xua * AN000246 - Simple USB Audio Device using lib_xua
* AN000247 - Using lib_xua with lib_spdif (transmit) * AN000247 - Using lib_xua with lib_spdif (transmit)
* AN000248 - Using lib_xua with lib_mic_array * AN000248 - Using lib_xua with lib_mic_array
Required software (dependencies) Required Software (dependencies)
================================ ================================
* lib_locks (git@github.com:xmos/lib_locks.git) * lib_locks (www.github.com/xmos/lib_locks)
* lib_logging (git@github.com:xmos/lib_logging.git) * lib_logging (www.github.com/xmos/lib_logging)
* lib_mic_array (git@github.com:xmos/lib_mic_array.git) * lib_mic_array (www.github.com/xmos/lib_mic_array)
* lib_xassert (git@github.com:xmos/lib_xassert.git) * lib_xassert (www.github.com/xmos/lib_xassert)
* lib_dsp (git@github.com:xmos/lib_dsp) * lib_dsp (www.github.com/xmos/lib_dsp)
* lib_i2c (git@github.com:xmos/lib_i2c.git) * lib_spdif (www.github.com/xmos/lib_spdif)
* lib_i2s (git@github.com:xmos/lib_i2s.git) * lib_xud (www.github.com/xmos/lib_xud)
* lib_gpio (git@github.com:xmos/lib_gpio.git) * lib_adat (www.github.com/xmos/lib_adat)
* lib_mic_array_board_support (git@github.com:xmos/lib_mic_array_board_support.git)
* lib_spdif (git@github.com:xmos/lib_spdif.git) Documentation
* lib_xud (git@github.com:xmos/lib_xud.git) =============
* lib_adat (git@github.com:xmos/lib_adat)
You can find the documentation for this software in the /doc directory of the package.
Support
=======
This package is supported by XMOS Ltd. Issues can be raised against the software at: http://www.xmos.com/support

View File

@@ -21,4 +21,3 @@ USED_MODULES = lib_xua lib_xud lib_i2c
XMOS_MAKE_PATH ?= ../.. XMOS_MAKE_PATH ?= ../..
include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common

View File

@@ -20,4 +20,3 @@ USED_MODULES = lib_xua lib_xud lib_spdif
XMOS_MAKE_PATH ?= ../.. XMOS_MAKE_PATH ?= ../..
include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common

View File

@@ -19,4 +19,3 @@ USED_MODULES = lib_xua lib_xud lib_mic_array
XMOS_MAKE_PATH ?= ../.. XMOS_MAKE_PATH ?= ../..
include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common

View File

@@ -0,0 +1,10 @@
all:
@echo =======================================================
@echo Build complete [module only - cannot be run on its own]
@echo =======================================================
clean:
@echo =======================================================
@echo Build clean [module only - cannot be run on its own]
@echo =======================================================

View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>host_usb_mixer_control</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
<triggers>clean,full,incremental,</triggers>
<arguments>
<dictionary>
<key>?name?</key>
<value></value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.append_environment</key>
<value>true</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.autoBuildTarget</key>
<value>all</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.buildArguments</key>
<value></value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.buildCommand</key>
<value>xmake</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.cleanBuildTarget</key>
<value>clean</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.contents</key>
<value>org.eclipse.cdt.make.core.activeConfigSettings</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.enableAutoBuild</key>
<value>false</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.enableCleanBuild</key>
<value>true</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.enableFullBuild</key>
<value>true</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.fullBuildTarget</key>
<value>all</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.stopOnError</key>
<value>true</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.useDefaultBuildCmd</key>
<value>true</value>
</dictionary>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
<nature>org.eclipse.cdt.core.cnature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,2 @@
all:
g++ -g -o xmos_mixer usb_mixer.cpp mixer_app.cpp -I. -IOSX OSX/libusb-1.0.0.dylib -arch x86_64

View File

@@ -0,0 +1,5 @@
!if [set SDKPath=C:\Program^ Files\XMOS\tusbaudiosdk]
!endif
all:
msbuild host_usb_mixer_control.vcxproj /property:Configuration=Release /property:Platform=x64

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,117 @@
The XMOS USB Audio Reference Design, by default, contains an 18x8 mixer unit
(note that at sample rates above 96Khz only the first two outputs are
enabled).
----WINDOWS REQUIREMENTS----
Building the mixer on Windows requires the tusbaudio SDK from Thesycon. The
default location for the SDK is C:\Program Files\XMOS\tusbaudiosdk\
If it can be found on a different path then this can be changed in
Makefile.Win.
The mixer app on windows makes use of a USB dynamic library, also from Thesycon.
If required please contact thesycon.de for support.
----------------------------
This unit takes input takes 18 inputs: USB OUT channels 1..10 and
DEVICE IN channels 1..6,9..10 and produces 8 outputs: Mixer Output
1..8
Before the mixer there is an unit that allows the selection of the 18 mixer inputs
from all the possible device inputs (DAW and physical audio). This is
an extension unit with id 50 in the descriptors
After the mixer unit there is are channel map units for each output terminal:
Each of these outputs can select a source from one of 28 channels sources: USB OUT
channels 1..10, DEVICE IN channels 1..10 and Mixer Output 1..8
The channel map units are extension unit with init ids 51 and 52. This unit
lets you implement arbitrary routings including loopbacks.
The mixer is controlled on macOS via the command line utility
xmos_mixer. Running this application requires having the
libusb-1.0.0.dylib in the dynamic library load path. Sourcing the
setup.sh script will do this. Source code for the application is
provided as a guide on how to communicate with the device.
Here are the commands for the mixer application (note that the USB
audio reference design has only one unit so the mixer_id argument
should always be 0):
--help
--display-info
Show information about the device.
--display-mixer-nodes mixer_id
Display all the weights of all the mixer nodes (and their id) of a particular mixer.
--display-min mixer_id
Display the minimum allowable weights of a particular mixer.
--display-max mixer_id
Display the maximum allowable weights of a particular mixer.
--display-res mixer_id
Display the resolution of a particular mixer.
--set-value mixer_id mixer_unit value
Set the weight value in the mixer. The second argument should
correspond to the values shown by the --display-unit command. Values
can range from -127db to +128db with the special value -inf for mute.
--get-value mixer_id mixer_unit
Get the weight value in the mixer. The second argument should
correspond to the values shown by the --display-unit command. Values
can range from -127db to +128db with the special value -inf for mute.
--set-mixer-source mixer_id, dst_channel_id, src_channel_id
Allows the selection of the mixer inputs. Sets mixer input (dst) to src
--display-current-mixer-sources mixer_id
Displays the current inputs to a particular mixer
--display-available-mixer-sources mixer_id
Displays all the input channels available that can be fed into the inputs of a particular mixer
--set-aud-channel-map dst src
Sets a channel map value for the device audio output
--display-aud-channel-map
Show audio output channel map i.e. for each audio output of the device what the source is.
--display-aud-channel-map-sources
Show the available audio output channel map sources.
--set-daw-channel-map dst src
Sets a channel map value for the DAW output to the host
--display-daw-channel-map
Show audio output channel map i.e. for each DAW output to host, what the source is.
--display-daw-channel-map-sources
Show the DAW output channel map sources.
--get-mixer-levels-input
--get-mixer-levels-output
--vendor-audio-request-get bRequest, ControlSelector, ChannelNumber, UnitId
--vendor-audio-request-set bRequest, ControlSelector, ChannelNumber, UnitId, Data[0], Data[1],...

View File

@@ -0,0 +1,48 @@
// Copyright 2022-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
/************************************************************************
*
* Module: global.h
* Description:
* APP global includes, constants, declarations, etc.
*
* Author(s):
* Udo Eberhardt
*
* Companies:
* Thesycon GmbH, Germany http://www.thesycon.de
*
************************************************************************/
#ifndef __global_h__
#define __global_h__
// define the Windows versions supported by the application
#define _WIN32_WINNT 0x0500 //Windows 2000 or later
//#define _WIN32_WINNT 0x0501 //Windows XP or later
//#define _WIN32_WINNT 0x0600 //Windows Vista or later
//#define _WIN32_WINNT 0x0A00 //Windows 10 or later
// exclude rarely-used stuff from Windows headers
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
// version defs
//#include "version.h"
// libwn.h pulls in windows.h
#include "libwn.h"
// TUSBAUDIO driver API
#include "tusbaudioapi.h"
#include "TUsbAudioApiDll.h"
#endif // __global_h__
/*************************** EOF **************************************/

View File

@@ -0,0 +1,177 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{84eacf4f-e405-4909-b440-a04a84a3f8c8}</ProjectGuid>
<RootNamespace>
</RootNamespace>
<SDKPath Condition="'$(SDKPath)' == ''">C:\Program Files\XMOS\tusbaudiosdk</SDKPath>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>false</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
<Import Project="$(SDKPath)\source\tusbaudioapi_inc\tusbaudioapi_inc_vs2019.vcxitems" Label="Shared" />
<Import Project="$(SDKPath)\source\libwn_min\_libwn_min_vs2019.vcxitems" Label="Shared" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<OutDir>.\bin\$(Configuration)\$(PlatformName)\</OutDir>
<IntDir>$(Configuration)\$(PlatformName)_$(PlatformToolset)</IntDir>
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutDir>.\bin\$(Configuration)\$(PlatformName)\</OutDir>
<IntDir>$(Configuration)\$(PlatformName)_$(PlatformToolset)</IntDir>
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<SDLCheck>
</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_AMD64;_CONSOLE;_UNICODE;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>.;.\Win;$(SDKPath)\source\shared;$(SDKPath)\source\inc;$(SDKPath)\source\libwn_min;$(SDKPath)\source\libwtl;$(SDKPath)\source\tusbaudioapi_inc</AdditionalIncludeDirectories>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<ExceptionHandling>Async</ExceptionHandling>
<PrecompiledHeaderFile />
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
<EnableUAC>false</EnableUAC>
<UACExecutionLevel />
<UACUIAccess />
<GenerateMapFile>true</GenerateMapFile>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>false</IntrinsicFunctions>
<SDLCheck>
</SDLCheck>
<PreprocessorDefinitions>_AMD64;_CONSOLE;_UNICODE;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>.;.\Win;$(SDKPath)\source\shared;$(SDKPath)\source\inc;$(SDKPath)\source\libwn_min;$(SDKPath)\source\libwtl;$(SDKPath)\source\tusbaudioapi_inc</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<ExceptionHandling>Async</ExceptionHandling>
<PrecompiledHeaderFile />
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
<EnableUAC>false</EnableUAC>
<UACExecutionLevel />
<UACUIAccess />
<GenerateMapFile>true</GenerateMapFile>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="usb_mixer.h" />
<ClInclude Include="Win\global.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="mixer_app.cpp" />
<ClCompile Include="usb_mixer.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="usb_mixer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Win\global.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="mixer_app.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Win\usb_mixer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>

View File

@@ -0,0 +1,718 @@
// Copyright 2022-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "usb_mixer.h"
#define MIXER_UNIT_DISPLAY_VALUE 2
#define MIXER_UNIT_DISPLAY_MIN 3
#define MIXER_UNIT_DISPLAY_MAX 4
#define MIXER_UNIT_DISPLAY_RES 5
// TODO
// res, min, max
int mixer_init(void)
{
/* Open the connection to the USB mixer */
if (usb_mixer_connect() == USB_MIXER_FAILURE)
{
return USB_MIXER_FAILURE;
}
return USB_MIXER_SUCCESS;
}
int mixer_deinit(void) {
// Close the connection to the USB mixer
if (usb_mixer_disconnect() == USB_MIXER_FAILURE) {
return USB_MIXER_FAILURE;
}
return USB_MIXER_SUCCESS;
}
int mixer_display(unsigned int mixer_index, unsigned int type) {
int i = 0;
int j = 0;
int num_inputs = usb_mixer_get_num_inputs(mixer_index);
int num_outputs = usb_mixer_get_num_outputs(mixer_index);
printf("\n");
switch (type) {
case MIXER_UNIT_DISPLAY_VALUE:
//mixer_update_all_values(mixer_index);
printf(" Mixer Values (%d)\n", mixer_index);
printf(" ----------------\n\n");
break;
case MIXER_UNIT_DISPLAY_MIN:
printf(" Mixer Ranges Min (%d)\n", mixer_index);
printf(" --------------------\n\n");
break;
case MIXER_UNIT_DISPLAY_MAX:
printf(" Mixer Ranges Max (%d)\n", mixer_index);
printf(" --------------------\n\n");
break;
case MIXER_UNIT_DISPLAY_RES:
printf(" Mixer Ranges Res (%d)\n", mixer_index);
printf(" --------------------\n\n");
break;
default:
return USB_MIXER_FAILURE;
break;
}
printf(" \t\t\t");
printf("Mixer Outputs\n");
printf("\t\t ");
for (i = 0; i < num_outputs; i++) {
printf(" %d", i+1);
}
printf("\n");
for (i = 0; i < num_inputs; i++) {
printf(" %-20s", usb_mixer_get_input_name(mixer_index,i));
for (j = 0; j < num_outputs; j++) {
switch (type) {
case MIXER_UNIT_DISPLAY_VALUE:
{
double mixNodeVal = usb_mixer_get_value(mixer_index, (i*num_outputs)+j);
int nodeid = (i*num_outputs)+j;
if (mixNodeVal <= -127.996)// todo shoud be < min
{
printf("\t%3d:[ %s ]", nodeid,"-inf");
}
else
{
printf("\t%3d:[%08.03f]", nodeid, mixNodeVal);
}
}
break;
case MIXER_UNIT_DISPLAY_MIN:
{
int nodeid = (i*num_outputs)+j;
printf("\t%3d:[%08.03f]", nodeid, usb_mixer_get_min(mixer_index, (i*num_outputs)+j)) ;
}
break;
case MIXER_UNIT_DISPLAY_MAX:
{
int nodeid = (i*num_outputs)+j;
printf("\t%3d:[%08.03f]", nodeid, usb_mixer_get_max(mixer_index, (i*num_outputs)+j)) ;
}
break;
case MIXER_UNIT_DISPLAY_RES:
{
int nodeid = (i*num_outputs)+j;
printf("\t%3d:[%08.03f]", nodeid, usb_mixer_get_res(mixer_index, (i*num_outputs)+j)) ;
}
break;
default:
return USB_MIXER_FAILURE;
break;
}
}
printf("\n");
}
printf("\n");
return USB_MIXER_SUCCESS;
}
/* Displays basic mixer information */
int mixer_display_info(void)
{
unsigned int i = 0;
int num_mixers = usb_mixer_get_num_mixers();
printf("\n");
printf(" Mixer Info\n");
printf(" ----------\n\n");
printf(" Mixers : %d\n\n", num_mixers);
for (i = 0; i < num_mixers; i++)
{
int num_inputs = usb_mixer_get_num_inputs(i);
int num_outputs = usb_mixer_get_num_outputs(i);
printf(" Mixer %d\n", i);
printf(" -------\n");
printf(" Inputs : %d\n"
" Outputs : %d\n\n", num_inputs, num_outputs);
printf(" Mixer Output Labels:\n");
for(int j = 0; j < num_outputs; j++)
{
printf(" %d: %s\n", j,usb_mixer_get_output_name(i,j));
}
//printf("\n Selectable Inputs (%d): \n", usb_mixsel_get_input_count(i));
//for(int j = 0; j < usb_mixsel_get_input_count(i); j++)
//{
// printf(" %d: %s\n", j, usb_mixsel_get_input_string(i,j));
//}
}
printf("\n");
return USB_MIXER_SUCCESS;
}
void display_available_mixer_sources(int mixIndex)
{
printf("\n");
printf(" Available Mixer Sources (%d)\n", mixIndex);
printf(" -------------------------\n\n");
for(int j = 0; j < usb_mixsel_get_input_count(mixIndex); j++)
{
printf(" %d: %s\n", j, usb_mixsel_get_input_string(mixIndex,j));
}
}
/* Gets the current mixer inputs from the device an displays them */
void display_mixer_sources(int mixerIndex)
{
printf("\n");
printf(" Current Mixer Sources (%d)\n", mixerIndex);
printf(" -------------------------\n\n");
/* Note, mixSel output cound and mixer input chan count should be the same! */
printf(" Number of mixer sources: %d\n", usb_mixsel_get_output_count(mixerIndex));
/* Get the current channel number for every mixer input */
for(int i = 0; i < usb_mixsel_get_output_count(mixerIndex); i++)
{
int inputChan = (int)usb_mixsel_get_state(mixerIndex, i);
char *str = usb_mixer_get_input_name(mixerIndex,inputChan);
printf(" Mixer input %d: Source chan id: %d (%s)\n", i, inputChan, str);
}
}
/* set mixer source */
void set_mixer_source(unsigned mixerIndex, unsigned dst, unsigned src)
{
usb_mixsel_set_state(mixerIndex, dst, src);
/* String lookup */
char *str = usb_mixer_get_input_name(mixerIndex, dst);
int state = usb_mixsel_get_state(mixerIndex, dst);
printf("\n Set mixer(%d) input %d to device input %d (%s)\n", mixerIndex, dst, state, str);
}
void display_aud_channel_map()
{
printf("\n");
printf(" Audio Output Channel Map\n");
printf(" ------------------------\n\n");
for (int i=0;i<usb_get_aud_channel_map_num_outputs();i++)
{
int x = usb_get_aud_channel_map(i);
printf("%d (DEVICE OUT - %s) source is ",i, usb_get_aud_channel_map_name(i));
switch (usb_get_aud_channel_map_type(x))
{
case USB_CHAN_OUT:
printf(" %d (DAW OUT - %s)\n",x,usb_get_aud_channel_map_name(x));
break;
case USB_CHAN_IN:
printf("%d (DEVICE IN - %s)\n",x,usb_get_aud_channel_map_name(x));
break;
case USB_CHAN_MIXER:
printf("%d (%s)\n",x,usb_get_aud_channel_map_name(x));
break;
}
}
}
void display_daw_channel_map()
{
printf("\n");
printf(" DAW Output To Host Channel Map\n");
printf(" ------------------------\n\n");
for (int i=0;i<usb_get_usb_channel_map_num_outputs();i++)
{
int x = usb_get_usb_channel_map(i);
printf("%d (DAW IN - %s) source is ",i, usb_get_usb_channel_map_name(i + usb_get_aud_channel_map_num_outputs()));
switch (usb_get_usb_channel_map_type(x))
{
case USB_CHAN_OUT:
printf(" %d (DAW OUT - %s)\n",x,usb_get_usb_channel_map_name(x));
break;
case USB_CHAN_IN:
printf("%d (DEVICE IN - %s)\n",x,usb_get_usb_channel_map_name(x));
break;
case USB_CHAN_MIXER:
printf("%d (%s)\n",x,usb_get_usb_channel_map_name(x));
break;
}
}
}
void display_aud_channel_map_sources(void)
{
printf("\n");
printf(" Audio Output Channel Map Source List\n");
printf(" ------------------------------------\n\n");
for (int i=0;i<usb_get_aud_channel_map_num_inputs();i++) {
switch (usb_get_aud_channel_map_type(i))
{
case USB_CHAN_OUT:
printf("%d (DAW OUT - %s)\n",i,usb_get_aud_channel_map_name(i));
break;
case USB_CHAN_IN:
printf("%d (DEVICE IN - %s)\n",i,usb_get_aud_channel_map_name(i));
break;
case USB_CHAN_MIXER:
printf("%d (%s)\n",i,usb_get_aud_channel_map_name(i));
break;
}
}
}
void display_daw_channel_map_sources(void)
{
printf("\n");
printf(" DAW Output to Host Channel Map Source List\n");
printf(" ------------------------------------------\n\n");
for (int i=0;i<usb_get_usb_channel_map_num_inputs();i++) {
switch (usb_get_usb_channel_map_type(i))
{
case USB_CHAN_OUT:
printf("%d (DAW OUT - %s)\n",i,usb_get_usb_channel_map_name(i));
break;
case USB_CHAN_IN:
printf("%d (DEVICE IN - %s)\n",i,usb_get_usb_channel_map_name(i));
break;
case USB_CHAN_MIXER:
printf("%d (%s)\n",i,usb_get_usb_channel_map_name(i));
break;
}
}
}
int usb_audio_request_get(unsigned bRequest, unsigned cs, unsigned cn, unsigned unitId, unsigned char *data)
{
char reqStr[] = "Custom";
if(bRequest == CUR)
{
strcpy(reqStr, "CUR");
}
else if(bRequest == RANGE)
{
strcpy(reqStr, "RANGE");
}
else if(bRequest == MEM)
{
strcpy(reqStr, "MEM");
}
printf("Performing class GET request to Audio Interface:\n\
bRequest: 0x%02x (%s)\n\
wValue: 0x%04x (Control Sel: %d, Channel Number: %d)\n\
wIndex: 0x%04x (Interface: 0, Entity: %d)\n\
\n", bRequest, reqStr, (cs<<8)|cn, cs, cn, unitId<<8, unitId);
return usb_audio_class_get(bRequest, cs, cn, unitId, 64, data);
}
int usb_audio_request_set(unsigned bRequest, unsigned cs, unsigned cn, unsigned unitId,
unsigned char *data, int datalength)
{
char reqStr[] = "Custom";
if(bRequest == CUR)
{
strcpy(reqStr, "CUR");
}
else if(bRequest == RANGE)
{
strcpy(reqStr, "RANGE");
}
{
strcpy(reqStr, "MEM");
}
printf("Performing class SET request to Audio Interface:\n\
bRequest: 0x%02x (%s)\n\
wValue: 0x%04x (Control Sel: %d, Channel Number: %d)\n\
wIndex: 0x%04x (Interface: 0, Entity: %d)\n\
\n", bRequest, reqStr, (cs<<8)|cn, cs, cn, unitId<<8, unitId);
return usb_audio_class_set(bRequest, cs, cn, unitId, datalength, data);
}
int usb_audio_memreq_get(unsigned unitId, unsigned offset, unsigned char *data)
{
/* Mem requests dont have CS/CN, just an offset.. */
return usb_audio_request_get(MEM, (offset>>8), offset&0xff, unitId, data);
}
void print_levels(const char* levelTitle, unsigned char* levels, int levelBytes)
{
unsigned levelCount = levelBytes/2;
unsigned short* levelData = (unsigned short*) levels;
printf("\n %s Level Data\n"
" ----------------------\n\n"
"%d bytes (%d channels) returned:\n"
, levelTitle, levelBytes, levelCount);
for(int i = 0; i<levelCount; i++)
{
printf("%s %d: 0x%04x\n", levelTitle, i,levelData[i]);
}
}
void mixer_display_usage(void) {
fprintf(stderr, "Usage :\n");
fprintf(stderr,
" --display-info\n"
" --display-mixer-nodes mixer_id\n"
" --display-min mixer_id\n"
" --display-max mixer_id\n"
" --display-res mixer_id\n"
" --set-value mixer_id, mixer_node, value\n"
" --get-value mixer_id, mixer_node\n"
"\n"
" --set-mixer-source mixer_id dst channel_id, src_channel_id\n"
" --display-current-mixer-sources mixer_id\n"
" --display-available-mixer-sources mixer_id\n"
"\n"
" --set-aud-channel-map dst_channel_id, src_channel_id\n"
" --display-aud-channel-map \n"
" --display-aud-channel-map-sources\n"
" --set-daw-channel-map dst_channel_id, src_channel_id\n"
" --display-daw-channel-map \n"
" --display-daw-channel-map-sources\n"
"\n"
" --get-mixer-levels-input mixer_id\n"
" --get-mixer-levels-output mixer_id\n"
" --vendor-audio-request-get bRequest, ControlSelector, ChannelNumber, UnitId\n"
" --vendor-audio-request-set bRequest, ControlSelector, ChannelNumber, UnitId, Data[0], Data[1],...\n"
);
}
void usage_error()
{
fprintf(stderr, "ERROR :: incorrect number of arguments passed. See --help\n");
}
int main (int argc, char **argv) {
unsigned int mixer_index = 0;
unsigned int result = 0;
if (argc < 2) {
fprintf(stderr, "ERROR :: No options passed to mixer application\n");
mixer_display_usage();
return -1;
}
if (strcmp(argv[1], "--help") == 0) {
mixer_display_usage();
return 0;
}
if (mixer_init() != USB_MIXER_SUCCESS) {
fprintf(stderr, "ERROR :: Cannot connect\n");
return -1;
}
if (strcmp(argv[1], "--display-info") == 0)
{
mixer_display_info();
}
else if (strcmp(argv[1], "--display-mixer-nodes") == 0)
{
if (argv[2])
{
mixer_index = atoi(argv[2]);
} else {
fprintf(stderr, "ERROR :: No mixer index supplied\n");
return -1;
}
mixer_display(mixer_index, MIXER_UNIT_DISPLAY_VALUE);
} else if (strcmp(argv[1], "--display-mixer-nodes") == 0) {
if (argv[2]) {
mixer_index = atoi(argv[2]);
} else {
fprintf(stderr, "ERROR :: No mixer index supplied\n");
return -1;
}
mixer_display(mixer_index, MIXER_UNIT_DISPLAY_VALUE);
} else if (strcmp(argv[1], "--display-min") == 0) {
if (argv[2]) {
mixer_index = atoi(argv[2]);
} else {
fprintf(stderr, "ERROR :: No mixer index supplied\n");
return -1;
}
mixer_display(mixer_index, MIXER_UNIT_DISPLAY_MIN);
} else if (strcmp(argv[1], "--display-max") == 0) {
if (argv[2]) {
mixer_index = atoi(argv[2]);
} else {
fprintf(stderr, "ERROR :: No mixer index supplied\n");
return -1;
}
mixer_display(mixer_index, MIXER_UNIT_DISPLAY_MAX);
} else if (strcmp(argv[1], "--display-res") == 0) {
if (argv[2]) {
mixer_index = atoi(argv[2]);
} else {
fprintf(stderr, "ERROR :: No mixer index supplied\n");
return -1;
}
mixer_display(mixer_index, MIXER_UNIT_DISPLAY_RES);
}
else if (strcmp(argv[1], "--set-value") == 0) {
unsigned int mixer_unit = 0;
double value = 0;
if (argc < 5) {
fprintf(stderr, "ERROR :: incorrect number of arguments passed\n");
return -1;
}
mixer_index = atoi(argv[2]);
mixer_unit = atoi(argv[3]);
if (strcmp(argv[4],"-inf")==0)
value = -128;
else
value = atof(argv[4]);
usb_mixer_set_value(mixer_index, mixer_unit, value);
} else if (strcmp(argv[1], "--get-value") == 0) {
unsigned int mixer_unit = 0;
double result = 0;
if (argc < 4) {
fprintf(stderr, "ERROR :: incorrect number of arguments passed\n");
return -1;
}
mixer_index = atoi(argv[2]);
mixer_unit = atoi(argv[3]);
result = usb_mixer_get_value(mixer_index, mixer_unit);
if (result <= -127.996)
printf("%s\n", "-inf");
else
printf("%g\n",result);
}
else if (strcmp(argv[1], "--display-current-mixer-sources") == 0)
{
if(argc < 3)
{
usage_error();
return -1;
}
display_mixer_sources(atoi(argv[2]));
}
else if (strcmp(argv[1], "--display-available-mixer-sources") == 0)
{
if(argc < 3)
{
usage_error();
return -1;
}
display_available_mixer_sources(atoi(argv[2]));
}
else if(strcmp(argv[1], "--set-mixer-source") == 0)
{
if(argc < 5)
{
usage_error();
return -1;
}
set_mixer_source(atoi(argv[2]), atoi(argv[3]), atoi(argv[4]));
}
else if (strcmp(argv[1], "--display-aud-channel-map") == 0)
{
/* Display the channel mapping to the devices audio outputs */
display_aud_channel_map();
}
else if (strcmp(argv[1], "--display-aud-channel-map-sources") == 0)
{
display_aud_channel_map_sources();
}
else if (strcmp(argv[1], "--display-daw-channel-map") == 0)
{
/* Display the channel mapping to the devices DAW output to host */
display_daw_channel_map();
}
else if (strcmp(argv[1], "--display-daw-channel-map-sources") == 0)
{
display_daw_channel_map_sources();
}
else if (strcmp(argv[1], "--set-aud-channel-map") == 0)
{
unsigned int dst = 0;
unsigned int src = 0;
if (argc != 4)
{
usage_error();
return -1;
}
dst = atoi(argv[2]);
src = atoi(argv[3]);
usb_set_aud_channel_map(dst, src);
}
else if (strcmp(argv[1], "--set-daw-channel-map") == 0)
{
unsigned int dst = 0;
unsigned int src = 0;
if (argc != 4)
{
usage_error();
return -1;
}
dst = atoi(argv[2]);
src = atoi(argv[3]);
usb_set_usb_channel_map(dst, src);
}
else if(strcmp(argv[1], "--get-mixer-levels-input") == 0 ||
strcmp(argv[1],"--get-mixer-levels-output") == 0)
{
unsigned int dst = 0;
unsigned char levels[64];
int datalength = 0;
int offset = 0;
if (argc < 3) {
fprintf(stderr, "ERROR :: incorrect number of arguments passed\n");
return -1;
}
if(strcmp(argv[1],"--get-mixer-levels-output") == 0)
offset = 1;
for(int i = 0; i < 64; i++)
levels[i] = 0;
dst = atoi(argv[2]);
/* Mem request to mixer with offset of 0 gives input levels */
datalength = usb_mixer_mem_get(dst, offset, levels);
if(datalength < 0)
{
fprintf(stderr, "ERROR in control request: %d\n", datalength);
return -1;
}
if(offset)
print_levels("Mixer Output", levels, datalength);
else
print_levels("Mixer Input", levels, datalength);
}
else if(strcmp(argv[1], "--vendor-audio-request-get") == 0)
{
unsigned int bRequest = 0;
unsigned int cs = 0;
unsigned int cn = 0;
unsigned int unitId = 0;
int datalength = 0;
unsigned char data[64];
if(argc < 6)
{
fprintf(stderr, "ERROR :: incorrect number of arguments passed\n");
return -1;
}
for(int i = 0; i < 64; i++)
data[i] = 0;
bRequest = atoi(argv[2]);
cs = atoi(argv[3]);
cn = atoi(argv[4]);
unitId = atoi(argv[5]);
/* Do request */
datalength = usb_audio_request_get(bRequest, cs, cn, unitId, data);
/* Print result */
if(datalength < 0)
{
fprintf(stderr, "ERROR in control request: %d\n", datalength);
}
else
{
printf("Response (%d bytes):\n", datalength);
for(int i = 0; i < datalength; i++)
printf("0x%02x\n" ,data[i]);
}
}
else if(strcmp(argv[1], "--vendor-audio-request-set") == 0)
{
unsigned int bRequest = 0;
unsigned int cs = 0;
unsigned int cn = 0;
unsigned int unitId = 0;
unsigned char data[64];
for(int i=0; i<64; i++)
{
data[i] = 0;
}
if(argc < 7)
{
fprintf(stderr, "ERROR :: incorrect number of arguments passed - no data passed\n");
return -1;
}
bRequest = atoi(argv[2]);
cs = atoi(argv[3]);
cn = atoi(argv[4]);
unitId = atoi(argv[5]);
/* Get data */
for(int i=0; i < argc-6; i++)
{
data[i] = atoi(argv[i+6]);
}
result = usb_audio_request_set(bRequest, cs, cn, unitId, data, argc-6);
if(result < 0)
{
fprintf(stderr, "ERROR :: Error detected in Set request: %d\n", result);
return -1;
}
}
else
{
fprintf(stderr, "ERROR :: Invalid option passed to mixer application\n");
return -1;
}
mixer_deinit();
return result;
}

View File

@@ -0,0 +1 @@
export DYLD_LIBRARY_PATH=$PWD/OSX:$DYLD_LIBRARY_PATH

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,108 @@
// Copyright 2022-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#define USB_MIXER_SUCCESS 0
#define USB_MIXER_FAILURE -1
#define USB_MIXERS 1
#define USB_MIXER_INPUTS 18
#define USB_MIXER_OUTPUTS 8
#define USB_MAX_CHANNEL_MAP_SIZE 40
#define USB_MIXER_MAX_NAME_LEN 64
enum usb_chan_type {
USB_CHAN_OUT=0,
USB_CHAN_IN=1,
USB_CHAN_MIXER=2
};
/* A.14 Audio Class-Specific Request Codes */
#define REQUEST_CODE_UNDEFINED 0x00
#define CUR (1)
#define RANGE (2)
#define MEM (3)
int usb_mixer_connect();
int usb_mixer_disconnect();
/* MIXER UNIT(s) INTERFACE */
/* Returns total number of mixers in device */
int usb_mixer_get_num_mixers();
/* Returns number of inputs and outputs for a selected mixer */
int usb_mixer_get_layout(unsigned int mixer, unsigned int *inputs, unsigned int *outputs);
/* Returns the name for a selected mixer input */
char *usb_mixer_get_input_name(unsigned int mixer, unsigned int input);
/* Returns the name for a selected mixer output */
char *usb_mixer_get_output_name(unsigned int mixer, unsigned int output);
/* Returns the current value of a selected mixer unit */
double usb_mixer_get_value(unsigned int mixer, unsigned int mixer_unit);
/* Sets the current value for a selected mixer unit */
int usb_mixer_set_value(unsigned int mixer, unsigned int mixer_unit, double val);
/* Returns the range values for a selected mixer unit */
int usb_mixer_get_range(unsigned int mixer, unsigned int mixer_unit, double *min, double *max, double *res);
/* Returns the number of bytes read from a mem request, data is stored in data */
int usb_mixer_mem_get(unsigned int mixer, unsigned offset, unsigned char *data);
/* INPUT / OUTPUT / MIXER MAPPING UNIT INTERFACE */
/* Get the number of selectable inputs */
int usb_mixsel_get_input_count(unsigned int mixer);
/* Get the string of a input */
char *usb_mixsel_get_input_string(unsigned int mixer, unsigned int channel);
int usb_mixsel_get_output_count(unsigned int mixer);
int usb_mixer_get_num_outputs(unsigned int mixer);
int usb_mixer_get_num_inputs(unsigned int mixer);
unsigned char usb_mixsel_get_state(unsigned int mixer, unsigned int channel);
void usb_mixsel_set_state(unsigned int mixer, unsigned int dst, unsigned int src);
int usb_set_usb_channel_map(int channel, int val);
/* Get the current map for a specified input / output / mixer channel */
int usb_get_usb_channel_map(int channel);
int usb_get_aud_channel_map(int channel);
/* Maps an input / output / mixer channel to another input / output / mixer channel */
int usb_set_aud_channel_map(int channel, int val);
int usb_set_usb_channel_map(int channel, int val);
/* Gets the name of a specified channel */
char *usb_get_aud_channel_map_name(int channel);
char *usb_get_usb_channel_map_name(int channel);
/* Get the type of a channel map */
enum usb_chan_type usb_get_aud_channel_map_type(int channel);
enum usb_chan_type usb_get_usb_channel_map_type(int channel);
int usb_get_aud_channel_map_num_outputs();
int usb_get_usb_channel_map_num_outputs();
int usb_get_aud_channel_map_num_inputs();
int usb_get_usb_channel_map_num_inputs();
/* CUSTOM/GENERIC AUDIO CLASS REQUESTS */
int usb_audio_class_get(unsigned char bRequest, unsigned char cs, unsigned char cn, unsigned short unitID, unsigned short wLength, unsigned char *data);
int usb_audio_class_set(unsigned char bRequest, unsigned char cs, unsigned char cn, unsigned short unitID, unsigned short wLength, unsigned char *data);
double usb_mixer_get_res(unsigned int mixer, unsigned int nodeId);
double usb_mixer_get_min(unsigned int mixer, unsigned int nodeId) ;
double usb_mixer_get_max(unsigned int mixer, unsigned int nodeId) ;

View File

@@ -1,4 +1,4 @@
// Copyright 2017-2022 XMOS LIMITED. // Copyright 2017-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1. // This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef _XUA_H_ #ifndef _XUA_H_
#define _XUA_H_ #define _XUA_H_
@@ -7,14 +7,14 @@
#include "xua_conf_full.h" #include "xua_conf_full.h"
#if __XC__ || __STDC__ #ifndef __ASSEMBLER__
#include "xua_audiohub.h" #include "xua_audiohub.h"
#include "xua_endpoint0.h" #include "xua_endpoint0.h"
#include "xua_buffer.h" #include "xua_buffer.h"
#include "xua_mixer.h" #include "xua_mixer.h"
#endif #endif
#if __XC__ #ifdef __XC__
#include "xua_clocking.h" #include "xua_clocking.h"
#include "xua_midi.h" #include "xua_midi.h"
#if XUA_NUM_PDM_MICS > 0 #if XUA_NUM_PDM_MICS > 0

View File

@@ -1,9 +1,9 @@
// Copyright 2011-2022 XMOS LIMITED. // Copyright 2011-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1. // This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef __XUA_AUDIOHUB_H__ #ifndef _XUA_AUDIOHUB_H_
#define __XUA_AUDIOHUB_H__ #define _XUA_AUDIOHUB_H_
#if __XC__ #ifdef __XC__
#include "xccompat.h" #include "xccompat.h"
#include "xs1.h" #include "xs1.h"
@@ -80,4 +80,4 @@ void UserBufferManagementInit();
void UserBufferManagement(unsigned sampsFromUsbToAudio[], unsigned sampsFromAudioToUsb[]); void UserBufferManagement(unsigned sampsFromUsbToAudio[], unsigned sampsFromAudioToUsb[]);
#endif // __XUA_AUDIOHUB_H__ #endif // _XUA_AUDIOHUB_H_

View File

@@ -1,4 +1,4 @@
// Copyright 2011-2022 XMOS LIMITED. // Copyright 2011-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1. // This Software is subject to the terms of the XMOS Public Licence: Version 1.
/* /*
* @brief Defines relating to device configuration and customisation of lib_xua * @brief Defines relating to device configuration and customisation of lib_xua
@@ -997,17 +997,12 @@
#define MIXER (0) #define MIXER (0)
#endif #endif
/* Tidy up old ifndef usage */
#if defined(MIXER) && (MIXER == 0)
#undef MIXER
#endif
/** /**
* @brief Number of seperate mixes to perform * @brief Number of seperate mixes to perform
* *
* Default: 8 if MIXER enabled, else 0 * Default: 8 if MIXER enabled, else 0
*/ */
#ifdef MIXER #if (MIXER)
#ifndef MAX_MIX_COUNT #ifndef MAX_MIX_COUNT
#define MAX_MIX_COUNT (8) #define MAX_MIX_COUNT (8)
#endif #endif
@@ -1087,44 +1082,24 @@
#define VOLUME_RES_MIXER (0x100) #define VOLUME_RES_MIXER (0x100)
#endif #endif
/* Handle out volume control in the mixer */ /* Handle out volume control in the mixer - enabled by default */
#if defined(OUT_VOLUME_IN_MIXER) && (OUT_VOLUME_IN_MIXER==0) #ifndef OUT_VOLUME_IN_MIXER
#undef OUT_VOLUME_IN_MIXER #define OUT_VOLUME_IN_MIXER (1)
#else
#if defined(MIXER)
// Disabled by default
//#define OUT_VOLUME_IN_MIXER
#endif
#endif #endif
/* Apply out volume controls after the mix */ /* Apply out volume controls after the mix. Only relevant when OUT_VOLUME_IN_MIXER enabled. Enabled by default */
#if defined(OUT_VOLUME_AFTER_MIX) && (OUT_VOLUME_AFTER_MIX==0) #ifndef OUT_VOLUME_AFTER_MIX
#undef OUT_VOLUME_AFTER_MIX #define OUT_VOLUME_AFTER_MIX (1)
#else
#if defined(MIXER) && defined(OUT_VOLUME_IN_MIXER)
// Enabled by default
#define OUT_VOLUME_AFTER_MIX
#endif
#endif #endif
/* Handle in volume control in the mixer */ /* Handle in volume control in the mixer - disabled by default */
#if defined(IN_VOLUME_IN_MIXER) && (IN_VOLUME_IN_MIXER==0) #ifndef IN_VOLUME_IN_MIXER
#undef IN_VOLUME_IN_MIXER #define IN_VOLUME_IN_MIXER (0)
#else
#if defined(MIXER)
/* Disabled by default */
//#define IN_VOLUME_IN_MIXER
#endif
#endif #endif
/* Apply in volume controls after the mix */ /* Apply in volume controls after the mix. Only relebant when IN_VOLUMNE_IN MIXER enabled. Enabled by default */
#if defined(IN_VOLUME_AFTER_MIX) && (IN_VOLUME_AFTER_MIX==0) #ifndef IN_VOLUME_AFTER_MIX
#undef IN_VOLUME_AFTER_MIX #define IN_VOLUME_AFTER_MIX (1)
#else
#if defined(MIXER) && defined(IN_VOLUME_IN_MIXER)
// Enabled by default
#define IN_VOLUME_AFTER_MIX
#endif
#endif #endif
/* Always enable explicit feedback EP, even when input stream is present */ /* Always enable explicit feedback EP, even when input stream is present */
@@ -1319,9 +1294,9 @@ enum USBEndpointNumber_Out
/* Some defines that allow us to remove unused code */ /* Some defines that allow us to remove unused code */
/* Useful for dropping lower part of macs in volume processing... */ /* Useful for dropping lower part of macs in volume processing... */
#if (FS_STREAM_FORMAT_OUTPUT_1_RESOLUTION_BITS > 24) || (FS_STREAM_FORMAT_OUTPUT_2_RESOLUTION_BITS > 24) || \ #if (FS_STREAM_FORMAT_OUTPUT_1_RESOLUTION_BITS > 24) || (HS_STREAM_FORMAT_OUTPUT_2_RESOLUTION_BITS > 24) || \
(FS_STREAM_FORMAT_OUTPUT_3_RESOLUTION_BITS > 24) || (HS_STREAM_FORMAT_OUTPUT_1_RESOLUTION_BITS > 24) || \ (((FS_STREAM_FORMAT_OUTPUT_2_RESOLUTION_BITS > 24) || (HS_STREAM_FORMAT_OUTPUT_2_RESOLUTION_BITS > 24)) && (OUTPUT_FORMAT_COUNT > 1)) || \
(HS_STREAM_FORMAT_OUTPUT_2_RESOLUTION_BITS > 24) || (HS_STREAM_FORMAT_OUTPUT_3_RESOLUTION_BITS > 24) (((FS_STREAM_FORMAT_OUTPUT_3_RESOLUTION_BITS > 24) || (HS_STREAM_FORMAT_OUTPUT_3_RESOLUTION_BITS > 24)) && (OUTPUT_FORMAT_COUNT > 2))
#define STREAM_FORMAT_OUTPUT_RESOLUTION_32BIT_USED 1 #define STREAM_FORMAT_OUTPUT_RESOLUTION_32BIT_USED 1
#else #else
#define STREAM_FORMAT_OUTPUT_RESOLUTION_32BIT_USED 0 #define STREAM_FORMAT_OUTPUT_RESOLUTION_32BIT_USED 0

View File

@@ -1,7 +1,7 @@
// Copyright 2017-2022 XMOS LIMITED. // Copyright 2017-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1. // This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef __XUA_CONF_FULL_H__ #ifndef _XUA_CONF_FULL_H_
#define __XUA_CONF_FULL_H__ #define _XUA_CONF_FULL_H_
#ifdef __xua_conf_h_exists__ #ifdef __xua_conf_h_exists__
#include "xua_conf.h" #include "xua_conf.h"

View File

@@ -1,8 +1,10 @@
// Copyright 2011-2022 XMOS LIMITED. // Copyright 2011-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1. // This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef _XUA_MIXER_H_ #ifndef _XUA_MIXER_H_
#define _XUA_MIXER_H_ #define _XUA_MIXER_H_
#include "xua.h"
enum mix_ctl_cmd { enum mix_ctl_cmd {
SET_SAMPLES_TO_HOST_MAP, SET_SAMPLES_TO_HOST_MAP,
SET_SAMPLES_TO_DEVICE_MAP, SET_SAMPLES_TO_DEVICE_MAP,
@@ -31,4 +33,14 @@ enum mix_ctl_cmd {
*/ */
void mixer(chanend c_to_host, chanend c_to_audio, chanend c_mix_ctl); void mixer(chanend c_to_host, chanend c_to_audio, chanend c_mix_ctl);
#define XUA_MIXER_OFFSET_OUT (0)
#define XUA_MIXER_OFFSET_IN (NUM_USB_CHAN_OUT)
#define XUA_MIXER_OFFSET_MIX (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN)
#define XUA_MIXER_OFFSET_OFF (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT)
/* Defines uses for DB to actual muliplier conversion */
#define XUA_MIXER_MULT_FRAC_BITS (25)
#define XUA_MIXER_DB_FRAC_BITS (8)
#define XUA_MIXER_MAX_MULT (1<<XUA_MIXER_MULT_FRAC_BITS) /* i.e. multiply by 0 */
#endif #endif

View File

@@ -1,9 +1,8 @@
.. _sec_api: .. _sec_api:
API Reference API Reference
------------- *************
.. toctree:: .. toctree::

View File

@@ -1,7 +1,7 @@
.. _sec_api_component: .. _sec_api_component:
Component API Component API
------------- =============
The following functions can be called from the top level main of an The following functions can be called from the top level main of an
application and implement the various components described in application and implement the various components described in

View File

@@ -2,7 +2,7 @@
.. _sec_api_defines: .. _sec_api_defines:
Configuration Defines Configuration Defines
--------------------- =====================
An application using the USB audio framework needs to have defines set for configuration. An application using the USB audio framework needs to have defines set for configuration.
Defaults for these defines are found in ``xua_conf_default.h``. Defaults for these defines are found in ``xua_conf_default.h``.
@@ -13,7 +13,7 @@ for a relevant build configuration.
This section fully documents all of the settable defines and their default values (where appropriate). This section fully documents all of the settable defines and their default values (where appropriate).
Code Location (tile) Code Location (tile)
~~~~~~~~~~~~~~~~~~~~ --------------------
.. doxygendefine:: AUDIO_IO_TILE .. doxygendefine:: AUDIO_IO_TILE
.. doxygendefine:: XUD_TILE .. doxygendefine:: XUD_TILE
@@ -23,7 +23,7 @@ Code Location (tile)
.. doxygendefine:: PLL_REF_TILE .. doxygendefine:: PLL_REF_TILE
Channel Counts Channel Counts
~~~~~~~~~~~~~~ --------------
.. doxygendefine:: NUM_USB_CHAN_OUT .. doxygendefine:: NUM_USB_CHAN_OUT
.. doxygendefine:: NUM_USB_CHAN_IN .. doxygendefine:: NUM_USB_CHAN_IN
@@ -31,7 +31,7 @@ Channel Counts
.. doxygendefine:: I2S_CHANS_ADC .. doxygendefine:: I2S_CHANS_ADC
Frequencies and Clocks Frequencies and Clocks
~~~~~~~~~~~~~~~~~~~~~~ ----------------------
.. doxygendefine:: MAX_FREQ .. doxygendefine:: MAX_FREQ
.. doxygendefine:: MIN_FREQ .. doxygendefine:: MIN_FREQ
@@ -40,24 +40,24 @@ Frequencies and Clocks
.. doxygendefine:: MCLK_48 .. doxygendefine:: MCLK_48
Audio Class Audio Class
~~~~~~~~~~~ -----------
.. doxygendefine:: AUDIO_CLASS .. doxygendefine:: AUDIO_CLASS
.. doxygendefine:: AUDIO_CLASS_FALLBACK .. doxygendefine:: AUDIO_CLASS_FALLBACK
.. doxygendefine:: FULL_SPEED_AUDIO_2 .. doxygendefine:: FULL_SPEED_AUDIO_2
System Feature Configuration Feature Configuration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ---------------------
MIDI MIDI
.... ^^^^
.. doxygendefine:: MIDI .. doxygendefine:: MIDI
.. doxygendefine:: MIDI_RX_PORT_WIDTH .. doxygendefine:: MIDI_RX_PORT_WIDTH
S/PDIF S/PDIF
...... ^^^^^^
.. doxygendefine:: XUA_SPDIF_TX_EN .. doxygendefine:: XUA_SPDIF_TX_EN
.. doxygendefine:: SPDIF_TX_INDEX .. doxygendefine:: SPDIF_TX_INDEX
@@ -65,37 +65,37 @@ S/PDIF
.. doxygendefine:: SPDIF_RX_INDEX .. doxygendefine:: SPDIF_RX_INDEX
ADAT ADAT
.... ^^^^
.. doxygendefine:: XUA_ADAT_RX_EN .. doxygendefine:: XUA_ADAT_RX_EN
.. doxygendefine:: ADAT_RX_INDEX .. doxygendefine:: ADAT_RX_INDEX
PDM Microphones PDM Microphones
............... ^^^^^^^^^^^^^^^
.. doxygendefine:: XUA_NUM_PDM_MICS .. doxygendefine:: XUA_NUM_PDM_MICS
DFU DFU
... ^^^
.. doxygendefine:: XUA_DFU_EN .. doxygendefine:: XUA_DFU_EN
.. .. doxygendefine:: DFU_FLASH_DEVICE .. .. doxygendefine:: DFU_FLASH_DEVICE
HID HID
... ^^^
.. doxygendefine:: HID_CONTROLS .. doxygendefine:: HID_CONTROLS
CODEC Interface CODEC Interface
............... ^^^^^^^^^^^^^^^
.. doxygendefine:: CODEC_MASTER .. doxygendefine:: CODEC_MASTER
USB Device Configuration USB Device Configuration
~~~~~~~~~~~~~~~~~~~~~~~~ ------------------------
.. doxygendefine:: VENDOR_STR .. doxygendefine:: VENDOR_STR
.. doxygendefine:: VENDOR_ID .. doxygendefine:: VENDOR_ID
@@ -108,10 +108,10 @@ USB Device Configuration
Stream Formats Stream Formats
~~~~~~~~~~~~~~ --------------
Output/Playback Output/Playback
............... ^^^^^^^^^^^^^^^
.. doxygendefine:: OUTPUT_FORMAT_COUNT .. doxygendefine:: OUTPUT_FORMAT_COUNT
@@ -132,7 +132,8 @@ Output/Playback
.. doxygendefine:: STREAM_FORMAT_OUTPUT_3_DATAFORMAT .. doxygendefine:: STREAM_FORMAT_OUTPUT_3_DATAFORMAT
Input/Recording Input/Recording
............... ^^^^^^^^^^^^^^^
.. doxygendefine:: INPUT_FORMAT_COUNT .. doxygendefine:: INPUT_FORMAT_COUNT
.. doxygendefine:: STREAM_FORMAT_INPUT_1_RESOLUTION_BITS .. doxygendefine:: STREAM_FORMAT_INPUT_1_RESOLUTION_BITS
@@ -144,7 +145,7 @@ Input/Recording
.. doxygendefine:: STREAM_FORMAT_INPUT_1_DATAFORMAT .. doxygendefine:: STREAM_FORMAT_INPUT_1_DATAFORMAT
Volume Control Volume Control
~~~~~~~~~~~~~~ --------------
.. doxygendefine:: OUTPUT_VOLUME_CONTROL .. doxygendefine:: OUTPUT_VOLUME_CONTROL
.. doxygendefine:: INPUT_VOLUME_CONTROL .. doxygendefine:: INPUT_VOLUME_CONTROL
@@ -152,8 +153,8 @@ Volume Control
.. doxygendefine:: MAX_VOLUME .. doxygendefine:: MAX_VOLUME
.. doxygendefine:: VOLUME_RES .. doxygendefine:: VOLUME_RES
Mixing Parameters Mixing
~~~~~~~~~~~~~~~~~ ------
.. doxygendefine:: MIXER .. doxygendefine:: MIXER
.. doxygendefine:: MAX_MIX_COUNT .. doxygendefine:: MAX_MIX_COUNT
@@ -163,8 +164,7 @@ Mixing Parameters
.. doxygendefine:: VOLUME_RES_MIXER .. doxygendefine:: VOLUME_RES_MIXER
Power Power
~~~~~ -----
.. doxygendefine:: SELF_POWERED .. doxygendefine:: XUA_POWERMODE
.. doxygendefine:: BMAX_POWER

View File

@@ -1,10 +1,10 @@
Required User Function Definitions Required User Function Definitions
---------------------------------- ==================================
The following functions need to be defined by an application using the XMOS USB Audio framework. The following functions need to be defined by an application using the XMOS USB Audio framework.
External Audio Hardware Configuration Functions External Audio Hardware Configuration Functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -----------------------------------------------
.. c:function:: void AudioHwInit(chanend ?c_codec) .. c:function:: void AudioHwInit(chanend ?c_codec)
@@ -38,7 +38,7 @@ External Audio Hardware Configuration Functions
Audio Streaming Functions Audio Streaming Functions
~~~~~~~~~~~~~~~~~~~~~~~~~ -------------------------
The following functions can be optionally used by the design. They can be useful for mute lines etc. The following functions can be optionally used by the design. They can be useful for mute lines etc.
@@ -52,7 +52,7 @@ The following functions can be optionally used by the design. They can be useful
This function is called when the audio stream from device to host stops. This function is called when the audio stream from device to host stops.
Host Active Host Active
~~~~~~~~~~~ -----------
The following function can be used to signal that the device is connected to a valid host. The following function can be used to signal that the device is connected to a valid host.
@@ -64,10 +64,11 @@ This is called on a change in state.
HID Controls HID Controls
~~~~~~~~~~~~ ------------
The following function is called when the device wishes to read physical user input (buttons etc). The following function is called when the device wishes to read physical user input (buttons etc).
.. c:function:: void UserReadHIDButtons(unsigned char hidData[]) .. c:function:: void UserReadHIDButtons(unsigned char hidData[])
:param hidData: The function should write relevant HID bits into this array. The bit ordering and functionality is defined by the HID report descriptor used. :param hidData: The function should write relevant HID bits into this array. The bit ordering and functionality is defined by the HID report descriptor used.

View File

@@ -2,7 +2,7 @@
.. _usb_audio_sec_architecture: .. _usb_audio_sec_architecture:
Software Architecture Software Architecture
--------------------- *********************
This section describes the required software architecture of a USB Audio device implemented using `lib_xua`, its dependencies and other supporting libraries. This section describes the required software architecture of a USB Audio device implemented using `lib_xua`, its dependencies and other supporting libraries.

View File

@@ -1,8 +1,8 @@
Features & Options Additional Features
------------------ *******************
The previous section describes the use of core functionality contained within ``lib_xua`` The previous chapter describes the use of core functionality contained within ``lib_xua``
This seciton details enabling additional features with supported external dependencies, for example, This seciton details enabling additional features with supported external dependencies, for example,
``lib_xua`` can provide S/PDIF output though the used of ``lib_spdif`` ``lib_xua`` can provide S/PDIF output though the used of ``lib_spdif``

View File

@@ -1,6 +1,6 @@
S/PDIF Receive S/PDIF Receive
~~~~~~~~~~~~~~ ==============
``lib_xua`` supports the development of devices with S/PDIF receive functionality through the use of ``lib_xua`` supports the development of devices with S/PDIF receive functionality through the use of
``lib_spdif``. The XMOS S/PDIF receiver runs in a single core and supports rates up to 192kHz. ``lib_spdif``. The XMOS S/PDIF receiver runs in a single core and supports rates up to 192kHz.

View File

@@ -1,6 +1,6 @@
S/PDIF Transmit S/PDIF Transmit
~~~~~~~~~~~~~~~ ===============
``lib_xua`` supports the development of devices with S/PDIF transmit functionality through the use of ``lib_xua`` supports the development of devices with S/PDIF transmit functionality through the use of
``lib_spdif``. The XMOS S/PDIF transmitter runs in a single core and supports rates up to 192kHz. ``lib_spdif``. The XMOS S/PDIF transmitter runs in a single core and supports rates up to 192kHz.

View File

@@ -2,7 +2,7 @@
|appendix| |appendix|
Known Issues Known Issues
------------ ************
- Quad-SPI DFU will corrupt the factory image with tools version < 14.0.4 due to an issue with libquadflash - Quad-SPI DFU will corrupt the factory image with tools version < 14.0.4 due to an issue with libquadflash

View File

@@ -2,8 +2,7 @@
.. include:: ../../../README.rst .. include:: ../../../README.rst
About This Document About This Document
~~~~~~~~~~~~~~~~~~~ ===================
This document describes the structure of ``lib_xua``, its use and resources required. It also covers some implementation detail. This document describes the structure of ``lib_xua``, its use and resources required. It also covers some implementation detail.
@@ -18,7 +17,7 @@ the XMOS tool chain and XC language.
Options <opt> Options <opt>
Advanced Usage <using_adv> Advanced Usage <using_adv>
Additional Features <feat> Additional Features <feat>
Software Detail <sw> Implementation Detail <sw>
API <api> API <api>
Known Issues <issues> Known Issues <issues>

View File

@@ -2,7 +2,7 @@
.. _sec_options: .. _sec_options:
Options Options
------- *******
This section describes key options of ``lib_xua``. These are typically controlled using build time defines. This section describes key options of ``lib_xua``. These are typically controlled using build time defines.
Where something must be defined, it is recommended this is done in `xua_conf.h` but could also be done in the application Makefile. Where something must be defined, it is recommended this is done in `xua_conf.h` but could also be done in the application Makefile.

View File

@@ -1,7 +1,7 @@
|newpage| |newpage|
USB Audio Class Version USB Audio Class Version
~~~~~~~~~~~~~~~~~~~~~~~ =======================
The codebase supports USB Audio Class versions 1.0 and 2.0. The codebase supports USB Audio Class versions 1.0 and 2.0.
@@ -15,22 +15,22 @@ Additional improvements, amongst others, include:
- Extensive support for interrupts to inform the host about dynamic changes that occur to different entities such as Clocks etc - Extensive support for interrupts to inform the host about dynamic changes that occur to different entities such as Clocks etc
Driver Support Driver Support
.............. --------------
Audio Class 1.0 Audio Class 1.0
+++++++++++++++ ^^^^^^^^^^^^^^^
Audio Class 1.0 is fully supported in Apple OSX. Audio Class 1.0 is fully supported in all modern Microsoft Windows operating systems (i.e. Windows XP and later). Audio Class 1.0 is fully supported in Apple OSX. Audio Class 1.0 is fully supported in all modern Microsoft Windows operating systems (i.e. Windows XP and later).
Audio Class 2.0 Audio Class 2.0
+++++++++++++++ ^^^^^^^^^^^^^^^
Audio Class 2.0 is fully supported in Apple OSX since version 10.6.4. Starting with Windows 10, release 1703, a USB Audio 2.0 driver is shipped with Windows. Audio Class 2.0 is fully supported in Apple OSX since version 10.6.4. Starting with Windows 10, release 1703, a USB Audio 2.0 driver is shipped with Windows.
Third party Windows drivers are also available, however, documentation of these is beyond the scope of this document, please contact XMOS for further details. Third party Windows drivers are also available, however, documentation of these is beyond the scope of this document, please contact XMOS for further details.
Audio Class 1.0 Mode and Fall-back Audio Class 1.0 Mode and Fall-back
.................................. ----------------------------------
The default for XMOS USB Audio applications is to run as a high-speed Audio Class 2.0 The default for XMOS USB Audio applications is to run as a high-speed Audio Class 2.0
device. However, some products may prefer to run in Audio Class 1.0 mode, this is normally to device. However, some products may prefer to run in Audio Class 1.0 mode, this is normally to
@@ -64,7 +64,7 @@ Due to bandwidth limitations of full-speed USB the following sample-frequency re
Related Defines Related Defines
................ ---------------
:ref:`opt_audio_class_defines` descibes the defines that effect audio class selection. :ref:`opt_audio_class_defines` descibes the defines that effect audio class selection.

View File

@@ -3,7 +3,7 @@
.. _sec_opt_audio_formats: .. _sec_opt_audio_formats:
Audio Stream Formats Audio Stream Formats
~~~~~~~~~~~~~~~~~~~~ ====================
The design currently supports up to three different stream formats for playback, selectable at The design currently supports up to three different stream formats for playback, selectable at
run time. This is implemented using standard Alternative Settings to the Audio Streaming interfaces. run time. This is implemented using standard Alternative Settings to the Audio Streaming interfaces.
@@ -32,7 +32,7 @@ By default the design exposes two sets of Alternative Settings for the playback
24-bit playback. When DSD is enabled an additional (32-bit) alternative is exposed. 24-bit playback. When DSD is enabled an additional (32-bit) alternative is exposed.
Audio Subslot Audio Subslot
............. -------------
An audio subslot holds a single audio sample. See `USB Device Class Definition for Audio Data Formats An audio subslot holds a single audio sample. See `USB Device Class Definition for Audio Data Formats
<http://www.usb.org/developers/devclass_docs/Audio2.0_final.zip>`_ for full details. <http://www.usb.org/developers/devclass_docs/Audio2.0_final.zip>`_ for full details.
@@ -73,7 +73,7 @@ Values other than 4 may be used for the following reasons:
Audio Sample Resolution Audio Sample Resolution
....................... -----------------------
An audio sample is represented using a number of bits (`bBitResolution`) less than or equal to the number An audio sample is represented using a number of bits (`bBitResolution`) less than or equal to the number
of total bits available in the audio subslot i.e. `bBitResolution` <= `bSubslotSize` * 8). The design of total bits available in the audio subslot i.e. `bBitResolution` <= `bSubslotSize` * 8). The design
@@ -99,7 +99,7 @@ supports values 16, 24 and 32.
Audio Format Audio Format
............ ------------
The design supports two audio formats, PCM and, when "Native" DSD is enabled, Direct Stream Digital (DSD). The design supports two audio formats, PCM and, when "Native" DSD is enabled, Direct Stream Digital (DSD).
A DSD capable DAC is required for the latter. A DSD capable DAC is required for the latter.
@@ -122,7 +122,6 @@ The following options are supported:
* UAC_FORMAT_TYPEI_PCM * UAC_FORMAT_TYPEI_PCM
.. note:: .. note::
Currently DSD is only supported on the output/playback stream Currently DSD is only supported on the output/playback stream

View File

@@ -1,7 +1,7 @@
|newpage| |newpage|
Channel Counts and Sample Rates Channel Counts and Sample Rates
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ===============================
The codebase is fully configurable in relation to channel counts and sample rates. The codebase is fully configurable in relation to channel counts and sample rates.
Practical limitations of these are normally based on USB packet size restrictions and I/O Practical limitations of these are normally based on USB packet size restrictions and I/O

View File

@@ -1,7 +1,7 @@
|newpage| |newpage|
Direct Stream Digital (DSD) Direct Stream Digital (DSD)
~~~~~~~~~~~~~~~~~~~~~~~~~~~ ===========================
Direct Stream Digital (DSD) is used for digitally encoding audio signals on Super Audio CDs (SACD). Direct Stream Digital (DSD) is used for digitally encoding audio signals on Super Audio CDs (SACD).
It uses pulse-density modulation (PDM) encoding. It uses pulse-density modulation (PDM) encoding.
@@ -48,7 +48,7 @@ If only DoP functionality is desired the Native implementation can be disabled w
DSD over PCM (DoP) DSD over PCM (DoP)
.................. ------------------
DoP support follows the method described in the `DoP Open Standard 1.1 DoP support follows the method described in the `DoP Open Standard 1.1
<http://dsd-guide.com/sites/default/files/white-papers/DoP_openStandard_1v1.pdf>`_. <http://dsd-guide.com/sites/default/files/white-papers/DoP_openStandard_1v1.pdf>`_.
@@ -81,14 +81,14 @@ of rate.
DoP requires bit-perfect transmission - therefore any audio/volume processing will break the stream. DoP requires bit-perfect transmission - therefore any audio/volume processing will break the stream.
"Native" vs DoP "Native" vs DoP
~~~~~~~~~~~~~~~ ---------------
Since the DoP specification requires header bytes this eats into the data bandwidth. The "Native" implementation Since the DoP specification requires header bytes this eats into the data bandwidth. The "Native" implementation
has no such overhead and can therefore transfer the same DSD rate and half the effective PCM rate of DoP. has no such overhead and can therefore transfer the same DSD rate and half the effective PCM rate of DoP.
Such a property may be desired when upporting DSD128 without exposing a 352.8kHz PCM rate, for example. Such a property may be desired when upporting DSD128 without exposing a 352.8kHz PCM rate, for example.
Ports Ports
..... -----
The codebase expects 1-bit ports to be defined in the application XN file for the DSD data and The codebase expects 1-bit ports to be defined in the application XN file for the DSD data and
clock lines for example:: clock lines for example::

View File

@@ -1,7 +1,7 @@
|newpage| |newpage|
I2S/TDM I2S/TDM
~~~~~~~ =======
I2S/TDM is typically fundamental to most products and is built into the ``XUA_AudioHub()`` core. I2S/TDM is typically fundamental to most products and is built into the ``XUA_AudioHub()`` core.

View File

@@ -1,7 +1,7 @@
|newpage| |newpage|
Code Location Code Location
~~~~~~~~~~~~~ =============
When designing a system there is a choice as to which hardware resources to use for each interface. When designing a system there is a choice as to which hardware resources to use for each interface.
In a multi-tile system the codebase needs to be informed as to which tiles to use for these hardware In a multi-tile system the codebase needs to be informed as to which tiles to use for these hardware

View File

@@ -2,7 +2,7 @@
|newpage| |newpage|
MIDI MIDI
~~~~ ====
The codebase supports MIDI input/output over USB as per `Universal Serial Bus Device Class Definition for MIDI Devices <https://www.usb.org/sites/default/files/midi10.pdf>`_. The codebase supports MIDI input/output over USB as per `Universal Serial Bus Device Class Definition for MIDI Devices <https://www.usb.org/sites/default/files/midi10.pdf>`_.

View File

@@ -1,7 +1,7 @@
|newpage| |newpage|
Mixer Mixer
~~~~~ =====
The codebase supports audio mixing functionality with highly flexible routing options. The codebase supports audio mixing functionality with highly flexible routing options.

View File

@@ -1,11 +1,10 @@
|newpage| |newpage|
Other Options Other Options
~~~~~~~~~~~~~ =============
There are a few other, lesser used, options available. There are a few other, lesser used, options available.
.. _opt_other_defines: .. _opt_other_defines:
.. list-table:: Other defines .. list-table:: Other defines

View File

@@ -1,7 +1,7 @@
|newpage| |newpage|
PDM Microphones PDM Microphones
~~~~~~~~~~~~~~~ ===============
The codebase supports input from up to 8 PDM microphones. The codebase supports input from up to 8 PDM microphones.

View File

@@ -1,7 +1,7 @@
|newpage| |newpage|
S/PDIF Receive S/PDIF Receive
~~~~~~~~~~~~~~ ==============
The codebase supports a single, stereo, S/PDIF receiver. This can be input via 75 Ω coaxial or optical fibre. The codebase supports a single, stereo, S/PDIF receiver. This can be input via 75 Ω coaxial or optical fibre.
In order to provide S/PDIF functionality ``lib_xua`` uses ``lib_spdif`` (https://www.github.com/xmos/lib_spdif). In order to provide S/PDIF functionality ``lib_xua`` uses ``lib_spdif`` (https://www.github.com/xmos/lib_spdif).

View File

@@ -1,7 +1,7 @@
|newpage| |newpage|
S/PDIF Transmit S/PDIF Transmit
~~~~~~~~~~~~~~~ ===============
The codebase supports a single, stereo, S/PDIF transmitter. This can be output over 75 Ω coaxial or optical fibre. The codebase supports a single, stereo, S/PDIF transmitter. This can be output over 75 Ω coaxial or optical fibre.
In order to provide S/PDIF transmit functionality ``lib_xua`` uses ``lib_spdif`` (https://www.github.com/xmos/lib_spdif). In order to provide S/PDIF transmit functionality ``lib_xua`` uses ``lib_spdif`` (https://www.github.com/xmos/lib_spdif).

View File

@@ -1,6 +1,6 @@
Strings and ID's Strings and ID's
~~~~~~~~~~~~~~~~ ================
The codebase includes various strings and ID's that should be customised to match the product requirements. The codebase includes various strings and ID's that should be customised to match the product requirements.
These are listed in ::ref:`opt_strings_defines`. These are listed in ::ref:`opt_strings_defines`.

View File

@@ -2,7 +2,7 @@
|newpage| |newpage|
Synchronisation Synchronisation
~~~~~~~~~~~~~~~ ===============
The codebase supports "Synchronous" and "Asynchronous" modes for USB transfer as defined by the The codebase supports "Synchronous" and "Asynchronous" modes for USB transfer as defined by the
USB specification(s). USB specification(s).

View File

@@ -1,6 +1,5 @@
Overview Overview
-------- ********
.. table:: .. table::
:class: vertical-borders :class: vertical-borders
@@ -75,5 +74,3 @@ Overview
| Reference code is maintained by XMOS Limited. | | Reference code is maintained by XMOS Limited. |
+-------------------------------------------------------------------------------------------------------------------------------+ +-------------------------------------------------------------------------------------------------------------------------------+

View File

@@ -1,8 +1,8 @@
Implementation Detail Implementation Detail
--------------------- *********************
This section examines the implementation of the various components that make up ``lib_xua``. It also examines the integration of dependencies and supporting libraries. This chapter examines the implementation of the various components that make up ``lib_xua``. It also examines the integration of dependencies and supporting libraries.
.. toctree:: .. toctree::
@@ -10,8 +10,8 @@ This section examines the implementation of the various components that make up
sw_ep0 sw_ep0
sw_xud sw_xud
sw_clocking sw_clocking
sw_spdif
sw_mixer sw_mixer
sw_spdif
sw_spdif_rx sw_spdif_rx
sw_adat_rx sw_adat_rx
sw_midi sw_midi

View File

@@ -1,7 +1,7 @@
|newpage| |newpage|
ADAT Receive ADAT Receive
------------ ============
The ADAT receive component receives up to eight channels of audio at a sample rate The ADAT receive component receives up to eight channels of audio at a sample rate
of 44.1kHz or 48kHz. The API for calling the receiver functions is of 44.1kHz or 48kHz. The API for calling the receiver functions is

View File

@@ -3,7 +3,7 @@
.. _usb_audio_sec_audio: .. _usb_audio_sec_audio:
Audio Hub Audio Hub
......... =========
The Audio Hub task performs many functions. It receives and transmits samples from/to the Decoupler The Audio Hub task performs many functions. It receives and transmits samples from/to the Decoupler
or Mixer core over a channel. or Mixer core over a channel.
@@ -96,9 +96,8 @@ Two master clock frequencies to support 44.1kHz and 48kHz audio frequencies (e.g
and 12.288/24.576MHz respectively). This master clock input is then provided to the external audio and 12.288/24.576MHz respectively). This master clock input is then provided to the external audio
hardware and the xCORE device. hardware and the xCORE device.
Port Configuration (xCORE Master) Port Configuration (xCORE Master)
+++++++++++++++++++++++++++++++++ ---------------------------------
The default software configuration is xCORE is I2S master. That is, the XMOS device provides the BCLK and LRCLK signals to the external audio hardware The default software configuration is xCORE is I2S master. That is, the XMOS device provides the BCLK and LRCLK signals to the external audio hardware
@@ -143,7 +142,7 @@ before the data (as required by the I2S standard) and alternates between high an
and right channels of audio. and right channels of audio.
Changing Audio Sample Frequency Changing Audio Sample Frequency
+++++++++++++++++++++++++++++++ -------------------------------
.. _usb_audio_sec_chang-audio-sample: .. _usb_audio_sec_chang-audio-sample:
@@ -159,5 +158,3 @@ functions.
Once this is complete, the I2S/TDM interface (i.e. the main loop in AudioHub) is restarted at the new frequency. Once this is complete, the I2S/TDM interface (i.e. the main loop in AudioHub) is restarted at the new frequency.

View File

@@ -4,7 +4,7 @@
.. _usb_audio_sec_clock_recovery: .. _usb_audio_sec_clock_recovery:
External Clock Recovery (Clock Gen) External Clock Recovery (Clock Gen)
----------------------------------- ===================================
To provide an audio master clock an application may use selectable oscillators, clock To provide an audio master clock an application may use selectable oscillators, clock
generation IC or, in the case of xCORE.ai devices, integrated secondary PLL, to generate fixed generation IC or, in the case of xCORE.ai devices, integrated secondary PLL, to generate fixed
@@ -14,7 +14,7 @@ It may also use an external PLL/Clock Multiplier to generate a master clock base
the xCORE. the xCORE.
Using an external PLL/Clock Multiplier allows an Asynchronous mode design to lock to an external Using an external PLL/Clock Multiplier allows an Asynchronous mode design to lock to an external
clock source from a digital stream (e.g. S/PDIF or ADAT input). The code-base supports the Cirrus clock source from a digital stream (e.g. S/PDIF or ADAT input). The codebase supports the Cirrus
Logic CS2100 device for this purpose. Other devices may be supported via code modification. Logic CS2100 device for this purpose. Other devices may be supported via code modification.
.. note:: .. note::

View File

@@ -3,14 +3,14 @@
.. _usb_audio_sec_usb: .. _usb_audio_sec_usb:
Endpoint 0: Management and Control Endpoint 0: Management and Control
.................................. ==================================
All USB devices must support a mandatory control endpoint, Endpoint 0. This controls the management tasks of the USB device. All USB devices must support a mandatory control endpoint, Endpoint 0. This controls the management tasks of the USB device.
These tasks can be generally split into enumeration, audio configuration and firmware upgrade requests. These tasks can be generally split into enumeration, audio configuration and firmware upgrade requests.
Enumeration Enumeration
~~~~~~~~~~~ -----------
When the device is first attached to a host, enumeration occurs. This process involves the host interrogating the device as to its functionality. The device does this by presenting several interfaces to the host via a set of descriptors. When the device is first attached to a host, enumeration occurs. This process involves the host interrogating the device as to its functionality. The device does this by presenting several interfaces to the host via a set of descriptors.
@@ -41,12 +41,13 @@ The function may also return ``XUD_RES_RST`` if a bus-reset has been issued onto
Since the ``USB_StandardRequests()`` function STALLs an unknown request, the endpoint 0 code must first parse the ``USB_SetupPacket_t`` structure to handle device specific requests and then call ``USB_StandardRequests()`` as required. Since the ``USB_StandardRequests()`` function STALLs an unknown request, the endpoint 0 code must first parse the ``USB_SetupPacket_t`` structure to handle device specific requests and then call ``USB_StandardRequests()`` as required.
Over-riding Standard Requests Over-riding Standard Requests
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -----------------------------
The USB Audio design "over-rides" some of the requests handled by ``USB_StandardRequests()``, for example it uses the SET_INTERFACE request to indicate if the host is streaming audio to the device. In this case the setup packet is parsed, the relevant action taken, the ``USB_StandardRequests()`` is still called to handle the response to the host. The USB Audio design "over-rides" some of the requests handled by ``USB_StandardRequests()``, for example it uses the SET_INTERFACE request to indicate if the host is streaming audio to the device. In this case the setup packet is parsed, the relevant action taken, the ``USB_StandardRequests()`` is still called to handle the response to the host.
Class Requests Class Requests
~~~~~~~~~~~~~~ --------------
Before making the call to ``USB_StandardRequests()`` the setup packet is parsed for Class requests. These are handled in functions such as ``AudioClassRequests_1()``, ``AudioClassRequests_2``, ``DFUDeviceRequests()`` etc depending on the type of request. Before making the call to ``USB_StandardRequests()`` the setup packet is parsed for Class requests. These are handled in functions such as ``AudioClassRequests_1()``, ``AudioClassRequests_2``, ``DFUDeviceRequests()`` etc depending on the type of request.
Any device specific requests are handled - in this case Audio Class, MIDI class, DFU requests etc. Any device specific requests are handled - in this case Audio Class, MIDI class, DFU requests etc.
@@ -54,7 +55,7 @@ Any device specific requests are handled - in this case Audio Class, MIDI class,
Some of the common Audio Class requests and their associated behaviour will now be examined. Some of the common Audio Class requests and their associated behaviour will now be examined.
Audio Requests Audio Requests
++++++++++++++ ^^^^^^^^^^^^^^
When the host issues an audio request (e.g. sample rate or volume change), it sends a command to Endpoint 0. Like all requests this is returned from ``USB_GetSetupPacket()``. After some parsing (namely as Class Request to an Audio Interface) the request is handled by either the ``AudioClassRequests_1()`` or ``AudioClassRequests_2()`` function (based on whether the device is running in Audio Class 1.0 or 2.0 mode). When the host issues an audio request (e.g. sample rate or volume change), it sends a command to Endpoint 0. Like all requests this is returned from ``USB_GetSetupPacket()``. After some parsing (namely as Class Request to an Audio Interface) the request is handled by either the ``AudioClassRequests_1()`` or ``AudioClassRequests_2()`` function (based on whether the device is running in Audio Class 1.0 or 2.0 mode).
@@ -63,7 +64,7 @@ Note, Audio Class 1.0 Sample rate changes are send to the relevant endpoint, rat
The ``AudioClassRequests_X()`` functions further parses the request in order to ascertain the correct audio operation to execute. The ``AudioClassRequests_X()`` functions further parses the request in order to ascertain the correct audio operation to execute.
Audio Request: Set Sample Rate Audio Request: Set Sample Rate
++++++++++++++++++++++++++++++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The ``AudioClassRequests_2()`` function parses the passed ``USB_SetupPacket_t`` structure for a ``CUR`` request of type ``SAM_FREQ_CONTROL`` to a Clock Unit in the devices topology (as described in the devices descriptors). The ``AudioClassRequests_2()`` function parses the passed ``USB_SetupPacket_t`` structure for a ``CUR`` request of type ``SAM_FREQ_CONTROL`` to a Clock Unit in the devices topology (as described in the devices descriptors).
@@ -72,7 +73,7 @@ The new sample frequency is extracted and passed via channel to the rest of the
.. _usb_audio_sec_audio-requ-volume: .. _usb_audio_sec_audio-requ-volume:
Audio Request: Volume Control Audio Request: Volume Control
+++++++++++++++++++++++++++++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When the host requests a volume change, it When the host requests a volume change, it
sends an audio interface request to Endpoint 0. An array is sends an audio interface request to Endpoint 0. An array is
@@ -102,10 +103,10 @@ to the mixer to change the volume. Mixer commands
are described in :ref:`usb_audio_sec_mixer`. are described in :ref:`usb_audio_sec_mixer`.
Audio Endpoints (Endpoint Buffer and Decoupler) Audio Endpoints (Endpoint Buffer and Decoupler)
............................................... ===============================================
Endpoint Buffer Endpoint Buffer
~~~~~~~~~~~~~~~ ---------------
All endpoints other that Endpoint 0 are handled in one core. This All endpoints other that Endpoint 0 are handled in one core. This
core is implemented in the file ``ep_buffer.xc``. This core communicates directly with the XUD library. core is implemented in the file ``ep_buffer.xc``. This core communicates directly with the XUD library.
@@ -114,7 +115,7 @@ The USB buffer core is also responsible for feedback calculation based on USB St
(SOF) notification and reads from the port counter of a port connected to the master clock. (SOF) notification and reads from the port counter of a port connected to the master clock.
Decouple Decouple
~~~~~~~~ --------
The decoupler supplies the USB buffering core with buffers to The decoupler supplies the USB buffering core with buffers to
transmit/receive audio data to/from the host. It marshals these buffers into transmit/receive audio data to/from the host. It marshals these buffers into
@@ -125,7 +126,7 @@ matching the audio rate to the USB packet rate). The decoupler is
implemented in the file ``decouple.xc``. implemented in the file ``decouple.xc``.
Audio Buffering Scheme Audio Buffering Scheme
~~~~~~~~~~~~~~~~~~~~~~~ ----------------------
This scheme is executed by co-operation between the buffering This scheme is executed by co-operation between the buffering
core, the decouple core and the XUD library. core, the decouple core and the XUD library.
@@ -133,7 +134,6 @@ core, the decouple core and the XUD library.
For data going from the device to the host the following scheme is For data going from the device to the host the following scheme is
used: used:
#. The Decouple core receives samples from the Audio Hub core and #. The Decouple core receives samples from the Audio Hub core and
puts them into a FIFO. This FIFO is split into packets when data is puts them into a FIFO. This FIFO is split into packets when data is
entered into it. Packets are stored in a format consisting of their entered into it. Packets are stored in a format consisting of their
@@ -152,11 +152,9 @@ used:
Decouple core that the buffer has been sent and the Decouple core Decouple core that the buffer has been sent and the Decouple core
moves the read pointer of the FIFO. moves the read pointer of the FIFO.
For data going from the host to the device the following scheme is For data going from the host to the device the following scheme is
used: used:
#. The Decouple core passes a pointer to the Endpoint Buffer core #. The Decouple core passes a pointer to the Endpoint Buffer core
pointing into a FIFO of data and signals to the XUD library that pointing into a FIFO of data and signals to the XUD library that
the Endpoint Buffer core is ready to receive. the Endpoint Buffer core is ready to receive.
@@ -171,9 +169,8 @@ used:
#. Upon request from the Audio Hub core, the Decouple core sends #. Upon request from the Audio Hub core, the Decouple core sends
samples to the Audio Hub core by reading samples out of the FIFO. samples to the Audio Hub core by reading samples out of the FIFO.
Decoupler/Audio Core interaction Decoupler/Audio Core interaction
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --------------------------------
To meet timing requirements of the audio system (i.e Audio Hub/Mixer), the Decoupler To meet timing requirements of the audio system (i.e Audio Hub/Mixer), the Decoupler
core must respond to requests from the audio system to core must respond to requests from the audio system to
@@ -200,7 +197,6 @@ in channel count sized chunks (i.e. ``NUM_USB_CHAN_OUT`` and
The complete communication scheme is shown in the table below (for non sample The complete communication scheme is shown in the table below (for non sample
frequency change case): frequency change case):
.. table:: Decouple/Audio System Channel Communication .. table:: Decouple/Audio System Channel Communication
+-----------------+-----------------+-----------------------------------------+ +-----------------+-----------------+-----------------------------------------+
@@ -242,7 +238,7 @@ frequency change case):
(this is especially advantageous in the DSD over PCM (DoP) case) (this is especially advantageous in the DSD over PCM (DoP) case)
Asynchronous Feedback Asynchronous Feedback
+++++++++++++++++++++ ---------------------
When built to operate in Asynchronous mode the device uses a feedback endpoint to report the rate at which When built to operate in Asynchronous mode the device uses a feedback endpoint to report the rate at which
audio is output/input to/from external audio interfaces/devices. This feedback is in accordance with audio is output/input to/from external audio interfaces/devices. This feedback is in accordance with
@@ -265,7 +261,7 @@ sent to the host. In practice this an explicit feedback endpoint is normally use
in Microsoft Windows operating systems (see ``UAC_FORCE_FEEDBACK_EP``). in Microsoft Windows operating systems (see ``UAC_FORCE_FEEDBACK_EP``).
USB Rate Control USB Rate Control
++++++++++++++++ ----------------
.. _usb_audio_sec_usb-rate-control: .. _usb_audio_sec_usb-rate-control:

View File

@@ -1,5 +1,5 @@
Audio Controls via Human Interface Device (HID) Audio Controls via Human Interface Device (HID)
------------------------------------------------ ===============================================
The design supports simple audio controls such as play/pause, volume up/down etc via the USB Human The design supports simple audio controls such as play/pause, volume up/down etc via the USB Human
Interface Device Class Specification. Interface Device Class Specification.

View File

@@ -1,11 +1,11 @@
|newpage| |newpage|
MIDI MIDI
---- ====
The MIDI core implements a 31250 baud UART for both input and output. On receiving 32-bit USB MIDI events The MIDI core implements a 31250 baud UART for both input and output. On receiving 32=bit USB MIDI events
from the Endpoint Buffer core, it parses these and translates them to 8-bit MIDI messages which are sent from the Endpoint Buffer core, it parses these and translates them to 8-bit MIDI messages which are sent
over UART. Similarly, incoming 8-bit MIDI messages are aggregated into 32-bit USB-MIDI events and over UART. Similarly, incoming 8-bit MIDI messages are aggregated into 32-bit USB MIDI events and
passed on to the Endpoint Buffer core. The MIDI core is implemented in the file ``usb_midi.xc``. passed on to the Endpoint Buffer core. The MIDI core is implemented in the file ``usb_midi.xc``.
The Endpoint Buffer core implements the two Bulk endpoints (one In and one Out) as well as interacting The Endpoint Buffer core implements the two Bulk endpoints (one In and one Out) as well as interacting

View File

@@ -87,6 +87,9 @@ intended as an example of how you might add mixer control to your own control ap
intended to be exposed to end users. intended to be exposed to end users.
For details, consult the README file in the host_usb_mixer_control directory. For details, consult the README file in the host_usb_mixer_control directory.
A list of arguments can also be seen with::
$ ./xmos_mixer --help
The main requirements of this control utility are to The main requirements of this control utility are to
@@ -102,78 +105,147 @@ The main requirements of this control utility are to
functionality to their end users. functionality to their end users.
Whilst using the XMOS Host control example application, consider the example of setting the Whilst using the XMOS Host control example application, consider the example of setting the
mixer to perform a loop-back from analogue inputs 1 and 2 to analogue outputs 1 and 2. mixer to perform a loop-back from analogue inputs 1 & 2 to analogue outputs 1 & 2.
Firstly consider the inputs to the mixer. The following will displays which channels are mapped .. note::
to which mixer inputs::
./xmos_mixer --display-aud-channel-map 0 The command outputs shown are examples; the actual output will depend on the mixer configuration.
The following command will displays which channels could possibly be mapped to mixer inputs. Notice The following will show the index for each device output along with which channel is currently mapped to it.
that analogue inputs 1 and 2 are on mixer inputs 10 and 11:: In this example the analogue outputs 1 & 2 are 0 & 1 respectively::
./xmos_mixer --display-aud-channel-map-sources 0 $ ./xmos_mixer --display-aud-channel-map
Now examine the audio output mapping using the following command:: Audio Output Channel Map
------------------------
0 (DEVICE OUT - Analogue 1) source is 0 (DAW OUT - Analogue 1)
1 (DEVICE OUT - Analogue 2) source is 1 (DAW OUT - Analogue 2)
2 (DEVICE OUT - SPDIF 1) source is 2 (DAW OUT - SPDIF 1)
3 (DEVICE OUT - SPDIF 2) source is 3 (DAW OUT - SPDIF 2)
$ _
./xmos_mixer --display-aud-channel-map 0 The DAW Output Map can be seen with::
This displays which channels are mapped to which outputs. By default all $ ./xmos_mixer --display-daw-channel-map
of these bypass the mixer. We can also see what all the possible
mappings are with the following command::
./xmos_mixer --display-aud-channel-map-sources 0 DAW Output To Host Channel Map
------------------------
0 (DEVICE IN - Analogue 1) source is 4 (DEVICE IN - Analogue 1)
1 (DEVICE IN - Analogue 2) source is 5 (DEVICE IN - Analogue 2)
$ _
We will now map the first two mixer outputs to physical outputs 1 and 2:: .. note::
./xmos_mixer --set-aud-channel-map 0 26 In both cases, by default, these bypass the mixer.
./xmos_mixer --set-aud-channel-map 1 27
The following command will list the channels which can be mapped to the device outputs from the
Audio Output Channel Map. Note that, in this example, analogue inputs 1 & 2 are source 4 & 5 and
Mix 1 & 2 are source 6 & 7::
$ ./xmos_mixer --display-aud-channel-map-sources
Audio Output Channel Map Source List
------------------------------------
0 (DAW OUT - Analogue 1)
1 (DAW OUT - Analogue 2)
2 (DAW OUT - SPDIF 1)
3 (DAW OUT - SPDIF 2)
4 (DEVICE IN - Analogue 1)
5 (DEVICE IN - Analogue 2)
6 (MIX - Mix 1)
7 (MIX - Mix 2)
$ _
Using the indices from the previous commands, we will now re-map the first two mixer channels (Mix 1 & Mix 2) to device outputs 1 & 2::
$ ./xmos_mixer --set-aud-channel-map 0 6
$ ./xmos_mixer --set-aud-channel-map 1 7
$ _
You can confirm the effect of this by re-checking the map:: You can confirm the effect of this by re-checking the map::
./xmos_mixer --display-aud-channel-map 0 $ ./xmos_mixer --display-aud-channel-map
This now derives analogue outputs 1 and 2 from the mixer, rather than directly from USB. However, Audio Output Channel Map
since the mixer is still mapped to pass the USB channels through to the outputs there will be no ------------------------
0 (DEVICE OUT - Analogue 1) source is 6 (MIX - Mix 1)
1 (DEVICE OUT - Analogue 2) source is 7 (MIX - Mix 2)
2 (DEVICE OUT - SPDIF 1) source is 2 (DAW OUT - SPDIF 1)
3 (DEVICE OUT - SPDIF 2) source is 3 (DAW OUT - SPDIF 2)
$ _
This now derives analogue outputs 1 & 2 from the mixer, rather than directly from USB. However,
since the mixer is mapped, by default, to just pass the USB channels through to the outputs there will be no
functional change. functional change.
The mixer nodes need to be individually set. They can be displayed
.. note::
The USB audio reference design has only one unit so the mixer_id argument should always be 0.
The mixer nodes need to be individually set. The nodes in mixer_id 0 can be displayed
with the following command:: with the following command::
./xmos_mixer --display-mixer-nodes 0 $ ./xmos_mixer --display-mixer-nodes 0
To get the audio from the analogue inputs to outputs 1 and 2, nodes 80 Mixer Values (0)
and 89 need to be set:: ----------------
./xmos_mixer --set-value 0 80 0 Mixer outputs
./xmos_mixer --set-value 0 89 0 1 2
DAW - Analogue 1 0:[0000.000] 1:[ -inf ]
DAW - Analogue 2 2:[ -inf ] 3:[0000.000]
DAW - SPDIF 1 4:[ -inf ] 5:[ -inf ]
DAW - SPDIF 2 6:[ -inf ] 7:[ -inf ]
AUD - Analogue 1 8:[ -inf ] 9:[ -inf ]
AUD - Analogue 2 10:[ -inf ] 11:[ -inf ]
$ _
With mixer outputs 1 & 2 mapped to device outputs analogue 1 & 2; to get the audio from the analogue inputs to device
outputs mixer_id 0 node 8 and node 11 need to be set to 0db::
$ ./xmos_mixer --set-value 0 8 0
$ ./xmos_mixer --set-value 0 11 0
$ _
At the same time, the original mixer outputs can be muted:: At the same time, the original mixer outputs can be muted::
./xmos_mixer --set-value 0 0 -inf $ ./xmos_mixer --set-value 0 0 -inf
./xmos_mixer --set-value 0 9 -inf $ ./xmos_mixer --set-value 0 3 -inf
$ _
Now audio inputs on analogue 1/2 should be heard on outputs 1/2. Now audio inputs on analogue 1 and 2 should be heard on outputs 1 and 2 respectively.
As mentioned above, the flexibility of the mixer is such that there will be multiple ways to create As mentioned above, the flexibility of the mixer is such that there will be multiple ways to create
a particular mix. Another option to create the same routing would be to change the mixer sources a particular mix. Another option to create the same routing would be to change the mixer sources
such that mixer 1/2 outputs come from the analogue inputs. such that mixer outputs 1 and 2 come from the analogue inputs 1 and 2.
To demonstrate this, firstly undo the changes above (or simply reset the device):: To demonstrate this, firstly undo the changes above (or simply reset the device)::
./xmos_mixer --set-value 0 80 -inf $ ./xmos_mixer --set-value 0 8 -inf
./xmos_mixer --set-value 0 89 -inf $ ./xmos_mixer --set-value 0 11 -inf
./xmos_mixer --set-value 0 0 0 $ ./xmos_mixer --set-value 0 0 0
./xmos_mixer --set-value 0 9 0 $ ./xmos_mixer --set-value 0 3 0
$ _
The mixer should now have the default values. The sources for mixer 1/2 can now be changed:: The mixer should now have the default values. The sources for mixer 0 output 1 and 2 can now be changed
using indices from the Audio Output Channel Map Source List::
./xmos_mixer --set-mixer-source 0 0 10 $ ./xmos_mixer --set-mixer-source 0 0 4
./xmos_mixer --set-mixer-source 0 1 11
Set mixer(0) input 0 to device input 4 (AUD - Analogue 1)
$ ./xmos_mixer --set-mixer-source 0 1 5
Set mixer(0) input 1 to device input 5 (AUD - Analogue 2)
$ _
If you re-run the following command then the first column now has "AUD - Analogue 1 and 2" rather If you re-run the following command then the first column now has "AUD - Analogue 1 and 2" rather
than "DAW (Digital Audio Workstation i.e. the host) - Analogue 1 and 2" confirming the new mapping. than "DAW (Digital Audio Workstation i.e. the host) - Analogue 1 and 2" confirming the new mapping.
Again, by playing audio into analogue inputs 1/2 this can be heard looped through to analogue outputs 1/2:: Again, by playing audio into analogue inputs 1/2 this can be heard looped through to analogue outputs 1/2::
./xmos_mixer --display-mixer-nodes 0 $ ./xmos_mixer --display-mixer-nodes 0

View File

@@ -1,7 +1,7 @@
|newpage| |newpage|
PDM Microphones PDM Microphones
--------------- ===============
The XMOS USB Audio Reference Design firmware is capable of integrating with PDM microphones. The XMOS USB Audio Reference Design firmware is capable of integrating with PDM microphones.
The PDM stream from the microphones is converted to PCM and output to the host via USB. The PDM stream from the microphones is converted to PCM and output to the host via USB.
@@ -46,9 +46,8 @@ After the decimation to the output sample-rate various other steps take place e.
and compensation etc. Please refer to the documentation provided with ``lib_mic_array`` for further and compensation etc. Please refer to the documentation provided with ``lib_mic_array`` for further
implementation detail and complete feature set. implementation detail and complete feature set.
PDM Microphone Hardware Characteristics PDM Microphone Hardware Characteristics
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ---------------------------------------
The PDM microphones require a *clock input* and provide the PDM signal on a *data output*. All of The PDM microphones require a *clock input* and provide the PDM signal on a *data output*. All of
the PDM microphones must share the same clock signal (buffered on the PCB as appropriate), and the PDM microphones must share the same clock signal (buffered on the PCB as appropriate), and
@@ -78,7 +77,7 @@ divide down 12.288 MHz master clock. Or, if clock accuracy is not important, the
reference can be divided down to provide an approximate clock. reference can be divided down to provide an approximate clock.
Usage & Integration Usage & Integration
~~~~~~~~~~~~~~~~~~~ -------------------
A PDM microphone wrapper is called from ``main()`` and takes one channel argument connecting it to the rest of the system: A PDM microphone wrapper is called from ``main()`` and takes one channel argument connecting it to the rest of the system:
@@ -113,3 +112,4 @@ sends the frame of audio back over this channel.
Note, it is assumed that the system shares a global master-clock, therefore no additional buffering or rate-matching/conversion Note, it is assumed that the system shares a global master-clock, therefore no additional buffering or rate-matching/conversion
is required. is required.

View File

@@ -3,7 +3,7 @@
.. _usb_audio_sec_resource_usage: .. _usb_audio_sec_resource_usage:
Resource Usage Resource Usage
-------------- ==============
The following table details the resource usage of each component of the reference design software. The following table details the resource usage of each component of the reference design software.
Note, memory usage is approximate and varies based on device used, compiler settings etc. Note, memory usage is approximate and varies based on device used, compiler settings etc.
@@ -50,3 +50,4 @@ Note, memory usage is approximate and varies based on device used, compiler sett
Unlike other interfaces, since the USB PHY is internal the USB ports are a fixed set of ports Unlike other interfaces, since the USB PHY is internal the USB ports are a fixed set of ports
and cannot be modified. See ``lib_xud`` documentation for full details. and cannot be modified. See ``lib_xud`` documentation for full details.

View File

@@ -2,7 +2,7 @@
|newpage| |newpage|
S/PDIF Transmit S/PDIF Transmit
............... ===============
``lib_xua`` supports the development of devices with S/PDIF transmit throught the use of ``lib_spdif``. ``lib_xua`` supports the development of devices with S/PDIF transmit throught the use of ``lib_spdif``.
The XMOS S/SPDIF transmitter component runs in a single core and supports sample-rates upto 192kHz. The XMOS S/SPDIF transmitter component runs in a single core and supports sample-rates upto 192kHz.
@@ -21,7 +21,7 @@ bits) and transmitted in biphase-mark encoding (BMC) with respect to an *externa
Note that a minor change to the ``SpdifTransmitPortConfig`` function would enable *internal* master Note that a minor change to the ``SpdifTransmitPortConfig`` function would enable *internal* master
clock generation (e.g. when clock source is already locked to desired audio clock). clock generation (e.g. when clock source is already locked to desired audio clock).
.. list-table:: S/PDIF Capabilities .. list-table:: S/PDIF Capabilities
* - **Sample frequencies** * - **Sample frequencies**
- 44.1, 48, 88.2, 96, 176.4, 192 kHz - 44.1, 48, 88.2, 96, 176.4, 192 kHz
@@ -31,7 +31,7 @@ clock generation (e.g. when clock source is already locked to desired audio cloc
- ``lib_spdif`` - ``lib_spdif``
Clocking Clocking
++++++++ --------
.. only:: latex .. only:: latex
@@ -54,7 +54,7 @@ This resamples the master clock to its clock domain (oscillator), which introduc
A typical jitter-reduction scheme is an external D-type flip-flop clocked from the master clock (as shown in the preceding diagram). A typical jitter-reduction scheme is an external D-type flip-flop clocked from the master clock (as shown in the preceding diagram).
Usage Usage
+++++ -----
The interface to the S/PDIF transmitter core is via a normal channel with streaming built-ins The interface to the S/PDIF transmitter core is via a normal channel with streaming built-ins
(``outuint``, ``inuint``). Data format should be 24-bit left-aligned in a 32-bit word: ``0x12345600`` (``outuint``, ``inuint``). Data format should be 24-bit left-aligned in a 32-bit word: ``0x12345600``
@@ -84,9 +84,8 @@ The following protocol is used on the channel:
This communication is wrapped up in the API functions provided by ``lib_spdif``. This communication is wrapped up in the API functions provided by ``lib_spdif``.
Output Stream Structure
Output stream structure -----------------------
+++++++++++++++++++++++
The stream is composed of words with the following structure shown in The stream is composed of words with the following structure shown in
:ref:`usb_audio_spdif_stream_structure`. The channel status bits are :ref:`usb_audio_spdif_stream_structure`. The channel status bits are

View File

@@ -1,7 +1,7 @@
|newpage| |newpage|
S/PDIF Receive S/PDIF Receive
--------------- ==============
XMOS devices can support S/PDIF receive up to 192kHz - see ``lib_spdif`` for full specifications. XMOS devices can support S/PDIF receive up to 192kHz - see ``lib_spdif`` for full specifications.
@@ -45,7 +45,7 @@ The tag has one of three values:
See S/PDIF, IEC 60958-3:2006, specification for further details on format, user bits etc. See S/PDIF, IEC 60958-3:2006, specification for further details on format, user bits etc.
Usage and Integration Usage and Integration
+++++++++++++++++++++ ---------------------
Since S/PDIF is a digital steam the devices master clock must be synchronised to it. This is typically Since S/PDIF is a digital steam the devices master clock must be synchronised to it. This is typically
done with an external device. See `Clock Recovery` (:ref:`usb_audio_sec_clock_recovery`). done with an external device. See `Clock Recovery` (:ref:`usb_audio_sec_clock_recovery`).

View File

@@ -2,7 +2,7 @@
|newpage| |newpage|
XMOS USB Device (XUD) Library XMOS USB Device (XUD) Library
............................. =============================
All low level communication with the USB host is handled by the XMOS USB Device (XUD) library - `lib_xud` All low level communication with the USB host is handled by the XMOS USB Device (XUD) library - `lib_xud`

View File

@@ -1,12 +1,12 @@
Basic Usage Basic Usage
----------- ***********
This sections describes the basic usage of `lib_xua` and provides a guide on how to program USB Audio Devices. This sections describes the basic usage of `lib_xua` and provides a guide on how to program USB Audio Devices.
Library Structure Library Structure
~~~~~~~~~~~~~~~~~ =================
The code is split into several directories. The code is split into several directories.
@@ -26,7 +26,7 @@ Note, the midi and dfu directories are potential candidates for separate libs in
Using in a Project Using in a Project
~~~~~~~~~~~~~~~~~~ ==================
All `lib_xua` functions can be accessed via the ``xua.h`` header file:: All `lib_xua` functions can be accessed via the ``xua.h`` header file::
@@ -39,7 +39,7 @@ It is also required to add ``lib_xua`` to the ``USED_MODULES`` field of your app
.. _sec_basic_usage_codeless: .. _sec_basic_usage_codeless:
"Codeless" Programming Model "Codeless" Programming Model
~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ============================
Whilst it is possible to code a USB Audio device using the building blocks provided by `lib_xua` Whilst it is possible to code a USB Audio device using the building blocks provided by `lib_xua`
it is realised that this might not be desirable for many classes of customers or products. it is realised that this might not be desirable for many classes of customers or products.
@@ -71,7 +71,7 @@ set ``EXCLUDE_USB_AUDIO_MAIN`` to 1 in the application makefile or ``xua_conf.h`
::ref:`sec_advanced_usage`. ::ref:`sec_advanced_usage`.
Configuring lib_xua Configuring lib_xua
~~~~~~~~~~~~~~~~~~~ ===================
Configuration of the various build time options of ``lib_xua`` is done via the optional header `xua_conf.h`. Configuration of the various build time options of ``lib_xua`` is done via the optional header `xua_conf.h`.
To allow the build scripts to locate this file it should reside somewhere in the application `src` directory. To allow the build scripts to locate this file it should reside somewhere in the application `src` directory.
@@ -93,9 +93,8 @@ should continue to include `xua.h` as previously directed. A simple example is s
#endif #endif
User Functions User Functions
~~~~~~~~~~~~~~ ==============
To enable custom functionality, such as configuring external audio hardware, bespoke behaviour on To enable custom functionality, such as configuring external audio hardware, bespoke behaviour on
stream start/stop etc, various functions can be overridden by the user. (see ::ref:`sec_api` for stream start/stop etc, various functions can be overridden by the user. (see ::ref:`sec_api` for

View File

@@ -1,7 +1,7 @@
.. _sec_advanced_usage: .. _sec_advanced_usage:
Advanced Usage Advanced Usage
-------------- **************
Whilst it is possible to program USB Audio devices using ``lib_xua`` by only setting defines Whilst it is possible to program USB Audio devices using ``lib_xua`` by only setting defines
(see :ref:`sec_basic_usage_codeless`) some developers may want to code a USB Audio device from (see :ref:`sec_basic_usage_codeless`) some developers may want to code a USB Audio device from

View File

@@ -1,5 +1,5 @@
Running the Core Components Running the Core Components
~~~~~~~~~~~~~~~~~~~~~~~~~~~ ===========================
In their most basic form the core components can be run as follows:: In their most basic form the core components can be run as follows::

View File

@@ -1,5 +1,5 @@
Core Hardware Resources Core Hardware Resources
~~~~~~~~~~~~~~~~~~~~~~~ =======================
The user must declare and initialise relevant hardware resources (globally) and pass them to the The user must declare and initialise relevant hardware resources (globally) and pass them to the
relevant function of `lib_xua`. relevant function of `lib_xua`.

View File

@@ -1,7 +1,7 @@
|newpage| |newpage|
I2S/TDM I2S/TDM
~~~~~~~ =======
I2S/TDM is typically fundamental to most products and is built into the ``XUA_AudioHub()`` core. I2S/TDM is typically fundamental to most products and is built into the ``XUA_AudioHub()`` core.

View File

@@ -1,7 +1,7 @@
|newpage| |newpage|
Mixer Mixer
~~~~~ =====
Since the mixer has no I/O the instantiation is straight forward. Communication wises, the mixer cores are inserted Since the mixer has no I/O the instantiation is straight forward. Communication wises, the mixer cores are inserted
between the `AudioHub` and Buffering core(s) between the `AudioHub` and Buffering core(s)

View File

@@ -1,19 +1,19 @@
VERSION = 3.3.0 VERSION = 3.4.0
DEBUG ?= 0 DEBUG ?= 0
ifeq ($(DEBUG),1) ifeq ($(DEBUG),1)
DEBUG_FLAGS = -g -DXASSERT_ENABLE_ASSERTIONS_DECOUPLE=1 DEBUG_FLAGS = -g -DXASSERT_ENABLE_ASSERTIONS=1 -DXASSERT_ENABLE_DEBUG=1 -DXASSERT_ENABLE_LINE_NUMBERS=1
else else
DEBUG_FLAGS = -DXASSERT_DISABLE_ASSERTIONS_DECOUPLE=1 DEBUG_FLAGS = -DXASSERT_ENABLE_ASSERTIONS=0 -DXASSERT_ENABLE_DEBUG=0 -DXASSERT_ENABLE_LINE_NUMBERS=0
endif endif
DEPENDENT_MODULES = lib_locks(>=2.1.0) \ DEPENDENT_MODULES = lib_locks(>=2.1.0) \
lib_logging(>=3.1.1) \ lib_logging(>=3.1.1) \
lib_mic_array(>=4.5.0) \ lib_mic_array(>=4.5.0) \
lib_spdif(>=4.1.0) \ lib_spdif(>=4.2.1) \
lib_xassert(>=4.1.0) \ lib_xassert(>=4.1.0) \
lib_xud(>=2.2.1) \ lib_xud(>=2.2.2) \
lib_adat(>=1.0.0) lib_adat(>=1.0.0)
MODULE_XCC_FLAGS = $(XCC_FLAGS) \ MODULE_XCC_FLAGS = $(XCC_FLAGS) \

View File

@@ -38,7 +38,6 @@ void InitPorts_master(unsigned divide, buffered _XUA_CLK_DIR port:32 p_lrclk, bu
} }
#endif #endif
#pragma xta endpoint "divide_1"
unsigned tmp; unsigned tmp;
p_lrclk <: 0 @ tmp; p_lrclk <: 0 @ tmp;
tmp += 100; tmp += 100;

View File

@@ -1,4 +1,4 @@
// Copyright 2011-2022 XMOS LIMITED. // Copyright 2011-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1. // This Software is subject to the terms of the XMOS Public Licence: Version 1.
/** /**
* @file xua_audiohub.xc * @file xua_audiohub.xc
@@ -43,12 +43,12 @@
#define MAX(x,y) ((x)>(y) ? (x) : (y)) #define MAX(x,y) ((x)>(y) ? (x) : (y))
static unsigned samplesOut[MAX(NUM_USB_CHAN_OUT, I2S_CHANS_DAC)]; unsigned samplesOut[MAX(NUM_USB_CHAN_OUT, I2S_CHANS_DAC)];
/* Two buffers for ADC data to allow for DAC and ADC I2S ports being offset */ /* Two buffers for ADC data to allow for DAC and ADC I2S ports being offset */
#define IN_CHAN_COUNT (I2S_CHANS_ADC + XUA_NUM_PDM_MICS + (8*XUA_ADAT_RX_EN) + (2*XUA_SPDIF_RX_EN)) #define IN_CHAN_COUNT (I2S_CHANS_ADC + XUA_NUM_PDM_MICS + (8*XUA_ADAT_RX_EN) + (2*XUA_SPDIF_RX_EN))
static unsigned samplesIn[2][MAX(NUM_USB_CHAN_IN, IN_CHAN_COUNT)]; unsigned samplesIn[2][MAX(NUM_USB_CHAN_IN, IN_CHAN_COUNT)];
#ifdef XTA_TIMING_AUDIO #ifdef XTA_TIMING_AUDIO
#pragma xta command "add exclusion received_command" #pragma xta command "add exclusion received_command"
@@ -89,69 +89,7 @@ unsigned dsdMode = DSD_MODE_OFF;
#if (XUA_ADAT_TX_EN) #if (XUA_ADAT_TX_EN)
#include "audiohub_adat.h" #include "audiohub_adat.h"
#endif #endif
#include "xua_audiohub_st.h"
#pragma unsafe arrays
static inline unsigned DoSampleTransfer(chanend ?c_out, const int readBuffNo, const unsigned underflowWord)
{
if(XUA_USB_EN)
{
outuint(c_out, underflowWord);
/* Check for sample freq change (or other command) or new samples from mixer*/
if(testct(c_out))
{
unsigned command = inct(c_out);
#ifndef CODEC_MASTER
if(dsdMode == DSD_MODE_OFF)
{
#if (I2S_CHANS_ADC != 0 || I2S_CHANS_DAC != 0)
/* Set clocks low */
p_lrclk <: 0;
p_bclk <: 0;
#endif
}
else
{
#if(DSD_CHANS_DAC != 0)
/* DSD Clock might not be shared with lrclk or bclk... */
p_dsd_clk <: 0;
#endif
}
#endif
#if (DSD_CHANS_DAC > 0)
if(dsdMode == DSD_MODE_DOP)
dsdMode = DSD_MODE_OFF;
#endif
return command;
}
else
{
#if NUM_USB_CHAN_OUT > 0
#pragma loop unroll
for(int i = 0; i < NUM_USB_CHAN_OUT; i++)
{
int tmp = inuint(c_out);
samplesOut[i] = tmp;
}
#else
inuint(c_out);
#endif
UserBufferManagement(samplesOut, samplesIn[readBuffNo]);
#if NUM_USB_CHAN_IN > 0
#pragma loop unroll
for(int i = 0; i < NUM_USB_CHAN_IN; i++)
{
outuint(c_out, samplesIn[readBuffNo][i]);
}
#endif
}
}
else
UserBufferManagement(samplesOut, samplesIn[readBuffNo]);
return 0;
}
static inline int HandleSampleClock(int frameCount, buffered _XUA_CLK_DIR port:32 p_lrclk) static inline int HandleSampleClock(int frameCount, buffered _XUA_CLK_DIR port:32 p_lrclk)
{ {
@@ -443,9 +381,8 @@ unsigned static AudioHub_MainLoop(chanend ?c_out, chanend ?c_spd_out
outuint(c_dig_rx, 0); outuint(c_dig_rx, 0);
#endif #endif
#if (XUA_SPDIF_TX_EN) && (NUM_USB_CHAN_OUT > 0) #if (XUA_SPDIF_TX_EN) && (NUM_USB_CHAN_OUT > 0)
outuint(c_spd_out, samplesOut[SPDIF_TX_INDEX]); /* Forward sample to S/PDIF Tx thread */ outuint(c_spd_out, samplesOut[SPDIF_TX_INDEX]); /* Forward samples to S/PDIF Tx thread */
unsigned sample = samplesOut[SPDIF_TX_INDEX + 1]; outuint(c_spd_out, samplesOut[SPDIF_TX_INDEX + 1]);
outuint(c_spd_out, sample); /* Forward sample to S/PDIF Tx thread */
#endif #endif
#if (XUA_NUM_PDM_MICS > 0) #if (XUA_NUM_PDM_MICS > 0)

View File

@@ -0,0 +1,66 @@
// Copyright 2011-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#pragma unsafe arrays
static inline unsigned DoSampleTransfer(chanend ?c_out, const int readBuffNo, const unsigned underflowWord)
{
if(XUA_USB_EN)
{
outuint(c_out, underflowWord);
/* Check for sample freq change (or other command) or new samples from mixer*/
if(testct(c_out))
{
unsigned command = inct(c_out);
#ifndef CODEC_MASTER
if(dsdMode == DSD_MODE_OFF)
{
#if (I2S_CHANS_ADC != 0 || I2S_CHANS_DAC != 0)
/* Set clocks low */
p_lrclk <: 0;
p_bclk <: 0;
#endif
}
else
{
#if(DSD_CHANS_DAC != 0)
/* DSD Clock might not be shared with lrclk or bclk... */
p_dsd_clk <: 0;
#endif
}
#endif
#if (DSD_CHANS_DAC > 0)
if(dsdMode == DSD_MODE_DOP)
dsdMode = DSD_MODE_OFF;
#endif
return command;
}
else
{
#if NUM_USB_CHAN_OUT > 0
#pragma loop unroll
for(int i = 0; i < NUM_USB_CHAN_OUT; i++)
{
int tmp = inuint(c_out);
samplesOut[i] = tmp;
}
#else
inuint(c_out);
#endif
UserBufferManagement(samplesOut, samplesIn[readBuffNo]);
#if NUM_USB_CHAN_IN > 0
#pragma loop unroll
for(int i = 0; i < NUM_USB_CHAN_IN; i++)
{
outuint(c_out, samplesIn[readBuffNo][i]);
}
#endif
}
}
else
UserBufferManagement(samplesOut, samplesIn[readBuffNo]);
return 0;
}

View File

@@ -1,4 +1,4 @@
// Copyright 2011-2022 XMOS LIMITED. // Copyright 2011-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1. // This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include "xua.h" #include "xua.h"
@@ -58,15 +58,16 @@
#define IN_BUFFER_PREFILL (MAX(MAX_DEVICE_AUD_PACKET_SIZE_IN_HS, MAX_DEVICE_AUD_PACKET_SIZE_IN_FS)*2) #define IN_BUFFER_PREFILL (MAX(MAX_DEVICE_AUD_PACKET_SIZE_IN_HS, MAX_DEVICE_AUD_PACKET_SIZE_IN_FS)*2)
/* Volume and mute tables */ /* Volume and mute tables */
#if !defined(OUT_VOLUME_IN_MIXER) && (OUTPUT_VOLUME_CONTROL == 1) #if (OUT_VOLUME_IN_MIXER == 0) && (OUTPUT_VOLUME_CONTROL == 1)
unsigned int multOut[NUM_USB_CHAN_OUT + 1]; unsigned int multOut[NUM_USB_CHAN_OUT + 1];
static xc_ptr p_multOut; static xc_ptr p_multOut;
#endif #endif
#if !defined(IN_VOLUME_IN_MIXER) && (INPUT_VOLUME_CONTROL == 1) #if (IN_VOLUME_IN_MIXER == 0) && (INPUT_VOLUME_CONTROL == 1)
unsigned int multIn[NUM_USB_CHAN_IN + 1]; unsigned int multIn[NUM_USB_CHAN_IN + 1];
static xc_ptr p_multIn; static xc_ptr p_multIn;
#endif #endif
/* Default to something sensible but the following are setup at stream start (unless UAC1 only..) */
#if (AUDIO_CLASS == 2) #if (AUDIO_CLASS == 2)
int g_numUsbChan_In = NUM_USB_CHAN_IN; /* Number of channels to/from the USB bus - initialised to HS for UAC2.0 */ int g_numUsbChan_In = NUM_USB_CHAN_IN; /* Number of channels to/from the USB bus - initialised to HS for UAC2.0 */
int g_numUsbChan_Out = NUM_USB_CHAN_OUT; int g_numUsbChan_Out = NUM_USB_CHAN_OUT;
@@ -143,7 +144,66 @@ unsigned unpackData = 0;
unsigned packState = 0; unsigned packState = 0;
unsigned packData = 0; unsigned packData = 0;
/* Default to something sensible but the following are setup at stream start (unless UAC1 only..) */ static inline void SendSamples4(chanend c_mix_out)
{
/* Doing this checking allows us to unroll */
if(g_numUsbChan_Out == NUM_USB_CHAN_OUT)
{
/* Buffering not underflow condition send out some samples...*/
#pragma loop unroll
for(int i = 0; i < NUM_USB_CHAN_OUT; i++)
{
int sample;
int mult;
int h;
unsigned l;
read_via_xc_ptr(sample, g_aud_from_host_rdptr);
g_aud_from_host_rdptr+=4;
#if (OUTPUT_VOLUME_CONTROL == 1) && (!OUT_VOLUME_IN_MIXER)
asm volatile("ldw %0, %1[%2]":"=r"(mult):"r"(p_multOut),"r"(i));
{h, l} = macs(mult, sample, 0, 0);
h <<= 3;
#if (STREAM_FORMAT_OUTPUT_RESOLUTION_32BIT_USED == 1)
h |= (l >>29) & 0x7; // Note: This step is not required if we assume sample depth is 24bit (rather than 32bit)
// Note: We need all 32bits for Native DSD
#endif
outuint(c_mix_out, h);
#else
outuint(c_mix_out, sample);
#endif
}
}
else
{
#pragma loop unroll
for(int i = 0; i < NUM_USB_CHAN_OUT_FS; i++)
{
int sample;
int mult;
int h;
unsigned l;
read_via_xc_ptr(sample, g_aud_from_host_rdptr);
g_aud_from_host_rdptr+=4;
#if (OUTPUT_VOLUME_CONTROL == 1) && (!OUT_VOLUME_IN_MIXER)
asm volatile("ldw %0, %1[%2]":"=r"(mult):"r"(p_multOut),"r"(i));
{h, l} = macs(mult, sample, 0, 0);
h <<= 3;
#if (STREAM_FORMAT_OUTPUT_RESOLUTION_32BIT_USED == 1)
h |= (l >>29) & 0x7; // Note: This step is not required if we assume sample depth is 24bit (rather than 32bit)
// Note: We need all 32bits for Native DSD
#endif
outuint(c_mix_out, h);
#else
outuint(c_mix_out, sample);
#endif
}
}
}
#pragma select handler #pragma select handler
#pragma unsafe arrays #pragma unsafe arrays
@@ -206,7 +266,7 @@ __builtin_unreachable();
g_aud_from_host_rdptr+=2; g_aud_from_host_rdptr+=2;
sample <<= 16; sample <<= 16;
#if (OUTPUT_VOLUME_CONTROL == 1) && !defined(OUT_VOLUME_IN_MIXER) #if (OUTPUT_VOLUME_CONTROL == 1) && (!OUT_VOLUME_IN_MIXER)
asm volatile("ldw %0, %1[%2]":"=r"(mult):"r"(p_multOut),"r"(i)); asm volatile("ldw %0, %1[%2]":"=r"(mult):"r"(p_multOut),"r"(i));
{h, l} = macs(mult, sample, 0, 0); {h, l} = macs(mult, sample, 0, 0);
/* Note, in 2 byte subslot mode - ignore lower result of macs */ /* Note, in 2 byte subslot mode - ignore lower result of macs */
@@ -223,41 +283,17 @@ __builtin_unreachable();
__builtin_unreachable(); __builtin_unreachable();
#endif #endif
/* Buffering not underflow condition send out some samples...*/ /* Buffering not underflow condition send out some samples...*/
for(int i = 0; i < g_numUsbChan_Out; i++) SendSamples4(c_mix_out);
{
#pragma xta endpoint "mixer_request"
int sample;
int mult;
int h;
unsigned l;
read_via_xc_ptr(sample, g_aud_from_host_rdptr);
g_aud_from_host_rdptr+=4;
#if (OUTPUT_VOLUME_CONTROL == 1) && !defined(OUT_VOLUME_IN_MIXER)
asm volatile("ldw %0, %1[%2]":"=r"(mult):"r"(p_multOut),"r"(i));
{h, l} = macs(mult, sample, 0, 0);
h <<= 3;
#if (STREAM_FORMAT_OUTPUT_RESOLUTION_32BIT_USED == 1)
h |= (l >>29)& 0x7; // Note: This step is not required if we assume sample depth is 24bit (rather than 32bit)
// Note: We need all 32bits for Native DSD
#endif
outuint(c_mix_out, h);
#else
outuint(c_mix_out, sample);
#endif
}
break; break;
case 3: case 3:
#if (STREAM_FORMAT_OUTPUT_SUBSLOT_3_USED == 0) #if (STREAM_FORMAT_OUTPUT_SUBSLOT_3_USED == 0)
__builtin_unreachable(); __builtin_unreachable();
#endif #endif
/* Buffering not underflow condition send out some samples...*/ /* Note, in this case the unpacking of data is more of an overhead than the loop overhead
* so we do not currently make attempts to unroll */
for(int i = 0; i < g_numUsbChan_Out; i++) for(int i = 0; i < g_numUsbChan_Out; i++)
{ {
#pragma xta endpoint "mixer_request"
int sample; int sample;
int mult; int mult;
int h; int h;
@@ -289,7 +325,7 @@ __builtin_unreachable();
} }
unpackState++; unpackState++;
#if (OUTPUT_VOLUME_CONTROL == 1) && !defined(OUT_VOLUME_IN_MIXER) #if (OUTPUT_VOLUME_CONTROL == 1) && (!OUT_VOLUME_IN_MIXER)
asm volatile("ldw %0, %1[%2]":"=r"(mult):"r"(p_multOut),"r"(i)); asm volatile("ldw %0, %1[%2]":"=r"(mult):"r"(p_multOut),"r"(i));
{h, l} = macs(mult, sample, 0, 0); {h, l} = macs(mult, sample, 0, 0);
h <<= 3; h <<= 3;
@@ -335,7 +371,7 @@ __builtin_unreachable();
/* Receive sample */ /* Receive sample */
int sample = inuint(c_mix_out); int sample = inuint(c_mix_out);
#if (INPUT_VOLUME_CONTROL == 1) #if (INPUT_VOLUME_CONTROL == 1)
#if !defined(IN_VOLUME_IN_MIXER) #if (!IN_VOLUME_IN_MIXER)
/* Apply volume */ /* Apply volume */
int mult; int mult;
int h; int h;
@@ -345,7 +381,7 @@ __builtin_unreachable();
sample = h << 3; sample = h << 3;
/* Note, in 2 byte sub slot - ignore lower bits of macs */ /* Note, in 2 byte sub slot - ignore lower bits of macs */
#elif defined(IN_VOLUME_IN_MIXER) && defined(IN_VOLUME_AFTER_MIX) #elif (IN_VOLUME_IN_MIXER) && defined(IN_VOLUME_AFTER_MIX)
sample = sample << 3; sample = sample << 3;
#endif #endif
#endif #endif
@@ -365,7 +401,7 @@ __builtin_unreachable();
/* Receive sample */ /* Receive sample */
int sample = inuint(c_mix_out); int sample = inuint(c_mix_out);
#if(INPUT_VOLUME_CONTROL == 1) #if(INPUT_VOLUME_CONTROL == 1)
#if !defined(IN_VOLUME_IN_MIXER) #if (!IN_VOLUME_IN_MIXER)
/* Apply volume */ /* Apply volume */
int mult; int mult;
int h; int h;
@@ -376,7 +412,7 @@ __builtin_unreachable();
#if (STREAM_FORMAT_INPUT_RESOLUTION_32BIT_USED == 1) #if (STREAM_FORMAT_INPUT_RESOLUTION_32BIT_USED == 1)
sample |= (l >> 29) & 0x7; // Note, this step is not required if we assume sample depth is 24 (rather than 32) sample |= (l >> 29) & 0x7; // Note, this step is not required if we assume sample depth is 24 (rather than 32)
#endif #endif
#elif defined(IN_VOLUME_IN_MIXER) && defined(IN_VOLUME_AFTER_MIX) #elif (IN_VOLUME_IN_MIXER) && (IN_VOLUME_AFTER_MIX)
sample = sample << 3; sample = sample << 3;
#endif #endif
#endif #endif
@@ -396,7 +432,7 @@ __builtin_unreachable();
{ {
/* Receive sample */ /* Receive sample */
int sample = inuint(c_mix_out); int sample = inuint(c_mix_out);
#if (INPUT_VOLUME_CONTROL) && !defined(IN_VOLUME_IN_MIXER) #if (INPUT_VOLUME_CONTROL) && (!IN_VOLUME_IN_MIXER)
/* Apply volume */ /* Apply volume */
int mult; int mult;
int h; int h;
@@ -588,6 +624,7 @@ __builtin_unreachable();
} }
} }
#if (NUM_USB_CHAN_IN > 0)
/* Mark Endpoint (IN) ready with an appropriately sized zero buffer */ /* Mark Endpoint (IN) ready with an appropriately sized zero buffer */
static inline void SetupZerosSendBuffer(XUD_ep aud_to_host_usb_ep, unsigned sampFreq, unsigned slotSize, static inline void SetupZerosSendBuffer(XUD_ep aud_to_host_usb_ep, unsigned sampFreq, unsigned slotSize,
xc_ptr aud_to_host_zeros) xc_ptr aud_to_host_zeros)
@@ -619,6 +656,7 @@ static inline void SetupZerosSendBuffer(XUD_ep aud_to_host_usb_ep, unsigned samp
XUD_SetReady_InPtr(aud_to_host_usb_ep, aud_to_host_zeros+4, mid); XUD_SetReady_InPtr(aud_to_host_usb_ep, aud_to_host_zeros+4, mid);
} }
#endif
#pragma unsafe arrays #pragma unsafe arrays
void XUA_Buffer_Decouple(chanend c_mix_out void XUA_Buffer_Decouple(chanend c_mix_out
@@ -638,10 +676,10 @@ void XUA_Buffer_Decouple(chanend c_mix_out
int t = array_to_xc_ptr(outAudioBuff); int t = array_to_xc_ptr(outAudioBuff);
#if !defined(OUT_VOLUME_IN_MIXER) && (OUTPUT_VOLUME_CONTROL == 1) #if (!OUT_VOLUME_IN_MIXER) && (OUTPUT_VOLUME_CONTROL == 1)
p_multOut = array_to_xc_ptr(multOut); p_multOut = array_to_xc_ptr(multOut);
#endif #endif
#if !defined(IN_VOLUME_IN_MIXER) && (INPUT_VOLUME_CONTROL == 1) #if (!IN_VOLUME_IN_MIXER) && (INPUT_VOLUME_CONTROL == 1)
p_multIn = array_to_xc_ptr(multIn); p_multIn = array_to_xc_ptr(multIn);
#endif #endif
@@ -668,14 +706,14 @@ void XUA_Buffer_Decouple(chanend c_mix_out
xc_ptr aud_to_host_zeros = t; xc_ptr aud_to_host_zeros = t;
/* Init vol mult tables */ /* Init vol mult tables */
#if !defined(OUT_VOLUME_IN_MIXER) && (OUTPUT_VOLUME_CONTROL == 1) #if (OUT_VOLUME_IN_MIXER == 0) && (OUTPUT_VOLUME_CONTROL == 1)
for (int i = 0; i < NUM_USB_CHAN_OUT + 1; i++) for (int i = 0; i < NUM_USB_CHAN_OUT + 1; i++)
{ {
asm volatile("stw %0, %1[%2]"::"r"(MAX_VOL),"r"(p_multOut),"r"(i)); asm volatile("stw %0, %1[%2]"::"r"(MAX_VOL),"r"(p_multOut),"r"(i));
} }
#endif #endif
#if !defined(IN_VOLUME_IN_MIXER) && (INPUT_VOLUME_CONTROL == 1) #if (IN_VOLUME_IN_MIXER == 0) && (INPUT_VOLUME_CONTROL == 1)
for (int i = 0; i < NUM_USB_CHAN_IN + 1; i++) for (int i = 0; i < NUM_USB_CHAN_IN + 1; i++)
{ {
asm volatile("stw %0, %1[%2]"::"r"(MAX_VOL),"r"(p_multIn),"r"(i)); asm volatile("stw %0, %1[%2]"::"r"(MAX_VOL),"r"(p_multIn),"r"(i));
@@ -760,8 +798,10 @@ void XUA_Buffer_Decouple(chanend c_mix_out
/* Set buffer to send back to zeros buffer */ /* Set buffer to send back to zeros buffer */
aud_to_host_buffer = aud_to_host_zeros; aud_to_host_buffer = aud_to_host_zeros;
#if (NUM_USB_CHAN_IN > 0)
/* Update size of zeros buffer (and sampsToWrite) */ /* Update size of zeros buffer (and sampsToWrite) */
SetupZerosSendBuffer(aud_to_host_usb_ep, sampFreq, g_curSubSlot_In, aud_to_host_zeros); SetupZerosSendBuffer(aud_to_host_usb_ep, sampFreq, g_curSubSlot_In, aud_to_host_zeros);
#endif
/* Reset OUT buffer state */ /* Reset OUT buffer state */
outUnderflow = 1; outUnderflow = 1;
@@ -815,8 +855,10 @@ void XUA_Buffer_Decouple(chanend c_mix_out
/* Set buffer back to zeros buffer */ /* Set buffer back to zeros buffer */
aud_to_host_buffer = aud_to_host_zeros; aud_to_host_buffer = aud_to_host_zeros;
#if (NUM_USB_CHAN_IN > 0)
/* Update size of zeros buffer (and sampsToWrite) */ /* Update size of zeros buffer (and sampsToWrite) */
SetupZerosSendBuffer(aud_to_host_usb_ep, sampFreq, g_curSubSlot_In, aud_to_host_zeros); SetupZerosSendBuffer(aud_to_host_usb_ep, sampFreq, g_curSubSlot_In, aud_to_host_zeros);
#endif
GET_SHARED_GLOBAL(usbSpeed, g_curUsbSpeed); GET_SHARED_GLOBAL(usbSpeed, g_curUsbSpeed);
if (usbSpeed == XUD_SPEED_HS) if (usbSpeed == XUD_SPEED_HS)

View File

@@ -1,4 +1,4 @@
// Copyright 2011-2022 XMOS LIMITED. // Copyright 2011-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1. // This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <xs1.h> #include <xs1.h>
@@ -13,14 +13,14 @@
#include "spdif.h" #include "spdif.h"
#endif #endif
#define LOCAL_CLOCK_INCREMENT 166667 #define LOCAL_CLOCK_INCREMENT (166667)
#define LOCAL_CLOCK_MARGIN 1666 #define LOCAL_CLOCK_MARGIN (1666)
#define MAX_SAMPLES 64 /* Must be power of 2 */ #define MAX_SAMPLES (64) /* Must be power of 2 */
#define MAX_SPDIF_SAMPLES (2 * MAX_SAMPLES) /* Must be power of 2 */ #define MAX_SPDIF_SAMPLES (2 * MAX_SAMPLES) /* Must be power of 2 */
#define MAX_ADAT_SAMPLES (8 * MAX_SAMPLES) /* Must be power of 2 */ #define MAX_ADAT_SAMPLES (8 * MAX_SAMPLES) /* Must be power of 2 */
#define SPDIF_FRAME_ERRORS_THRESH 40 #define SPDIF_FRAME_ERRORS_THRESH (40)
unsigned g_digData[10]; unsigned g_digData[10];
@@ -241,12 +241,7 @@ extern int samples_to_host_inputs_buff[NUM_USB_CHAN_IN];
int VendorAudCoreReqs(unsigned cmd, chanend c); int VendorAudCoreReqs(unsigned cmd, chanend c);
#pragma unsafe arrays #pragma unsafe arrays
//#if (AUDIO_IO_TILE == PLL_REF_TILE)
#if 0
void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, out port p, chanend c_dig_rx, chanend c_clk_ctl, chanend c_clk_int)
#else
void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interface pll_ref_if i_pll_ref, chanend c_dig_rx, chanend c_clk_ctl, chanend c_clk_int) void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interface pll_ref_if i_pll_ref, chanend c_dig_rx, chanend c_clk_ctl, chanend c_clk_int)
#endif
{ {
timer t_local; timer t_local;
unsigned timeNextEdge, timeLastEdge, timeNextClockDetection; unsigned timeNextEdge, timeLastEdge, timeNextClockDetection;
@@ -723,7 +718,7 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interfa
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) #if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
/* Mixer requests data */ /* AudioHub requests data */
case inuint_byref(c_dig_rx, tmp): case inuint_byref(c_dig_rx, tmp):
#if (XUA_SPDIF_RX_EN) #if (XUA_SPDIF_RX_EN)
if(spdifUnderflow) if(spdifUnderflow)

View File

@@ -1,4 +1,4 @@
// Copyright 2011-2022 XMOS LIMITED. // Copyright 2011-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1. // This Software is subject to the terms of the XMOS Public Licence: Version 1.
/** /**
* @brief Implements endpoint zero for an USB Audio 1.0/2.0 device * @brief Implements endpoint zero for an USB Audio 1.0/2.0 device
@@ -106,13 +106,17 @@ unsigned int mutesOut[NUM_USB_CHAN_OUT + 1];
int volsIn[NUM_USB_CHAN_IN + 1]; int volsIn[NUM_USB_CHAN_IN + 1];
unsigned int mutesIn[NUM_USB_CHAN_IN + 1]; unsigned int mutesIn[NUM_USB_CHAN_IN + 1];
#ifdef MIXER #if (MIXER)
unsigned char mixer1Crossbar[18]; short mixer1Weights[MIX_INPUTS * MAX_MIX_COUNT];
short mixer1Weights[18*8];
unsigned char channelMap[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT]; //unsigned char channelMap[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT];
/* Mapping of channels to output audio interfaces */
unsigned char channelMapAud[NUM_USB_CHAN_OUT]; unsigned char channelMapAud[NUM_USB_CHAN_OUT];
/* Mapping of channels to USB host */
unsigned char channelMapUsb[NUM_USB_CHAN_IN]; unsigned char channelMapUsb[NUM_USB_CHAN_IN];
/* Mapping of channels to Mixer(s) */
unsigned char mixSel[MAX_MIX_COUNT][MIX_INPUTS]; unsigned char mixSel[MAX_MIX_COUNT][MIX_INPUTS];
#endif #endif
@@ -262,6 +266,49 @@ void XUA_Endpoint0_setVendorId(unsigned short vid) {
#endif // AUDIO_CLASS == 1} #endif // AUDIO_CLASS == 1}
} }
#if (MIXER)
void InitLocalMixerState()
{
for (int i = 0; i < MIX_INPUTS * MAX_MIX_COUNT; i++)
{
mixer1Weights[i] = 0x8001; //-inf
}
/* Configure default connections */
// TODO this should be a loop using defines.
mixer1Weights[0] = 0;
mixer1Weights[9] = 0;
mixer1Weights[18] = 0;
mixer1Weights[27] = 0;
mixer1Weights[36] = 0;
mixer1Weights[45] = 0;
mixer1Weights[54] = 0;
mixer1Weights[63] = 0;
#if NUM_USB_CHAN_OUT > 0
/* Setup up audio output channel mapping */
for(int i = 0; i < NUM_USB_CHAN_OUT; i++)
{
channelMapAud[i] = i;
}
#endif
#if NUM_USB_CHAN_IN > 0
for(int i = 0; i < NUM_USB_CHAN_IN; i++)
{
channelMapUsb[i] = i + NUM_USB_CHAN_OUT;
}
#endif
/* Init mixer inputs */
for(int j = 0; j < MAX_MIX_COUNT; j++)
for(int i = 0; i < MIX_INPUTS; i++)
{
mixSel[j][i] = i;
}
}
#endif
void concatenateAndCopyStrings(char* string1, char* string2, char* string_buffer) { void concatenateAndCopyStrings(char* string1, char* string2, char* string_buffer) {
debug_printf("concatenateAndCopyStrings() for \"%s\" and \"%s\"\n", string1, string2); debug_printf("concatenateAndCopyStrings() for \"%s\" and \"%s\"\n", string1, string2);
@@ -408,75 +455,11 @@ void XUA_Endpoint0_init(chanend c_ep0_out, chanend c_ep0_in, NULLABLE_RESOURCE(c
XUA_Endpoint0_setStrTable(); XUA_Endpoint0_setStrTable();
#if 0
/* Dont need to init globals.. */
/* Init tables for volumes (+ 1 for master) */
for(int i = 0; i < NUM_USB_CHAN_OUT + 1; i++)
{
volsOut[i] = 0;
mutesOut[i] = 0;
}
for(int i = 0; i < NUM_USB_CHAN_IN + 1; i++)
{
volsIn[i] = 0;
mutesIn[i] = 0;
}
#endif
VendorRequests_Init(VENDOR_REQUESTS_PARAMS); VendorRequests_Init(VENDOR_REQUESTS_PARAMS);
#ifdef MIXER #if (MIXER)
/* Set up mixer default state */ /* Set up mixer default state */
for (int i = 0; i < 18*8; i++) InitLocalMixerState();
{
mixer1Weights[i] = 0x8001; //-inf
}
/* Configure default connections */
mixer1Weights[0] = 0;
mixer1Weights[9] = 0;
mixer1Weights[18] = 0;
mixer1Weights[27] = 0;
mixer1Weights[36] = 0;
mixer1Weights[45] = 0;
mixer1Weights[54] = 0;
mixer1Weights[63] = 0;
#if NUM_USB_CHAN_OUT > 0
/* Setup up audio output channel mapping */
for(int i = 0; i < NUM_USB_CHAN_OUT; i++)
{
channelMapAud[i] = i;
}
#endif
#if NUM_USB_CHAN_IN > 0
for(int i = 0; i < NUM_USB_CHAN_IN; i++)
{
channelMapUsb[i] = i + NUM_USB_CHAN_OUT;
}
#endif
/* Set up channel mapping default */
for (int i = 0; i < NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN; i++)
{
channelMap[i] = i;
}
#if MAX_MIX_COUNT > 0
/* Mixer outputs mapping defaults */
for (int i = 0; i < MAX_MIX_COUNT; i++)
{
channelMap[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + i] = i;
}
#endif
/* Init mixer inputs */
for(int j = 0; j < MAX_MIX_COUNT; j++)
for(int i = 0; i < MIX_INPUTS; i++)
{
mixSel[j][i] = i;
}
#endif #endif
#ifdef VENDOR_AUDIO_REQS #ifdef VENDOR_AUDIO_REQS
@@ -529,7 +512,6 @@ void XUA_Endpoint0_init(chanend c_ep0_out, chanend c_ep0_in, NULLABLE_RESOURCE(c
cfgDesc_Audio1[USB_AS_OUT_INTERFACE_DESCRIPTOR_OFFSET_FREQ + 3*i + 2] = (get_usb_to_device_rate() & 0xff0000)>> 16; cfgDesc_Audio1[USB_AS_OUT_INTERFACE_DESCRIPTOR_OFFSET_FREQ + 3*i + 2] = (get_usb_to_device_rate() & 0xff0000)>> 16;
} }
cfgDesc_Audio1[USB_AS_OUT_EP_DESCRIPTOR_OFFSET_MAXPACKETSIZE] = ((get_usb_to_device_bit_res() >> 3) * MAX_PACKET_SIZE_MULT_OUT_FS) & 0xff; //max packet size cfgDesc_Audio1[USB_AS_OUT_EP_DESCRIPTOR_OFFSET_MAXPACKETSIZE] = ((get_usb_to_device_bit_res() >> 3) * MAX_PACKET_SIZE_MULT_OUT_FS) & 0xff; //max packet size
cfgDesc_Audio1[USB_AS_OUT_EP_DESCRIPTOR_OFFSET_MAXPACKETSIZE + 1] = (((get_usb_to_device_bit_res() >> 3) * MAX_PACKET_SIZE_MULT_OUT_FS) & 0xff00) >> 8; //max packet size cfgDesc_Audio1[USB_AS_OUT_EP_DESCRIPTOR_OFFSET_MAXPACKETSIZE + 1] = (((get_usb_to_device_bit_res() >> 3) * MAX_PACKET_SIZE_MULT_OUT_FS) & 0xff00) >> 8; //max packet size
#endif // NUM_USB_CHAN_OUT #endif // NUM_USB_CHAN_OUT

View File

@@ -1,4 +1,4 @@
// Copyright 2011-2022 XMOS LIMITED. // Copyright 2011-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1. // This Software is subject to the terms of the XMOS Public Licence: Version 1.
/** /**
* @file xua_ep0_descriptors.h * @file xua_ep0_descriptors.h
@@ -308,28 +308,28 @@ typedef struct
#error NUM_USB_CHAN > 32 #error NUM_USB_CHAN > 32
#endif #endif
#if defined(MIXER) && (MAX_MIX_COUNT > 0) #if (MIXER) && (MAX_MIX_COUNT > 0)
STR_TABLE_ENTRY(mixOutStr_1); STR_TABLE_ENTRY(mixOutStr_1);
#endif #endif
#if defined(MIXER) && (MAX_MIX_COUNT > 1) #if (MIXER) && (MAX_MIX_COUNT > 1)
STR_TABLE_ENTRY(mixOutStr_2); STR_TABLE_ENTRY(mixOutStr_2);
#endif #endif
#if defined(MIXER) && (MAX_MIX_COUNT > 2) #if (MIXER) && (MAX_MIX_COUNT > 2)
STR_TABLE_ENTRY(mixOutStr_3); STR_TABLE_ENTRY(mixOutStr_3);
#endif #endif
#if defined(MIXER) && (MAX_MIX_COUNT > 3) #if (MIXER) && (MAX_MIX_COUNT > 3)
STR_TABLE_ENTRY(mixOutStr_4); STR_TABLE_ENTRY(mixOutStr_4);
#endif #endif
#if defined(MIXER) && (MAX_MIX_COUNT > 4) #if (MIXER) && (MAX_MIX_COUNT > 4)
STR_TABLE_ENTRY(mixOutStr_5); STR_TABLE_ENTRY(mixOutStr_5);
#endif #endif
#if defined(MIXER) && (MAX_MIX_COUNT > 5) #if (MIXER) && (MAX_MIX_COUNT > 5)
STR_TABLE_ENTRY(mixOutStr_6); STR_TABLE_ENTRY(mixOutStr_6);
#endif #endif
#if defined(MIXER) && (MAX_MIX_COUNT > 6) #if (MIXER) && (MAX_MIX_COUNT > 6)
STR_TABLE_ENTRY(mixOutStr_7); STR_TABLE_ENTRY(mixOutStr_7);
#endif #endif
#if defined(MIXER) && (MAX_MIX_COUNT > 7) #if (MIXER) && (MAX_MIX_COUNT > 7)
STR_TABLE_ENTRY(mixOutStr_8); STR_TABLE_ENTRY(mixOutStr_8);
#endif #endif
#ifdef IAP #ifdef IAP
@@ -391,31 +391,31 @@ StringDescTable_t g_strTable =
#error NUM_USB_CHAN_IN > 32 #error NUM_USB_CHAN_IN > 32
#endif #endif
#if defined(MIXER) && (MAX_MIX_COUNT > 0) #if (MIXER) && (MAX_MIX_COUNT > 0)
.mixOutStr_1 = "Mix 1", .mixOutStr_1 = "Mix 1",
#endif #endif
#if defined(MIXER) && (MAX_MIX_COUNT > 1) #if (MIXER) && (MAX_MIX_COUNT > 1)
.mixOutStr_2 = "Mix 2", .mixOutStr_2 = "Mix 2",
#endif #endif
#if defined(MIXER) && (MAX_MIX_COUNT > 2) #if (MIXER) && (MAX_MIX_COUNT > 2)
.mixOutStr_3 = "Mix 3", .mixOutStr_3 = "Mix 3",
#endif #endif
#if defined(MIXER) && (MAX_MIX_COUNT > 3) #if (MIXER) && (MAX_MIX_COUNT > 3)
.mixOutStr_4 = "Mix 4", .mixOutStr_4 = "Mix 4",
#endif #endif
#if defined(MIXER) && (MAX_MIX_COUNT > 4) #if (MIXER) && (MAX_MIX_COUNT > 4)
.mixOutStr_5 = "Mix 5", .mixOutStr_5 = "Mix 5",
#endif #endif
#if defined(MIXER) && (MAX_MIX_COUNT > 5) #if (MIXER) && (MAX_MIX_COUNT > 5)
.mixOutStr_6 = "Mix 6", .mixOutStr_6 = "Mix 6",
#endif #endif
#if defined(MIXER) && (MAX_MIX_COUNT > 6) #if (MIXER) && (MAX_MIX_COUNT > 6)
.mixOutStr_7 = "Mix 7", .mixOutStr_7 = "Mix 7",
#endif #endif
#if defined(MIXER) && (MAX_MIX_COUNT > 7) #if (MIXER) && (MAX_MIX_COUNT > 7)
.mixOutStr_8 = "Mix 8", .mixOutStr_8 = "Mix 8",
#endif #endif
#if defined(MIXER) && (MAX_MIX_COUNT > 8) #if (MIXER) && (MAX_MIX_COUNT > 8)
#error #error
#endif #endif
#ifdef IAP #ifdef IAP
@@ -558,7 +558,7 @@ unsigned char devQualDesc_Null[] =
}; };
#if defined(MIXER) && !defined(AUDIO_PATH_XUS) && (MAX_MIX_COUNT > 0) #if (MIXER) && !defined(AUDIO_PATH_XUS) && (MAX_MIX_COUNT > 0)
//#warning Extension units on the audio path are required for mixer. Enabling them now. //#warning Extension units on the audio path are required for mixer. Enabling them now.
#define AUDIO_PATH_XUS #define AUDIO_PATH_XUS
#endif #endif
@@ -575,7 +575,7 @@ unsigned char devQualDesc_Null[] =
#define DFU_LENGTH (0) #define DFU_LENGTH (0)
#endif #endif
#ifdef MIXER #if (MIXER)
#define MIX_BMCONTROLS_LEN_TMP ((MAX_MIX_COUNT * MIX_INPUTS) / 8) #define MIX_BMCONTROLS_LEN_TMP ((MAX_MIX_COUNT * MIX_INPUTS) / 8)
#if ((MAX_MIX_COUNT * MIX_INPUTS)%8)==0 #if ((MAX_MIX_COUNT * MIX_INPUTS)%8)==0
@@ -666,7 +666,7 @@ typedef struct
#if (NUM_USB_CHAN_OUT > 0) #if (NUM_USB_CHAN_OUT > 0)
/* Output path */ /* Output path */
USB_Descriptor_Audio_InputTerminal_t Audio_Out_InputTerminal; USB_Descriptor_Audio_InputTerminal_t Audio_Out_InputTerminal;
#if defined(MIXER) && (MAX_MIX_COUNT > 0) #if (MIXER) && (MAX_MIX_COUNT > 0)
USB_Descriptor_Audio_ExtensionUnit_t Audio_Out_ExtensionUnit; USB_Descriptor_Audio_ExtensionUnit_t Audio_Out_ExtensionUnit;
#endif #endif
#if(OUTPUT_VOLUME_CONTROL == 1) #if(OUTPUT_VOLUME_CONTROL == 1)
@@ -677,7 +677,7 @@ typedef struct
#if (NUM_USB_CHAN_IN > 0) #if (NUM_USB_CHAN_IN > 0)
/* Input path */ /* Input path */
USB_Descriptor_Audio_InputTerminal_t Audio_In_InputTerminal; USB_Descriptor_Audio_InputTerminal_t Audio_In_InputTerminal;
#if defined(MIXER) && (MAX_MIX_COUNT > 0) #if (MIXER) && (MAX_MIX_COUNT > 0)
USB_Descriptor_Audio_ExtensionUnit_t Audio_In_ExtensionUnit; USB_Descriptor_Audio_ExtensionUnit_t Audio_In_ExtensionUnit;
#endif #endif
#if(INPUT_VOLUME_CONTROL == 1) #if(INPUT_VOLUME_CONTROL == 1)
@@ -685,7 +685,7 @@ typedef struct
#endif #endif
USB_Descriptor_Audio_OutputTerminal_t Audio_In_OutputTerminal; USB_Descriptor_Audio_OutputTerminal_t Audio_In_OutputTerminal;
#endif #endif
#if defined(MIXER) && (MAX_MIX_COUNT > 0) #if (MIXER) && (MAX_MIX_COUNT > 0)
USB_Descriptor_Audio_ExtensionUnit2_t Audio_Mix_ExtensionUnit; USB_Descriptor_Audio_ExtensionUnit2_t Audio_Mix_ExtensionUnit;
// Currently no struct for mixer unit // Currently no struct for mixer unit
// USB_Descriptor_Audio_MixerUnit_t Audio_MixerUnit; // USB_Descriptor_Audio_MixerUnit_t Audio_MixerUnit;
@@ -1168,7 +1168,7 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2=
UAC_CS_DESCTYPE_INTERFACE, /* 1 bDescriptorType: CS_INTERFACE */ UAC_CS_DESCTYPE_INTERFACE, /* 1 bDescriptorType: CS_INTERFACE */
UAC_CS_AC_INTERFACE_SUBTYPE_FEATURE_UNIT, /* 2 bDescriptorSubType: FEATURE_UNIT */ UAC_CS_AC_INTERFACE_SUBTYPE_FEATURE_UNIT, /* 2 bDescriptorSubType: FEATURE_UNIT */
FU_USBIN, /* 3 bUnitID */ FU_USBIN, /* 3 bUnitID */
#if defined(MIXER) && (MAX_MIX_COUNT > 0) #if (MIXER) && (MAX_MIX_COUNT > 0)
ID_XU_IN, /* 4 bSourceID */ ID_XU_IN, /* 4 bSourceID */
#else #else
ID_IT_AUD, /* 4 bSourceID */ ID_IT_AUD, /* 4 bSourceID */
@@ -1300,7 +1300,7 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2=
}, },
#endif /* (NUM_USB_CHAN_IN > 0) */ #endif /* (NUM_USB_CHAN_IN > 0) */
#if defined(MIXER) && (MAX_MIX_COUNT > 0) #if (MIXER) && (MAX_MIX_COUNT > 0)
/* Extension Unit Descriptor (4.7.2.12) */ /* Extension Unit Descriptor (4.7.2.12) */
.Audio_Mix_ExtensionUnit = .Audio_Mix_ExtensionUnit =
{ {
@@ -1392,7 +1392,7 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2=
0x00, /* bmControls */ 0x00, /* bmControls */
0 /* Mixer unit string descriptor index */ 0 /* Mixer unit string descriptor index */
}, },
#endif /* defined(MIXER) && (MAX_MIX_COUNT > 0) */ #endif /* (MIXER) && (MAX_MIX_COUNT > 0) */
#if (XUA_SPDIF_RX_EN) || (XUA_ADAT_RX_EN) #if (XUA_SPDIF_RX_EN) || (XUA_ADAT_RX_EN)
/* Standard AS Interrupt Endpoint Descriptor (4.8.2.1): */ /* Standard AS Interrupt Endpoint Descriptor (4.8.2.1): */

View File

@@ -1,4 +1,4 @@
// Copyright 2011-2022 XMOS LIMITED. // Copyright 2011-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1. // This Software is subject to the terms of the XMOS Public Licence: Version 1.
/** /**
* @brief Implements relevant requests from the USB Audio 2.0 Specification * @brief Implements relevant requests from the USB Audio 2.0 Specification
@@ -30,10 +30,9 @@ extern unsigned int mutesOut[];
extern int volsIn[]; extern int volsIn[];
extern unsigned int mutesIn[]; extern unsigned int mutesIn[];
/* Mixer settings */ #if (MIXER)
#ifdef MIXER /* Mixer weights */
extern unsigned char mixer1Crossbar[]; extern short mixer1Weights[MIX_INPUTS * MAX_MIX_COUNT];
extern short mixer1Weights[];
/* Device channel mapping */ /* Device channel mapping */
extern unsigned char channelMapAud[NUM_USB_CHAN_OUT]; extern unsigned char channelMapAud[NUM_USB_CHAN_OUT];
@@ -130,16 +129,16 @@ static unsigned longMul(unsigned a, unsigned b, int prec)
} }
/* Update master volume i.e. i.e update weights for all channels */ /* Update master volume i.e. i.e update weights for all channels */
static void updateMasterVol( int unitID, chanend ?c_mix_ctl) static void updateMasterVol(int unitID, chanend ?c_mix_ctl)
{ {
int x; int x;
#ifndef OUT_VOLUME_IN_MIXER #if (OUT_VOLUME_IN_MIXER == 0)
xc_ptr p_multOut = array_to_xc_ptr(multOut); xc_ptr p_multOut = array_to_xc_ptr(multOut);
#endif #endif
#ifndef IN_VOLUME_IN_MIXER #if (IN_VOLUME_IN_MIXER == 0)
xc_ptr p_multIn = array_to_xc_ptr(multIn); xc_ptr p_multIn = array_to_xc_ptr(multIn);
#endif #endif
switch( unitID) switch(unitID)
{ {
case FU_USBOUT: case FU_USBOUT:
{ {
@@ -153,9 +152,11 @@ static void updateMasterVol( int unitID, chanend ?c_mix_ctl)
x = longMul(master_vol, vol, 29) * !mutesOut[0] * !mutesOut[i]; x = longMul(master_vol, vol, 29) * !mutesOut[0] * !mutesOut[i];
#ifdef OUT_VOLUME_IN_MIXER #if (OUT_VOLUME_IN_MIXER)
if (!isnull(c_mix_ctl)) if (!isnull(c_mix_ctl))
{ {
outct(c_mix_ctl, XS1_CT_END);
inct(c_mix_ctl);
outuint(c_mix_ctl, SET_MIX_OUT_VOL); outuint(c_mix_ctl, SET_MIX_OUT_VOL);
outuint(c_mix_ctl, i-1); outuint(c_mix_ctl, i-1);
outuint(c_mix_ctl, x); outuint(c_mix_ctl, x);
@@ -179,9 +180,11 @@ static void updateMasterVol( int unitID, chanend ?c_mix_ctl)
x = longMul(master_vol, vol, 29) * !mutesIn[0] * !mutesIn[i]; x = longMul(master_vol, vol, 29) * !mutesIn[0] * !mutesIn[i];
#ifdef IN_VOLUME_IN_MIXER #if (IN_VOLUME_IN_MIXER)
if (!isnull(c_mix_ctl)) if (!isnull(c_mix_ctl))
{ {
outct(c_mix_ctl, XS1_CT_END);
inct(c_mix_ctl);
outuint(c_mix_ctl, SET_MIX_IN_VOL); outuint(c_mix_ctl, SET_MIX_IN_VOL);
outuint(c_mix_ctl, i-1); outuint(c_mix_ctl, i-1);
outuint(c_mix_ctl, x); outuint(c_mix_ctl, x);
@@ -202,10 +205,10 @@ static void updateMasterVol( int unitID, chanend ?c_mix_ctl)
static void updateVol(int unitID, int channel, chanend ?c_mix_ctl) static void updateVol(int unitID, int channel, chanend ?c_mix_ctl)
{ {
int x; int x;
#ifndef OUT_VOLUME_IN_MIXER #if (OUT_VOLUME_IN_MIXER == 0)
xc_ptr p_multOut = array_to_xc_ptr(multOut); xc_ptr p_multOut = array_to_xc_ptr(multOut);
#endif #endif
#ifndef IN_VOLUME_IN_MIXER #if (IN_VOLUME_IN_MIXER == 0)
xc_ptr p_multIn = array_to_xc_ptr(multIn); xc_ptr p_multIn = array_to_xc_ptr(multIn);
#endif #endif
/* Check for master volume update */ /* Check for master volume update */
@@ -226,9 +229,11 @@ static void updateVol(int unitID, int channel, chanend ?c_mix_ctl)
x = longMul(master_vol, vol, 29) * !mutesOut[0] * !mutesOut[channel]; x = longMul(master_vol, vol, 29) * !mutesOut[0] * !mutesOut[channel];
#ifdef OUT_VOLUME_IN_MIXER #if (OUT_VOLUME_IN_MIXER)
if (!isnull(c_mix_ctl)) if (!isnull(c_mix_ctl))
{ {
outct(c_mix_ctl, XS1_CT_END);
inct(c_mix_ctl);
outuint(c_mix_ctl, SET_MIX_OUT_VOL); outuint(c_mix_ctl, SET_MIX_OUT_VOL);
outuint(c_mix_ctl, channel-1); outuint(c_mix_ctl, channel-1);
outuint(c_mix_ctl, x); outuint(c_mix_ctl, x);
@@ -244,13 +249,15 @@ static void updateVol(int unitID, int channel, chanend ?c_mix_ctl)
/* Calc multipliers with 29 fractional bits from a db value with 8 fractional bits */ /* Calc multipliers with 29 fractional bits from a db value with 8 fractional bits */
/* 0x8000 is a special value representing -inf (i.e. mute) */ /* 0x8000 is a special value representing -inf (i.e. mute) */
unsigned master_vol = volsIn[0] == 0x8000 ? 0 : db_to_mult(volsIn[0], 8, 29); unsigned master_vol = volsIn[0] == 0x8000 ? 0 : db_to_mult(volsIn[0], 8, 29);
unsigned vol = volsIn[channel] == 0x8000 ? 0 : db_to_mult(volsIn[channel], 8, 29); unsigned vol = volsIn[channel] == 0x8000 ? 0 : db_to_mult(volsIn[channel], 8, 29);
x = longMul(master_vol, vol, 29) * !mutesIn[0] * !mutesIn[channel]; x = longMul(master_vol, vol, 29) * !mutesIn[0] * !mutesIn[channel];
#ifdef IN_VOLUME_IN_MIXER #if (IN_VOLUME_IN_MIXER)
if (!isnull(c_mix_ctl)) if (!isnull(c_mix_ctl))
{ {
outct(c_mix_ctl, XS1_CT_END);
inct(c_mix_ctl);
outuint(c_mix_ctl, SET_MIX_IN_VOL); outuint(c_mix_ctl, SET_MIX_IN_VOL);
outuint(c_mix_ctl, channel-1); outuint(c_mix_ctl, channel-1);
outuint(c_mix_ctl, x); outuint(c_mix_ctl, x);
@@ -266,6 +273,38 @@ static void updateVol(int unitID, int channel, chanend ?c_mix_ctl)
} }
#endif #endif
void UpdateMixerOutputRouting(chanend c_mix_ctl, unsigned map, unsigned dst, unsigned src)
{
outct(c_mix_ctl, XS1_CT_END);
inct(c_mix_ctl);
outuint(c_mix_ctl, map);
outuint(c_mix_ctl, dst);
outuint(c_mix_ctl, src);
outct(c_mix_ctl, XS1_CT_END);
}
void UpdateMixMap(chanend c_mix_ctl, int mix, int input, int src)
{
outct(c_mix_ctl, XS1_CT_END);
inct(c_mix_ctl);
outuint(c_mix_ctl, SET_MIX_MAP);
outuint(c_mix_ctl, mix); /* Mix bus */
outuint(c_mix_ctl, input); /* Mixer input (cn) */
outuint(c_mix_ctl, src); /* Source (mixSel[cn]) */
outct(c_mix_ctl, XS1_CT_END);
}
void UpdateMixerWeight(chanend c_mix_ctl, int mix, int index, unsigned mult)
{
outct(c_mix_ctl, XS1_CT_END);
inct(c_mix_ctl);
outuint(c_mix_ctl, SET_MIX_MULT);
outuint(c_mix_ctl, mix);
outuint(c_mix_ctl, index);
outuint(c_mix_ctl, mult);
outct(c_mix_ctl, XS1_CT_END);
}
/* Handles the audio class specific requests /* Handles the audio class specific requests
* returns: XUD_RES_OKAY if request dealt with successfully without error, * returns: XUD_RES_OKAY if request dealt with successfully without error,
* XUD_RES_RST for device reset * XUD_RES_RST for device reset
@@ -282,7 +321,6 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c
/* Inspect request, NOTE: these are class specific requests */ /* Inspect request, NOTE: these are class specific requests */
switch( sp.bRequest ) switch( sp.bRequest )
{ {
/* CUR Request*/ /* CUR Request*/
case CUR: case CUR:
{ {
@@ -318,7 +356,7 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c
int newSampleRate = buffer[0]; int newSampleRate = buffer[0];
/* Instruct audio thread to change sample freq (if change required) */ /* Instruct audio thread to change sample freq (if change required) */
//if(newSampleRate != g_curSamFreq) if(newSampleRate != g_curSamFreq)
{ {
int newMasterClock; int newMasterClock;
@@ -371,7 +409,7 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c
} }
#endif /* MAX_FREQ != MIN_FREQ */ #endif /* MAX_FREQ != MIN_FREQ */
/* Send 0 Length as status stage */ /* Send 0 Length as status stage */
int x = XUD_DoSetRequestStatus(ep0_in); return XUD_DoSetRequestStatus(ep0_in);
} }
/* Direction: Device-to-host: Send Current Sample Freq */ /* Direction: Device-to-host: Send Current Sample Freq */
else else
@@ -537,7 +575,7 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c
if ((sp.wValue & 0xff) <= NUM_USB_CHAN_OUT) if ((sp.wValue & 0xff) <= NUM_USB_CHAN_OUT)
{ {
volsOut[ sp.wValue&0xff ] = (buffer, unsigned char[])[0] | (((int) (signed char) (buffer, unsigned char[])[1]) << 8); volsOut[ sp.wValue&0xff ] = (buffer, unsigned char[])[0] | (((int) (signed char) (buffer, unsigned char[])[1]) << 8);
updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl ); updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl);
return XUD_DoSetRequestStatus(ep0_in); return XUD_DoSetRequestStatus(ep0_in);
} }
} }
@@ -546,7 +584,7 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c
if ((sp.wValue & 0xff) <= NUM_USB_CHAN_IN) if ((sp.wValue & 0xff) <= NUM_USB_CHAN_IN)
{ {
volsIn[ sp.wValue&0xff ] = (buffer, unsigned char[])[0] | (((int) (signed char) (buffer, unsigned char[])[1]) << 8); volsIn[ sp.wValue&0xff ] = (buffer, unsigned char[])[0] | (((int) (signed char) (buffer, unsigned char[])[1]) << 8);
updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl ); updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl);
return XUD_DoSetRequestStatus(ep0_in); return XUD_DoSetRequestStatus(ep0_in);
} }
} }
@@ -632,85 +670,76 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c
break; /* FU_USBIN */ break; /* FU_USBIN */
#endif #endif
#if defined(MIXER) && (MAX_MIX_COUNT > 0) #if ((MIXER) && (MAX_MIX_COUNT > 0))
case ID_XU_OUT: case ID_XU_OUT:
{
if(sp.bmRequestType.Direction == USB_BM_REQTYPE_DIRECTION_H2D) /* Direction: Host-to-device */
{ {
unsigned volume = 0; int dst = sp.wValue & 0xff;
int c = sp.wValue & 0xff;
if(sp.bmRequestType.Direction == USB_BM_REQTYPE_DIRECTION_H2D) /* Direction: Host-to-device */
if((result = XUD_GetBuffer(ep0_out, (buffer, unsigned char[]), datalength)) != XUD_RES_OKAY)
{ {
return result; if((result = XUD_GetBuffer(ep0_out, (buffer, unsigned char[]), datalength)) != XUD_RES_OKAY)
}
channelMapAud[c] = (buffer, unsigned char[])[0] | (buffer, unsigned char[])[1] << 8;
if (!isnull(c_mix_ctl))
{
if (c < NUM_USB_CHAN_OUT)
{ {
outuint(c_mix_ctl, SET_SAMPLES_TO_DEVICE_MAP); return result;
outuint(c_mix_ctl, c);
outuint(c_mix_ctl, channelMapAud[c]);
outct(c_mix_ctl, XS1_CT_END);
/* Send 0 Length as status stage */
return XUD_DoSetRequestStatus(ep0_in);
} }
if (dst < NUM_USB_CHAN_OUT)
{
channelMapAud[dst] = (buffer, unsigned char[])[0] | (buffer, unsigned char[])[1] << 8;
if (!isnull(c_mix_ctl))
{
UpdateMixerOutputRouting(c_mix_ctl, SET_SAMPLES_TO_DEVICE_MAP, dst, channelMapAud[dst]);
}
}
/* Send 0 Length as status stage */
return XUD_DoSetRequestStatus(ep0_in);
}
else
{
(buffer, unsigned char[])[0] = channelMapAud[dst];
(buffer, unsigned char[])[1] = 0;
return XUD_DoGetRequest(ep0_out, ep0_in, (buffer, unsigned char[]), sp.wLength, sp.wLength);
} }
} }
else
{
(buffer, unsigned char[])[0] = channelMapAud[sp.wValue & 0xff];
(buffer, unsigned char[])[1] = 0;
return XUD_DoGetRequest(ep0_out, ep0_in, (buffer, unsigned char[]), sp.wLength, sp.wLength);
}
}
break; break;
case ID_XU_IN: case ID_XU_IN:
if(sp.bmRequestType.Direction == USB_BM_REQTYPE_DIRECTION_H2D) /* Direction: Host-to-device */
{ {
unsigned volume = 0; int dst = sp.wValue & 0xff;
int c = sp.wValue & 0xff;
if((result = XUD_GetBuffer(ep0_out, (buffer, unsigned char[]), datalength)) != XUD_RES_OKAY) if(sp.bmRequestType.Direction == USB_BM_REQTYPE_DIRECTION_H2D) /* Direction: Host-to-device */
{ {
return result; if((result = XUD_GetBuffer(ep0_out, (buffer, unsigned char[]), datalength)) != XUD_RES_OKAY)
}
channelMapUsb[c] = (buffer, unsigned char[])[0] | (buffer, unsigned char[])[1] << 8;
if (c < NUM_USB_CHAN_IN)
{
if (!isnull(c_mix_ctl))
{ {
outuint(c_mix_ctl, SET_SAMPLES_TO_HOST_MAP); return result;
outuint(c_mix_ctl, c);
outuint(c_mix_ctl, channelMapUsb[c]);
outct(c_mix_ctl, XS1_CT_END);
return XUD_DoSetRequestStatus(ep0_in);
} }
if (dst < NUM_USB_CHAN_IN)
{
channelMapUsb[dst] = (buffer, unsigned char[])[0] | (buffer, unsigned char[])[1] << 8;
if (!isnull(c_mix_ctl))
{
UpdateMixerOutputRouting(c_mix_ctl, SET_SAMPLES_TO_HOST_MAP, dst, channelMapUsb[dst]);
}
}
return XUD_DoSetRequestStatus(ep0_in);
}
else
{
/* Direction: Device-to-host */
(buffer, unsigned char[])[0] = channelMapUsb[dst];
(buffer, unsigned char[])[1] = 0;
return XUD_DoGetRequest(ep0_out, ep0_in, (buffer, unsigned char[]), sp.wLength, sp.wLength);
} }
}
else
{
/* Direction: Device-to-host */
(buffer, unsigned char[])[0] = channelMapUsb[sp.wValue & 0xff];
(buffer, unsigned char[])[1] = 0;
return XUD_DoGetRequest(ep0_out, ep0_in, (buffer, unsigned char[]), sp.wLength, sp.wLength);
} }
break; break;
case ID_XU_MIXSEL: case ID_XU_MIXSEL:
{ {
int cs = sp.wValue >> 8; /* Control Selector */ int cs = sp.wValue >> 8; /* Control Selector */
int cn = sp.wValue & 0xff; /* Channel number */ int cn = sp.wValue & 0xff; /* Channel Number */
/* Check for Get or Set */ /* Check for Get or Set */
if(sp.bmRequestType.Direction == USB_BM_REQTYPE_DIRECTION_H2D) if(sp.bmRequestType.Direction == USB_BM_REQTYPE_DIRECTION_H2D)
@@ -723,21 +752,19 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c
if(datalength > 0) if(datalength > 0)
{ {
/* cn bounds check for safety..*/ /* CN bounds check for safety..*/
if(cn < MIX_INPUTS) if(cn < MIX_INPUTS)
{ {
//if(cs == CS_XU_MIXSEL) //if(cs == CS_XU_MIXSEL)
/* cs now contains mix number */ /* cs now contains mix number */
if(cs < (MAX_MIX_COUNT + 1)) if(cs < (MAX_MIX_COUNT + 1))
{ {
int source = (buffer, unsigned char[])[0];
/* Check for "off" - update local state */ /* Check for "off" - update local state */
if((buffer, unsigned char[])[0] == 0xFF) if(source == 0xFF)
{ {
mixSel[cs][cn] = (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT); source = (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT);
}
else
{
mixSel[cs][cn] = (buffer, unsigned char[])[0];
} }
if(cs == 0) if(cs == 0)
@@ -745,21 +772,17 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c
/* Update all mix maps */ /* Update all mix maps */
for (int i = 0; i < MAX_MIX_COUNT; i++) for (int i = 0; i < MAX_MIX_COUNT; i++)
{ {
outuint(c_mix_ctl, SET_MIX_MAP); /* i : Mix bus */
outuint(c_mix_ctl, i); /* Mix bus */ /* cn: Mixer input */
outuint(c_mix_ctl, cn); /* Mixer input */ mixSel[i][cn] = source;
outuint(c_mix_ctl, (int) mixSel[cn]); /* Source */ UpdateMixMap(c_mix_ctl, i, cn, mixSel[i][cn]);
outct(c_mix_ctl, XS1_CT_END);
} }
} }
else else
{ {
/* Update relevant mix map */ /* Update relevant mix map */
outuint(c_mix_ctl, SET_MIX_MAP); /* Command */ mixSel[cs-1][cn] = source;
outuint(c_mix_ctl, (cs-1)); /* Mix bus */ UpdateMixMap(c_mix_ctl, cs-1, cn, mixSel[cs-1][cn]);
outuint(c_mix_ctl, cn); /* Mixer input */
outuint(c_mix_ctl, (int) mixSel[cs][cn]); /* Source */
outct(c_mix_ctl, XS1_CT_END); /* Wait for handshake back */
} }
return XUD_DoSetRequestStatus(ep0_in); return XUD_DoSetRequestStatus(ep0_in);
@@ -780,7 +803,7 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c
if((cs > 0) && (cs < (MAX_MIX_COUNT+1))) if((cs > 0) && (cs < (MAX_MIX_COUNT+1)))
{ {
(buffer, unsigned char[])[0] = mixSel[cs-1][cn]; (buffer, unsigned char[])[0] = mixSel[cs-1][cn];
return XUD_DoGetRequest(ep0_out, ep0_in, (buffer, unsigned char[]), 1, 1 ); return XUD_DoGetRequest(ep0_out, ep0_in, (buffer, unsigned char[]), 1, 1);
} }
} }
} }
@@ -788,49 +811,53 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c
} }
case ID_MIXER_1: case ID_MIXER_1:
if(sp.bmRequestType.Direction == USB_BM_REQTYPE_DIRECTION_H2D) /* Direction: Host-to-device */
{ {
unsigned volume = 0; int cs = sp.wValue >> 8; /* Control Selector - currently unused */
int cn = sp.wValue & 0xff; /* Channel number - used for mixer node index */
/* Expect OUT here with mute */ if(sp.bmRequestType.Direction == USB_BM_REQTYPE_DIRECTION_H2D) /* Direction: Host-to-device */
if((result = XUD_GetBuffer(ep0_out, (buffer, unsigned char[]), datalength)) != XUD_RES_OKAY)
{ {
return result; unsigned weightMult = 0;
}
mixer1Weights[sp.wValue & 0xff] = (buffer, unsigned char[])[0] | (buffer, unsigned char[])[1] << 8; /* Expect OUT here with weight */
if((result = XUD_GetBuffer(ep0_out, (buffer, unsigned char[]), datalength)) != XUD_RES_OKAY)
{
return result;
}
if (mixer1Weights[sp.wValue & 0xff] == 0x8000) if(cn < sizeof(mixer1Weights)/sizeof(mixer1Weights[0]))
{ {
volume = 0; mixer1Weights[cn] = (buffer, unsigned char[])[0] | (buffer, unsigned char[])[1] << 8;
if (mixer1Weights[cn] != 0x8000)
{
weightMult = db_to_mult(mixer1Weights[cn], XUA_MIXER_DB_FRAC_BITS, XUA_MIXER_MULT_FRAC_BITS);
}
if (!isnull(c_mix_ctl))
{
UpdateMixerWeight(c_mix_ctl, (cn) % 8, (cn) / 8, weightMult);
}
}
/* Send 0 Length as status stage */
return XUD_DoSetRequestStatus(ep0_in);
} }
else else
{ {
volume = db_to_mult(mixer1Weights[sp.wValue & 0xff], 8, 25); short weight = 0x8000;
}
if (!isnull(c_mix_ctl))
{
outuint(c_mix_ctl, SET_MIX_MULT);
outuint(c_mix_ctl, (sp.wValue & 0xff) % 8);
outuint(c_mix_ctl, (sp.wValue & 0xff) / 8);
outuint(c_mix_ctl, volume);
outct(c_mix_ctl, XS1_CT_END);
}
/* Send 0 Length as status stage */ if(cn < sizeof(mixer1Weights)/sizeof(mixer1Weights[0]))
return XUD_DoSetRequestStatus(ep0_in); {
} weight = mixer1Weights[cn];
else }
{
short weight = mixer1Weights[sp.wValue & 0xff];
(buffer, unsigned char[])[0] = weight & 0xff;
(buffer, unsigned char[])[1] = (weight >> 8) & 0xff;
return XUD_DoGetRequest(ep0_out, ep0_in, (buffer, unsigned char[]), sp.wLength, sp.wLength); storeShort((buffer, unsigned char[]), 0, weight);
return XUD_DoGetRequest(ep0_out, ep0_in, (buffer, unsigned char[]), sp.wLength, sp.wLength);
}
} }
break; break;
#endif #endif
default: default:
/* We dont have a unit with this ID! */ /* We dont have a unit with this ID! */
@@ -919,7 +946,6 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c
num_freqs++; num_freqs++;
} }
#endif #endif
storeShort((buffer, unsigned char[]), 0, num_freqs); storeShort((buffer, unsigned char[]), 0, num_freqs);
return XUD_DoGetRequest(ep0_out, ep0_in, (buffer, unsigned char[]), i, sp.wLength); return XUD_DoGetRequest(ep0_out, ep0_in, (buffer, unsigned char[]), i, sp.wLength);
@@ -957,7 +983,7 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c
} }
break; break;
#ifdef MIXER #if (MIXER)
/* Mixer Unit */ /* Mixer Unit */
case ID_MIXER_1: case ID_MIXER_1:
storeShort((buffer, unsigned char[]), 0, 1); storeShort((buffer, unsigned char[]), 0, 1);
@@ -967,7 +993,6 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c
return XUD_DoGetRequest(ep0_out, ep0_in, (buffer, unsigned char[]), sp.wLength, sp.wLength); return XUD_DoGetRequest(ep0_out, ep0_in, (buffer, unsigned char[]), sp.wLength, sp.wLength);
break; break;
#endif #endif
default: default:
/* Unknown Unit ID in Range Request selector for FU */ /* Unknown Unit ID in Range Request selector for FU */
break; break;
@@ -977,7 +1002,7 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c
break; /* case: RANGE */ break; /* case: RANGE */
} }
#if defined (MIXER) && (MAX_MIX_COUNT > 0) #if ((MIXER) && (MAX_MIX_COUNT > 0))
case MEM: /* Memory Requests (5.2.7.1) */ case MEM: /* Memory Requests (5.2.7.1) */
unitID = sp.wIndex >> 8; unitID = sp.wIndex >> 8;
@@ -1003,6 +1028,8 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c
{ {
if (!isnull(c_mix_ctl)) if (!isnull(c_mix_ctl))
{ {
outct(c_mix_ctl, XS1_CT_END);
inct(c_mix_ctl);
outuint(c_mix_ctl, GET_STREAM_LEVELS); outuint(c_mix_ctl, GET_STREAM_LEVELS);
outuint(c_mix_ctl, i); outuint(c_mix_ctl, i);
outct(c_mix_ctl, XS1_CT_END); outct(c_mix_ctl, XS1_CT_END);
@@ -1018,6 +1045,8 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c
{ {
if (!isnull(c_mix_ctl)) if (!isnull(c_mix_ctl))
{ {
outct(c_mix_ctl, XS1_CT_END);
inct(c_mix_ctl);
outuint(c_mix_ctl, GET_INPUT_LEVELS); outuint(c_mix_ctl, GET_INPUT_LEVELS);
outuint(c_mix_ctl, (i - NUM_USB_CHAN_OUT)); outuint(c_mix_ctl, (i - NUM_USB_CHAN_OUT));
outct(c_mix_ctl, XS1_CT_END); outct(c_mix_ctl, XS1_CT_END);
@@ -1040,6 +1069,8 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c
{ {
if (!isnull(c_mix_ctl)) if (!isnull(c_mix_ctl))
{ {
outct(c_mix_ctl, XS1_CT_END);
inct(c_mix_ctl);
outuint(c_mix_ctl, GET_OUTPUT_LEVELS); outuint(c_mix_ctl, GET_OUTPUT_LEVELS);
outuint(c_mix_ctl, i); outuint(c_mix_ctl, i);
outct(c_mix_ctl, XS1_CT_END); outct(c_mix_ctl, XS1_CT_END);
@@ -1107,13 +1138,10 @@ int AudioEndpointRequests_1(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp
if(newSampleRate != g_curSamFreq) if(newSampleRate != g_curSamFreq)
{ {
int curSamFreq44100Family;
int curSamFreq48000Family;
/* Windows Audio Class driver has a nice habbit of sending invalid SF's (e.g. 48001Hz) /* Windows Audio Class driver has a nice habbit of sending invalid SF's (e.g. 48001Hz)
* when under stress. Lets double check it here and ignore if not valid. */ * when under stress. Lets double check it here and ignore if not valid. */
curSamFreq48000Family = MCLK_48 % newSampleRate == 0; int curSamFreq48000Family = MCLK_48 % newSampleRate == 0;
curSamFreq44100Family = MCLK_441 % newSampleRate == 0; int curSamFreq44100Family = MCLK_441 % newSampleRate == 0;
if(curSamFreq48000Family || curSamFreq44100Family) if(curSamFreq48000Family || curSamFreq44100Family)
{ {
@@ -1128,7 +1156,7 @@ int AudioEndpointRequests_1(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp
/* Allow time for the change - feedback to stabilise */ /* Allow time for the change - feedback to stabilise */
FeedbackStabilityDelay(); FeedbackStabilityDelay();
} }
} }
return XUD_SetBuffer(ep0_in, (buffer, unsigned char[]), 0); return XUD_SetBuffer(ep0_in, (buffer, unsigned char[]), 0);
} }
@@ -1190,12 +1218,12 @@ XUD_Result_t AudioClassRequests_1(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket
{ {
case FU_USBOUT: case FU_USBOUT:
volsOut[ sp.wValue & 0xff ] = buffer[0] | (((int) (signed char) buffer[1]) << 8); volsOut[ sp.wValue & 0xff ] = buffer[0] | (((int) (signed char) buffer[1]) << 8);
updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl ); updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl);
return XUD_DoSetRequestStatus(ep0_in); return XUD_DoSetRequestStatus(ep0_in);
case FU_USBIN: case FU_USBIN:
volsIn[ sp.wValue & 0xff ] = buffer[0] | (((int) (signed char) buffer[1]) << 8); volsIn[ sp.wValue & 0xff ] = buffer[0] | (((int) (signed char) buffer[1]) << 8);
updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl ); updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl);
return XUD_DoSetRequestStatus(ep0_in); return XUD_DoSetRequestStatus(ep0_in);
} }
} }
@@ -1209,12 +1237,12 @@ XUD_Result_t AudioClassRequests_1(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket
{ {
case FU_USBOUT: case FU_USBOUT:
mutesOut[ sp.wValue & 0xff ] = buffer[0]; mutesOut[ sp.wValue & 0xff ] = buffer[0];
updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl ); updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl);
return XUD_DoSetRequestStatus(ep0_in); return XUD_DoSetRequestStatus(ep0_in);
case FU_USBIN: case FU_USBIN:
mutesIn[ sp.wValue & 0xff ] = buffer[0]; mutesIn[ sp.wValue & 0xff ] = buffer[0];
updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl ); updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl);
return XUD_DoSetRequestStatus(ep0_in); return XUD_DoSetRequestStatus(ep0_in);
} }
} }

View File

@@ -1,4 +1,4 @@
// Copyright 2012-2022 XMOS LIMITED. // Copyright 2012-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1. // This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include "xua.h" /* Device specific defines */ #include "xua.h" /* Device specific defines */
@@ -273,7 +273,7 @@ void usb_audio_core(chanend c_mix_out
#ifdef MIDI #ifdef MIDI
, chanend c_midi , chanend c_midi
#endif #endif
#ifdef MIXER #if (MIXER)
, chanend c_mix_ctl , chanend c_mix_ctl
#endif #endif
, chanend ?c_clk_int , chanend ?c_clk_int
@@ -290,7 +290,7 @@ VENDOR_REQUESTS_PARAMS_DEC_
chan c_xud_in[ENDPOINT_COUNT_IN]; chan c_xud_in[ENDPOINT_COUNT_IN];
chan c_aud_ctl; chan c_aud_ctl;
#ifndef MIXER #if (!MIXER)
#define c_mix_ctl null #define c_mix_ctl null
#endif #endif
@@ -401,7 +401,7 @@ void usb_audio_io(chanend ?c_aud_in,
#if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE) #if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE)
chanend c_spdif_tx, chanend c_spdif_tx,
#endif #endif
#ifdef MIXER #if (MIXER)
chanend c_mix_ctl, chanend c_mix_ctl,
#endif #endif
streaming chanend ?c_spdif_rx, streaming chanend ?c_spdif_rx,
@@ -422,7 +422,7 @@ void usb_audio_io(chanend ?c_aud_in,
#endif #endif
) )
{ {
#ifdef MIXER #if (MIXER)
chan c_mix_out; chan c_mix_out;
#endif #endif
@@ -446,7 +446,7 @@ void usb_audio_io(chanend ?c_aud_in,
par par
{ {
#ifdef MIXER #if (MIXER)
/* Mixer cores(s) */ /* Mixer cores(s) */
{ {
thread_speed(); thread_speed();
@@ -464,7 +464,7 @@ void usb_audio_io(chanend ?c_aud_in,
/* Audio I/O core (pars additional S/PDIF TX Core) */ /* Audio I/O core (pars additional S/PDIF TX Core) */
{ {
thread_speed(); thread_speed();
#ifdef MIXER #if (MIXER)
#define AUDIO_CHANNEL c_mix_out #define AUDIO_CHANNEL c_mix_out
#else #else
#define AUDIO_CHANNEL c_aud_in #define AUDIO_CHANNEL c_aud_in
@@ -531,7 +531,7 @@ int main()
#endif #endif
#endif #endif
#ifdef MIXER #if (MIXER)
chan c_mix_ctl; chan c_mix_ctl;
#endif #endif
@@ -608,7 +608,7 @@ int main()
, c_ea_data , c_ea_data
#endif #endif
#endif #endif
#ifdef MIXER #if (MIXER)
, c_mix_ctl , c_mix_ctl
#endif #endif
, c_clk_int, c_clk_ctl, dfuInterface , c_clk_int, c_clk_ctl, dfuInterface
@@ -628,7 +628,7 @@ int main()
#if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE) #if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE)
, c_spdif_tx , c_spdif_tx
#endif #endif
#ifdef MIXER #if (MIXER)
, c_mix_ctl , c_mix_ctl
#endif #endif
, c_spdif_rx, c_adat_rx, c_clk_ctl, c_clk_int , c_spdif_rx, c_adat_rx, c_clk_ctl, c_clk_int

View File

@@ -1,12 +1,21 @@
// Copyright 2018-2021 XMOS LIMITED. // Copyright 2018-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1. // This Software is subject to the terms of the XMOS Public Licence: Version 1.
#define MAX_MIX_COUNT 8 #include "xua.h"
#define MIX_INPUTS 18
#ifndef MAX_MIX_COUNT
#error
#endif
#ifndef MIX_INPUTS
#error
#endif
#if (MAX_MIX_COUNT > 0)
#define DOMIX_TOP(i) \ #define DOMIX_TOP(i) \
.cc_top doMix##i.function,doMix##i; \ .cc_top doMix##i.function,doMix##i; \
.align 4 ;\ .align 16 ;\
.globl doMix##i ;\ .globl doMix##i ;\
.type doMix##i, @function ;\ .type doMix##i, @function ;\
.globl doMix##i##.nstackwords ;\ .globl doMix##i##.nstackwords ;\
@@ -124,7 +133,7 @@ DOMIX_BOT(7)
#undef BODY #undef BODY
#define N MAX_MIX_COUNT #define N MAX_MIX_COUNT
.cc_top setPtr.function,setPtr; .cc_top setPtr.function,setPtr;
.align 4 ; .align 16 ;
.globl setPtr; .globl setPtr;
.type setPtr, @function .type setPtr, @function
.globl setPtr.nstackwords; .globl setPtr.nstackwords;
@@ -173,5 +182,5 @@ setPtr_go:
#undef N #undef N
#undef BODY #undef BODY
#endif

View File

@@ -1,25 +1,39 @@
// Copyright 2011-2022 XMOS LIMITED. // Copyright 2011-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1. // This Software is subject to the terms of the XMOS Public Licence: Version 1.
#define XASSERT_UNIT MIXER
#include "xassert.h"
#include <xs1.h> #include <xs1.h>
#include <print.h>
#include "xua.h" #include "xua.h"
#include "xc_ptr.h"
#include "xua_commands.h" #include "xua_commands.h"
#include "dbcalc.h" #include "dbcalc.h"
#ifdef MIXER /* FAST_MIXER has a bit of a nasty implentation but is more efficient */
#ifndef FAST_MIXER
#define FAST_MIXER (1)
#endif
/* FAST_MIXER has a bit of a nasty implentation but is more effcient */ #if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS) || !FAST_MIXER
#define FAST_MIXER 1 #include "xc_ptr.h"
#endif
//#ifdef OUT_VOLUME_IN_MIXER #if (MIXER)
#if (OUT_VOLUME_IN_MIXER)
static unsigned int multOut_array[NUM_USB_CHAN_OUT + 1]; static unsigned int multOut_array[NUM_USB_CHAN_OUT + 1];
static xc_ptr multOut; unsafe
//#endif {
//#ifdef IN_VOLUME_IN_MIXER int volatile * unsafe multOut = multOut_array;
}
#endif
#if (IN_VOLUME_IN_MIXER)
static unsigned int multIn_array[NUM_USB_CHAN_IN + 1]; static unsigned int multIn_array[NUM_USB_CHAN_IN + 1];
static xc_ptr multIn; unsafe
//#endif {
int volatile * unsafe multIn = multIn_array;
}
#endif
#if defined (LEVEL_METER_LEDS) || defined (LEVEL_METER_HOST) #if defined (LEVEL_METER_LEDS) || defined (LEVEL_METER_HOST)
static unsigned abs(int x) static unsigned abs(int x)
@@ -35,35 +49,38 @@ static unsigned abs(int x)
} }
#endif #endif
static const int SOURCE_COUNT = NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT + 1;
static int samples_array[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT + 1]; /* One larger for an "off" channel for mixer sources" */ static int samples_array[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT + 1]; /* One larger for an "off" channel for mixer sources" */
xc_ptr samples; static int samples_to_host_map_array[NUM_USB_CHAN_IN];
static int samples_to_device_map_array[NUM_USB_CHAN_OUT];
unsafe unsafe
{ {
static int volatile * const unsafe ptr_samples = samples_array; int volatile * const unsafe ptr_samples = samples_array;
int volatile * const unsafe samples_to_host_map = samples_to_host_map_array;
int volatile * const unsafe samples_to_device_map = samples_to_device_map_array;
} }
int savedsamples2[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT]; #if (MAX_MIX_COUNT > 0)
int mix_mult_array[MAX_MIX_COUNT * MIX_INPUTS];
int samples_to_host_map_array[NUM_USB_CHAN_IN]; #if (FAST_MIXER == 0)
xc_ptr samples_to_host_map; int mix_map_array[MAX_MIX_COUNT * MIX_INPUTS];
int samples_to_device_map_array[NUM_USB_CHAN_OUT];
xc_ptr samples_to_device_map;
#if MAX_MIX_COUNT > 0
int mix_mult_array[MAX_MIX_COUNT][MIX_INPUTS];
xc_ptr mix_mult;
#define write_word_to_mix_mult(x,y,val) write_via_xc_ptr_indexed(mix_mult,((x)*MIX_INPUTS)+(y), val)
#define mix_mult_slice(x) (mix_mult + x * MIX_INPUTS * sizeof(int))
#ifndef FAST_MIXER
int mix_map_array[MAX_MIX_COUNT][MIX_INPUTS];
xc_ptr mix_map;
#define write_word_to_mix_map(x,y,val) write_via_xc_ptr_indexed(mix_map,((x)*MIX_INPUTS)+(y), val)
#define mix_map_slice(x) (mix_map + x * MIX_INPUTS * sizeof(int))
#endif
#endif #endif
unsafe
{
int volatile * const unsafe mix_mult = mix_mult_array;
#if (FAST_MIXER == 0)
int volatile * const unsafe mix_map = mix_map_array;
#endif
}
#define slice(a, i) (a + i * MIX_INPUTS)
#endif
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
/* Arrays for level data */ /* Arrays for level data */
int samples_to_host_inputs[NUM_USB_CHAN_IN]; /* Audio transmitted to host i.e. device inputs */ int samples_to_host_inputs[NUM_USB_CHAN_IN]; /* Audio transmitted to host i.e. device inputs */
xc_ptr samples_to_host_inputs_ptr; xc_ptr samples_to_host_inputs_ptr;
@@ -77,19 +94,6 @@ static int samples_from_host_streams[NUM_USB_CHAN_OUT]; /* Peak samples for audi
static int samples_mixer_outputs[MAX_MIX_COUNT]; /* Peak samples out of the mixer */ static int samples_mixer_outputs[MAX_MIX_COUNT]; /* Peak samples out of the mixer */
xc_ptr samples_mixer_outputs_ptr; xc_ptr samples_mixer_outputs_ptr;
#if 0
#pragma xta command "add exclusion mixer1_rate_change"
#pragma xta command "analyse path mixer1_req mixer1_req"
#pragma xta command "set required - 10400 ns" /* 96kHz */
#endif
#if 0
#pragma xta command "add exclusion mixer2_rate_change"
#pragma xta command "analyse path mixer2_req mixer2_req"
#pragma xta command "set required - 10400 ns" /* 96kHz */
#endif
#if defined (LEVEL_METER_LEDS) || defined (LEVEL_METER_HOST)
static inline void ComputeMixerLevel(int sample, int i) static inline void ComputeMixerLevel(int sample, int i)
{ {
int x; int x;
@@ -108,42 +112,40 @@ static inline void ComputeMixerLevel(int sample, int i)
} }
} }
#endif #endif
#ifdef FAST_MIXER
#if (FAST_MIXER)
void setPtr(int src, int dst, int mix); void setPtr(int src, int dst, int mix);
int doMix0(xc_ptr samples, xc_ptr mult); int doMix0(volatile int * const unsafe samples, volatile int * const unsafe mult);
int doMix1(xc_ptr samples, xc_ptr mult); int doMix1(volatile int * const unsafe samples, volatile int * const unsafe mult);
int doMix2(xc_ptr samples, xc_ptr mult); int doMix2(volatile int * const unsafe samples, volatile int * const unsafe mult);
int doMix3(xc_ptr samples, xc_ptr mult); int doMix3(volatile int * const unsafe samples, volatile int * const unsafe mult);
int doMix4(xc_ptr samples, xc_ptr mult); int doMix4(volatile int * const unsafe samples, volatile int * const unsafe mult);
int doMix5(xc_ptr samples, xc_ptr mult); int doMix5(volatile int * const unsafe samples, volatile int * const unsafe mult);
int doMix6(xc_ptr samples, xc_ptr mult); int doMix6(volatile int * const unsafe samples, volatile int * const unsafe mult);
int doMix7(xc_ptr samples, xc_ptr mult); int doMix7(volatile int * const unsafe samples, volatile int * const unsafe mult);
int doMix8(xc_ptr samples, xc_ptr mult);
#else #else
/* DO NOT inline, causes 10.4.2 tools to add extra loads in loop */
/* At 18 x 12dB we could get 64 x bigger */
#pragma unsafe arrays #pragma unsafe arrays
static inline int doMix(xc_ptr samples, xc_ptr ptr, xc_ptr mult) static inline int doMix(volatile int * unsafe samples, volatile int * unsafe const mixMap, volatile int * const unsafe mult)
{ {
int h=0; int h=0;
int l=0; int l=0;
/* By breaking up the loop we keep things in the encoding for ldw (0-11) */
#pragma loop unroll #pragma loop unroll
for (int i=0; i<MIX_INPUTS; i++) for (int i=0; i<MIX_INPUTS; i++)
{ unsafe{
int sample; int sample;
int index; int source;
int m; int weight;
read_via_xc_ptr_indexed(index, ptr, i); read_via_xc_ptr_indexed(source, mixMap, i);
read_via_xc_ptr_indexed(sample,samples,index); sample = samples[source];
read_via_xc_ptr_indexed(m, mult, i); read_via_xc_ptr_indexed(weight, mult, i);
{h,l} = macs(sample, m, h, l);
{h,l} = macs(sample, weight, h, l);
} }
#if 1
/* Perform saturation */ /* Perform saturation */
l = sext(h, 25); l = sext(h, XUA_MIXER_MULT_FRAC_BITS);
if(l != h) if(l != h)
{ {
@@ -152,15 +154,14 @@ static inline int doMix(xc_ptr samples, xc_ptr ptr, xc_ptr mult)
else else
h = (0x7fffff00>>7); h = (0x7fffff00>>7);
} }
#endif
return h<<7; return h<<7;
} }
#endif #endif
#pragma unsafe arrays #pragma unsafe arrays
static inline void GiveSamplesToHost(chanend c, xc_ptr ptr, xc_ptr multIn) static inline void GiveSamplesToHost(chanend c, volatile int * unsafe hostMap)
{ {
#if defined(IN_VOLUME_IN_MIXER) && defined(IN_VOLUME_AFTER_MIX) #if (IN_VOLUME_IN_MIXER && IN_VOLUME_AFTER_MIX)
int mult; int mult;
int h; int h;
unsigned l; unsigned l;
@@ -170,23 +171,26 @@ static inline void GiveSamplesToHost(chanend c, xc_ptr ptr, xc_ptr multIn)
for (int i=0; i<NUM_USB_CHAN_IN; i++) for (int i=0; i<NUM_USB_CHAN_IN; i++)
{ {
int sample; int sample;
int index;
#if MAX_MIX_COUNT > 0 #if (MAX_MIX_COUNT > 0)
read_via_xc_ptr_indexed(index,ptr,i);
#else
index = i + NUM_USB_CHAN_OUT;
#endif
unsafe unsafe
{ {
//read_via_xc_ptr_indexed(sample,samples,index); sample = ptr_samples[hostMap[i]];
sample = ptr_samples[index];
} }
#else
unsafe
{
sample = ptr_samples[i + NUM_USB_CHAN_OUT];
}
#endif
#if defined(IN_VOLUME_IN_MIXER) && defined(IN_VOLUME_AFTER_MIX) #if (IN_VOLUME_IN_MIXER && IN_VOLUME_AFTER_MIX)
#warning IN Vols in mixer, AFTER mix & map #warning IN Vols in mixer, AFTER mix & map
//asm("ldw %0, %1[%2]":"=r"(mult):"r"(multIn),"r"(i));
read_via_xc_ptr_indexed(mult, multIn, i); unsafe
{
mult = multIn[i];
}
{h, l} = macs(mult, sample, 0, 0); {h, l} = macs(mult, sample, 0, 0);
//h <<= 3 done on other side */ //h <<= 3 done on other side */
@@ -209,7 +213,7 @@ static inline void GetSamplesFromHost(chanend c)
for (int i=0; i<NUM_USB_CHAN_OUT; i++) for (int i=0; i<NUM_USB_CHAN_OUT; i++)
unsafe { unsafe {
int sample, x; int sample, x;
#if defined(OUT_VOLUME_IN_MIXER) && !defined(OUT_VOLUME_AFTER_MIX) #if (OUT_VOLUME_IN_MIXER && !OUT_VOLUME_AFTER_MIX)
int mult; int mult;
int h; int h;
unsigned l; unsigned l;
@@ -226,50 +230,47 @@ static inline void GetSamplesFromHost(chanend c)
} }
#endif #endif
#if defined(OUT_VOLUME_IN_MIXER) && !defined(OUT_VOLUME_AFTER_MIX) #if (OUT_VOLUME_IN_MIXER && !OUT_VOLUME_AFTER_MIX)
#warning OUT Vols in mixer, BEFORE mix & map #warning OUT Vols in mixer, BEFORE mix & map
read_via_xc_ptr_indexed(mult, multOut, i); mult = multOut[i];
{h, l} = macs(mult, sample, 0, 0); {h, l} = macs(mult, sample, 0, 0);
h<<=3; h<<=3;
#if (STREAM_FORMAT_OUTPUT_RESOLUTION_32BIT_USED == 1) #if (STREAM_FORMAT_OUTPUT_RESOLUTION_32BIT_USED == 1)
h |= (l >>29)& 0x7; // Note: This step is not required if we assume sample depth is 24bit (rather than 32bit) h |= (l >>29)& 0x7; // Note: This step is not required if we assume sample depth is 24bit (rather than 32bit)
// Note: We need all 32bits for Native DSD // Note: We need all 32bits for Native DSD
#endif
sample = h;
#endif #endif
write_via_xc_ptr_indexed(multOut, index, val);
write_via_xc_ptr_indexed(samples_array, i, h);
#else
ptr_samples[i] = sample; ptr_samples[i] = sample;
#endif
} }
} }
#endif #endif
} }
#pragma unsafe arrays #pragma unsafe arrays
static inline void GiveSamplesToDevice(chanend c, xc_ptr ptr, xc_ptr multOut) static inline void GiveSamplesToDevice(chanend c, volatile int * unsafe deviceMap)
{ {
#if(NUM_USB_CHAN_OUT == 0) #if (NUM_USB_CHAN_OUT == 0)
outuint(c, 0); outuint(c, 0);
#else #else
#pragma loop unroll #pragma loop unroll
for (int i=0; i<NUM_USB_CHAN_OUT; i++) for (int i=0; i<NUM_USB_CHAN_OUT; i++)
{ {
int sample, x; int sample, x;
#if defined(OUT_VOLUME_IN_MIXER) && defined(OUT_VOLUME_AFTER_MIX) #if (OUT_VOLUME_IN_MIXER && OUT_VOLUME_AFTER_MIX)
int mult; int mult;
int h; int h;
unsigned l; unsigned l;
#endif #endif
int index; int index;
#if MAX_MIX_COUNT > 0 #if (MAX_MIX_COUNT > 0)
/* If mixer turned on sort out the channel mapping */ /* If mixer turned on sort out the channel mapping */
unsafe
/* Read pointer to sample from the map */ {
read_via_xc_ptr_indexed(index, ptr, i); /* Read index to sample from the map then Read the actual sample value */
sample = ptr_samples[deviceMap[i]];
/* Read the actual sample value */ }
read_via_xc_ptr_indexed(sample, samples, index);
#else #else
unsafe unsafe
{ {
@@ -278,16 +279,19 @@ static inline void GiveSamplesToDevice(chanend c, xc_ptr ptr, xc_ptr multOut)
} }
#endif #endif
#if defined(OUT_VOLUME_IN_MIXER) && defined(OUT_VOLUME_AFTER_MIX) #if (OUT_VOLUME_IN_MIXER && OUT_VOLUME_AFTER_MIX)
/* Do volume control processing */ /* Do volume control processing */
#warning OUT Vols in mixer, AFTER mix & map #warning OUT Vols in mixer, AFTER mix & map
read_via_xc_ptr_indexed(mult, multOut, i); unsafe
{
mult = multOut[i];
}
{h, l} = macs(mult, sample, 0, 0); {h, l} = macs(mult, sample, 0, 0);
h<<=3; // Shift used to be done in audio thread but now done here incase of 32bit support h<<=3; // Shift used to be done in audio thread but now done here incase of 32bit support
#error
#if (STREAM_FORMAT_OUTPUT_RESOLUTION_32BIT_USED == 1) #if (STREAM_FORMAT_OUTPUT_RESOLUTION_32BIT_USED == 1)
h |= (l >>29)& 0x7; // Note: This step is not required if we assume sample depth is 24bit (rather than 32bit) h |= (l >>29)& 0x7; // Note: This step is not required if we assume sample depth is 24bit (rather than 32bit)
// Note: We need all 32bits for Native DSD // Note: We need all 32bits for Native DSD
#endif #endif
outuint(c, h); outuint(c, h);
#else #else
@@ -300,14 +304,14 @@ static inline void GiveSamplesToDevice(chanend c, xc_ptr ptr, xc_ptr multOut)
#pragma unsafe arrays #pragma unsafe arrays
static inline void GetSamplesFromDevice(chanend c) static inline void GetSamplesFromDevice(chanend c)
{ {
#if defined(IN_VOLUME_IN_MIXER) && !defined(IN_VOLUME_AFTER_MIX) #if (IN_VOLUME_IN_MIXER && IN_VOLUME_AFTER_MIX)
int mult; int mult;
int h; int h;
unsigned l; unsigned l;
#endif #endif
#pragma loop unroll #pragma loop unroll
for (int i=0;i<NUM_USB_CHAN_IN;i++) for (int i=0; i<NUM_USB_CHAN_IN; i++)
{ {
int sample; int sample;
int x; int x;
@@ -327,22 +331,24 @@ static inline void GetSamplesFromDevice(chanend c)
} }
#endif #endif
#if defined(IN_VOLUME_IN_MIXER) && !defined(IN_VOLUME_AFTER_MIX) #if (IN_VOLUME_IN_MIXER && IN_VOLUME_AFTER_MIX)
/* Read relevant multiplier */ /* Volume processing - read relevant multiplier */
read_via_xc_ptr_indexed(mult, multIn, i); unsafe
{
mult = multIn[i];
}
/* Do the multiply */ /* Do the multiply */
{h, l} = macs(mult, sample, 0, 0); {h, l} = macs(mult, sample, 0, 0);
h <<=3; h <<= 3;
write_via_xc_ptr_indexed(samples_array, NUM_USB_CHAN_OUT+i, h); sample = h;
#else #endif
/* No volume processing */
unsafe unsafe
{ {
ptr_samples[NUM_USB_CHAN_OUT + i] = sample; assert((XUA_MIXER_OFFSET_IN + i) < (NUM_USB_CHAN_IN + NUM_USB_CHAN_OUT));
ptr_samples[XUA_MIXER_OFFSET_IN + i] = sample;
} }
#endif }
}
} }
static int mixer1_mix2_flag = (DEFAULT_FREQ > 96000); static int mixer1_mix2_flag = (DEFAULT_FREQ > 96000);
@@ -353,12 +359,14 @@ static void mixer1(chanend c_host, chanend c_mix_ctl, chanend c_mixer2)
#if (MAX_MIX_COUNT > 0) #if (MAX_MIX_COUNT > 0)
int mixed; int mixed;
#endif #endif
#if (MAX_MIX_COUNT > 0) || (IN_VOLUME_IN_MIXER) || (OUT_VOLUME_IN_MIXER) || defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
unsigned cmd; unsigned cmd;
unsigned char ct;
#endif
unsigned request = 0; unsigned request = 0;
while (1) while (1)
{ {
#pragma xta endpoint "mixer1_req"
/* Request from audio()/mixer2() */ /* Request from audio()/mixer2() */
request = inuint(c_mixer2); request = inuint(c_mixer2);
@@ -369,28 +377,62 @@ static void mixer1(chanend c_host, chanend c_mix_ctl, chanend c_mixer2)
/* Sync */ /* Sync */
outuint(c_mixer2, 0); outuint(c_mixer2, 0);
#endif #endif
/* Between request to decouple and response ~ 400nS latency for interrupt to fire */ /* Between request to decouple and response ~ 400nS latency for interrupt to fire */
#if (MAX_MIX_COUNT > 0) || (IN_VOLUME_IN_MIXER) || (OUT_VOLUME_IN_MIXER) || defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
select select
{ {
case inuint_byref(c_mix_ctl, cmd): /* Check if EP0 intends to send us a control command */
case inct_byref(c_mix_ctl, ct):
{ {
int mix, index, val; int mix, index, val;
/* Handshake back to tell EP0 we are ready for an update */
outct(c_mix_ctl, XS1_CT_END);
/* Receive command from EP0 */
cmd = inuint(c_mix_ctl);
/* Interpret control command */
switch (cmd) switch (cmd)
{ {
#if MAX_MIX_COUNT > 0 #if (MAX_MIX_COUNT > 0)
case SET_SAMPLES_TO_HOST_MAP: case SET_SAMPLES_TO_HOST_MAP:
index = inuint(c_mix_ctl); {
val = inuint(c_mix_ctl); int dst = inuint(c_mix_ctl);
inct(c_mix_ctl); int src = inuint(c_mix_ctl);
write_via_xc_ptr_indexed(samples_to_host_map, index, val); inct(c_mix_ctl);
assert((dst < NUM_USB_CHAN_IN) && msg("Host map destination out of range"));
assert((src < SOURCE_COUNT) && msg("Host map source out of range"));
if((dst < NUM_USB_CHAN_IN) && (src < SOURCE_COUNT))
{
unsafe
{
samples_to_host_map[dst] = src;
}
}
}
break; break;
case SET_SAMPLES_TO_DEVICE_MAP: case SET_SAMPLES_TO_DEVICE_MAP:
index = inuint(c_mix_ctl); {
val = inuint(c_mix_ctl); int dst = inuint(c_mix_ctl);
inct(c_mix_ctl); int src = inuint(c_mix_ctl);
write_via_xc_ptr_indexed(samples_to_device_map,index,val); inct(c_mix_ctl);
assert((dst < NUM_USB_CHAN_OUT) && msg("Device map destination out of range"));
assert((src < SOURCE_COUNT) && msg("Device map source out of range"));
if((dst < NUM_USB_CHAN_OUT) && (src < SOURCE_COUNT))
{
unsafe
{
samples_to_device_map[dst] = src;
}
}
}
break; break;
case SET_MIX_MULT: case SET_MIX_MULT:
@@ -399,40 +441,80 @@ static void mixer1(chanend c_host, chanend c_mix_ctl, chanend c_mixer2)
val = inuint(c_mix_ctl); val = inuint(c_mix_ctl);
inct(c_mix_ctl); inct(c_mix_ctl);
write_word_to_mix_mult(mix, index, val); assert((mix < MAX_MIX_COUNT) && msg("Mix mult mix out of range"));
assert((index < MIX_INPUTS) && msg("Mix mult index out of range"));
if((index < MIX_INPUTS) && (mix < MAX_MIX_COUNT))
{
unsafe
{
mix_mult[(mix * MIX_INPUTS) + index] = val;
}
}
break; break;
case SET_MIX_MAP: case SET_MIX_MAP:
mix = inuint(c_mix_ctl); {
index = inuint(c_mix_ctl); /* mixer input */ unsigned mix = inuint(c_mix_ctl);
val = inuint(c_mix_ctl); /* source */ unsigned input = inuint(c_mix_ctl); /* mixer input */
inct(c_mix_ctl); unsigned source = inuint(c_mix_ctl); /* source */
#ifdef FAST_MIXER inct(c_mix_ctl);
setPtr(index, val, mix);
assert((mix < MAX_MIX_COUNT) && msg("Mix map mix out of range"));
assert((input < MIX_INPUTS) && msg("Mix map index out of range"));
assert((source < SOURCE_COUNT) && msg("Mix map source out of range"));
if((input < MIX_INPUTS) && (mix < MAX_MIX_COUNT) && (source < SOURCE_COUNT))
{
#if (FAST_MIXER)
setPtr(input, source, mix);
#else #else
write_word_to_mix_map(mix, index, val); unsafe
{
mix_map[(mix * MIX_INPUTS) + input] = source;
}
#endif #endif
}
}
break; break;
#endif /* if MAX_MIX_COUNT > 0 */ #endif /* if MAX_MIX_COUNT > 0 */
#ifdef IN_VOLUME_IN_MIXER
#if (IN_VOLUME_IN_MIXER)
case SET_MIX_IN_VOL: case SET_MIX_IN_VOL:
index = inuint(c_mix_ctl); index = inuint(c_mix_ctl);
val = inuint(c_mix_ctl); val = inuint(c_mix_ctl);
inct(c_mix_ctl); inct(c_mix_ctl);
write_via_xc_ptr_indexed(multIn, index, val); assert((index < (NUM_USB_CHAN_IN + 1)) && msg("In volume index out of range"));
if(index < NUM_USB_CHAN_IN + 1)
{
unsafe
{
multIn[index] = val;
}
}
break; break;
#endif #endif
#ifdef OUT_VOLUME_IN_MIXER #if (OUT_VOLUME_IN_MIXER)
case SET_MIX_OUT_VOL: case SET_MIX_OUT_VOL:
index = inuint(c_mix_ctl); index = inuint(c_mix_ctl);
val = inuint(c_mix_ctl); val = inuint(c_mix_ctl);
inct(c_mix_ctl); inct(c_mix_ctl);
write_via_xc_ptr_indexed(multOut, index, val); assert((index < (NUM_USB_CHAN_OUT + 1)) && msg("Out volume index out of range"));
if(index < NUM_USB_CHAN_OUT + 1)
{
unsafe
{
multOut[index] = val;
}
}
break; break;
#endif #endif
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
/* Peak samples of stream from host to device (via USB) */ /* Peak samples of stream from host to device (via USB) */
case GET_STREAM_LEVELS: case GET_STREAM_LEVELS:
index = inuint(c_mix_ctl); index = inuint(c_mix_ctl);
@@ -441,42 +523,6 @@ static void mixer1(chanend c_host, chanend c_mix_ctl, chanend c_mixer2)
outct(c_mix_ctl, XS1_CT_END); outct(c_mix_ctl, XS1_CT_END);
samples_from_host_streams[index] = 0; samples_from_host_streams[index] = 0;
break; break;
case GET_INPUT_LEVELS:
index = inuint(c_mix_ctl);
chkct(c_mix_ctl, XS1_CT_END);
#ifdef LEVEL_METER_LEDS
/* Level LEDS process reseting samples_to_host_inputs
* Other side makes sure we don't miss a peak */
//val = samples_to_host_inputs_buff[index];
//samples_to_host_inputs_buff[index] = 0;
/* Access funcs used to avoid disjointness check */
read_via_xc_ptr_indexed(val, samples_to_host_inputs_buff_ptr, index);
write_via_xc_ptr_indexed(samples_to_host_inputs_buff_ptr, index, 0);
#else
/* We dont have a level LEDs process, so reset ourselves */
//val = samples_to_host_inputs[index];
//samples_to_host_inputs[index] = 0;
/* Access funcs used to avoid disjointness check */
read_via_xc_ptr_indexed(val, samples_to_host_inputs_ptr, index);
write_via_xc_ptr_indexed(samples_to_host_inputs_ptr, index, 0);
#endif
outuint(c_mix_ctl, val);
outct(c_mix_ctl, XS1_CT_END);
break;
#if (MAX_MIX_COUNT > 0)
/* Peak samples of the mixer outputs */
case GET_OUTPUT_LEVELS:
index = inuint(c_mix_ctl);
chkct(c_mix_ctl, XS1_CT_END);
read_via_xc_ptr_indexed(val, samples_mixer_outputs, index);
write_via_xc_ptr_indexed(samples_mixer_outputs, index, 0);
//val = samples_mixer_outputs[index];
//samples_mixer_outputs[index] = 0;
outuint(c_mix_ctl, val);
outct(c_mix_ctl, XS1_CT_END);
break;
#endif #endif
} }
break; break;
@@ -484,14 +530,13 @@ static void mixer1(chanend c_host, chanend c_mix_ctl, chanend c_mixer2)
default: default:
/* Select default */ /* Select default */
break; break;
} } // select
#endif
/* Get response from decouple */ /* Get response from decouple */
if(testct(c_host)) if(testct(c_host))
{ {
int sampFreq; int sampFreq;
#pragma xta endpoint "mixer1_rate_change"
unsigned command = inct(c_host); unsigned command = inct(c_host);
switch(command) switch(command)
@@ -520,9 +565,8 @@ static void mixer1(chanend c_host, chanend c_mix_ctl, chanend c_mixer2)
#pragma loop unroll #pragma loop unroll
/* Reset the mix values back to 0 */ /* Reset the mix values back to 0 */
for (int i=0;i<MAX_MIX_COUNT;i++) for (int i=0; i<MAX_MIX_COUNT; i++)
{ {
//write_via_xc_ptr_indexed(samples, (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + i), 0);
unsafe unsafe
{ {
ptr_samples[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + i] = 0; ptr_samples[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + i] = 0;
@@ -535,21 +579,24 @@ static void mixer1(chanend c_host, chanend c_mix_ctl, chanend c_mixer2)
} }
else else
{ {
#if MAX_MIX_COUNT > 0 #if (MAX_MIX_COUNT > 0)
GetSamplesFromHost(c_host); GetSamplesFromHost(c_host);
GiveSamplesToHost(c_host, samples_to_host_map, multIn); GiveSamplesToHost(c_host, samples_to_host_map);
/* Sync with mixer 2 (once it has swapped samples with audiohub) */ /* Sync with mixer 2 (once it has swapped samples with audiohub) */
outuint(c_mixer2, 0); outuint(c_mixer2, 0);
inuint(c_mixer2); inuint(c_mixer2);
/* Do the mixing */ /* Do the mixing */
#ifdef FAST_MIXER unsafe
mixed = doMix0(samples, mix_mult_slice(0)); {
#if (FAST_MIXER)
mixed = doMix0(ptr_samples, slice(mix_mult, 0));
#else #else
mixed = doMix(samples, mix_map_slice(0),mix_mult_slice(0)); mixed = doMix(ptr_samples, slice(mix_map, 0), slice(mix_mult, 0));
#endif #endif
write_via_xc_ptr_indexed(samples_array, (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 0), mixed); ptr_samples[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 0] = mixed;
}
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS) #if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
ComputeMixerLevel(mixed, 0); ComputeMixerLevel(mixed, 0);
@@ -560,40 +607,46 @@ static void mixer1(chanend c_host, chanend c_mix_ctl, chanend c_mixer2)
#endif #endif
{ {
#if MAX_MIX_COUNT > 2 #if (MAX_MIX_COUNT > 2)
#ifdef FAST_MIXER unsafe
mixed = doMix2(samples, mix_mult_slice(2)); {
#if (FAST_MIXER)
mixed = doMix2(ptr_samples, slice(mix_mult, 2));
#else #else
mixed = doMix(samples, mix_map_slice(2),mix_mult_slice(2)); mixed = doMix(ptr_samples, slice(mix_map, 2), slice(mix_mult, 2));
#endif #endif
write_via_xc_ptr_indexed(samples_array, (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 2), mixed); ptr_samples[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 2] = mixed;
}
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS) #if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
ComputeMixerLevel(mixed, 2); ComputeMixerLevel(mixed, 2);
#endif #endif
#endif #endif
#if MAX_MIX_COUNT > 4 #if (MAX_MIX_COUNT > 4)
#ifdef FAST_MIXER unsafe
mixed = doMix4(samples, mix_mult_slice(4)); {
#if (FAST_MIXER)
mixed = doMix4(ptr_samples, slice(mix_mult, 4));
#else #else
mixed = doMix(samples, mix_map_slice(4),mix_mult_slice(4)); mixed = doMix(ptr_samples, slice(mix_map, 4), slice(mix_mult, 4));
#endif #endif
write_via_xc_ptr_indexed(samples_array, (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 4), mixed); ptr_samples[XUA_MIXER_OFFSET_MIX + 4] = mixed;
}
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS) #if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
ComputeMixerLevel(mixed, 4); ComputeMixerLevel(mixed, 4);
#endif #endif
#endif #endif
#if MAX_MIX_COUNT > 6 #if (MAX_MIX_COUNT > 6)
#ifdef FAST_MIXER unsafe
mixed = doMix6(samples, mix_mult_slice(6)); {
#if (FAST_MIXER)
mixed = doMix6(ptr_samples, slice(mix_mult, 6));
#else #else
mixed = doMix(samples, mix_map_slice(6),mix_mult_slice(6)); mixed = doMix(ptr_samples, slice(mix_map, 6), slice(mix_mult, 6));
#endif #endif
write_via_xc_ptr_indexed(samples, (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 6), mixed); ptr_samples[XUA_MIXER_OFFSET_MIX + 6] = mixed;
}
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS) #if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
ComputeMixerLevel(mixed, 6); ComputeMixerLevel(mixed, 6);
#endif #endif
@@ -601,10 +654,10 @@ static void mixer1(chanend c_host, chanend c_mix_ctl, chanend c_mixer2)
} }
#else /* IF MAX_MIX_COUNT > 0 */ #else /* IF MAX_MIX_COUNT > 0 */
/* No mixes, this thread runs on its own doing just volume */ /* No mixes, this thread runs on its own doing just volume */
GiveSamplesToDevice(c_mixer2, samples_to_device_map, multOut); GiveSamplesToDevice(c_mixer2, samples_to_device_map);
GetSamplesFromDevice(c_mixer2); GetSamplesFromDevice(c_mixer2);
GetSamplesFromHost(c_host); GetSamplesFromHost(c_host);
GiveSamplesToHost(c_host, samples_to_host_map, multIn); GiveSamplesToHost(c_host, samples_to_host_map);
#endif #endif
} }
} }
@@ -621,7 +674,6 @@ static void mixer2(chanend c_mixer1, chanend c_audio)
while (1) while (1)
{ {
#pragma xta endpoint "mixer2_req"
request = inuint(c_audio); request = inuint(c_audio);
/* Forward the request on */ /* Forward the request on */
@@ -633,7 +685,6 @@ static void mixer2(chanend c_mixer1, chanend c_audio)
if(testct(c_mixer1)) if(testct(c_mixer1))
{ {
int sampFreq; int sampFreq;
#pragma xta endpoint "mixer2_rate_change"
unsigned command = inct(c_mixer1); unsigned command = inct(c_mixer1);
switch(command) switch(command)
@@ -660,8 +711,8 @@ static void mixer2(chanend c_mixer1, chanend c_audio)
} }
for (int i=0;i<MAX_MIX_COUNT;i++) for (int i=0;i<MAX_MIX_COUNT;i++)
{ unsafe{
write_via_xc_ptr_indexed(samples, (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + i), 0); ptr_samples[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + i] = 0;
} }
/* Wait for handshake and pass on */ /* Wait for handshake and pass on */
@@ -670,7 +721,7 @@ static void mixer2(chanend c_mixer1, chanend c_audio)
} }
else else
{ {
GiveSamplesToDevice(c_audio, samples_to_device_map, multOut); GiveSamplesToDevice(c_audio, samples_to_device_map);
GetSamplesFromDevice(c_audio); GetSamplesFromDevice(c_audio);
/* Sync with mixer 1 (once it has swapped samples with the buffering sub-system) */ /* Sync with mixer 1 (once it has swapped samples with the buffering sub-system) */
@@ -678,61 +729,67 @@ static void mixer2(chanend c_mixer1, chanend c_audio)
outuint(c_mixer1, 0); outuint(c_mixer1, 0);
/* Do the mixing */ /* Do the mixing */
#if MAX_MIX_COUNT > 1 #if (MAX_MIX_COUNT > 1)
#ifdef FAST_MIXER unsafe
mixed = doMix1(samples, mix_mult_slice(1)); {
#if (FAST_MIXER)
mixed = doMix1(ptr_samples, slice(mix_mult, 1));
#else #else
mixed = doMix(samples, mix_map_slice(1),mix_mult_slice(1)); mixed = doMix(ptr_samples, slice(mix_map, 1), slice(mix_mult, 1));
#endif #endif
ptr_samples[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 1] = mixed;
write_via_xc_ptr_indexed(samples, (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 1), mixed); }
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS) #if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
ComputeMixerLevel(mixed, 1); ComputeMixerLevel(mixed, 1);
#endif #endif
#endif #endif
#if (MAX_FREQ > 96000) #if (MAX_FREQ > 96000)
/* Fewer mixes when running higher than 96kHz */ /* Fewer mixes when running higher than 96kHz */
if (!mixer2_mix2_flag) if (!mixer2_mix2_flag)
#endif #endif
{ {
#if MAX_MIX_COUNT > 3 #if (MAX_MIX_COUNT > 3)
#ifdef FAST_MIXER unsafe
mixed = doMix3(samples, mix_mult_slice(3)); {
#if (FAST_MIXER)
mixed = doMix3(ptr_samples, slice(mix_mult, 3));
#else #else
mixed = doMix(samples, mix_map_slice(3),mix_mult_slice(3)); mixed = doMix(ptr_samples, slice(mix_map, 3), slice(mix_mult, 3));
#endif #endif
ptr_samples[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 3] = mixed;
write_via_xc_ptr_indexed(samples, (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 3), mixed); }
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS) #if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
ComputeMixerLevel(mixed, 3); ComputeMixerLevel(mixed, 3);
#endif #endif
#endif #endif
#if MAX_MIX_COUNT > 5 #if (MAX_MIX_COUNT > 5)
#ifdef FAST_MIXER unsafe
mixed = doMix5(samples, mix_mult_slice(5)); {
#if (FAST_MIXER)
mixed = doMix5(ptr_samples, slice(mix_mult, 5));
#else #else
mixed = doMix(samples, mix_map_slice(5),mix_mult_slice(5)); mixed = doMix(ptr_samples, slice(mix_map, 5), slice(mix_mult, 5));
#endif #endif
write_via_xc_ptr_indexed(samples, NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 5, mixed); ptr_samples[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 5] = mixed;
}
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS) #if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
ComputeMixerLevel(mixed, 5); ComputeMixerLevel(mixed, 5);
#endif #endif
#endif #endif
#if MAX_MIX_COUNT > 7 #if (MAX_MIX_COUNT > 7)
#ifdef FAST_MIXER unsafe
mixed = doMix7(samples, mix_mult_slice(7)); {
#if (FAST_MIXER)
mixed = doMix7(ptr_samples, slice(mix_mult, 7));
#else #else
mixed = doMix(samples, mix_map_slice(7),mix_mult_slice(7)); mixed = doMix(ptr_samples, slice(mix_map, 7), slice(mix_mult, 7));
#endif #endif
ptr_samples[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 7] = mixed;
write_via_xc_ptr_indexed(samples, NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 7, mixed); }
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS) #if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
ComputeMixerLevel(mixed, 7); ComputeMixerLevel(mixed, 7);
#endif #endif
@@ -748,68 +805,52 @@ void mixer(chanend c_mix_in, chanend c_mix_out, chanend c_mix_ctl)
#if (MAX_MIX_COUNT > 0) #if (MAX_MIX_COUNT > 0)
chan c; chan c;
#endif #endif
multOut = array_to_xc_ptr((multOut_array,unsigned[]));
multIn = array_to_xc_ptr((multIn_array,unsigned[]));
samples = array_to_xc_ptr((samples_array,unsigned[]));
samples_to_host_map = array_to_xc_ptr((samples_to_host_map_array,unsigned[]));
samples_to_device_map = array_to_xc_ptr((samples_to_device_map_array,unsigned[]));
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
samples_to_host_inputs_ptr = array_to_xc_ptr((samples_to_host_inputs, unsigned[])); samples_to_host_inputs_ptr = array_to_xc_ptr((samples_to_host_inputs, unsigned[]));
#ifdef LEVEL_METER_LEDS #ifdef LEVEL_METER_LEDS
samples_to_host_inputs_buff_ptr = array_to_xc_ptr((samples_to_host_inputs, unsigned[])); samples_to_host_inputs_buff_ptr = array_to_xc_ptr((samples_to_host_inputs, unsigned[]));
#endif #endif
samples_mixer_outputs_ptr = array_to_xc_ptr((samples_mixer_outputs, unsigned[])); samples_mixer_outputs_ptr = array_to_xc_ptr((samples_mixer_outputs, unsigned[]));
#if MAX_MIX_COUNT >0
mix_mult = array_to_xc_ptr((mix_mult_array,unsigned[]));
#ifndef FAST_MIXER
mix_map = array_to_xc_ptr((mix_map_array,unsigned[]));
#endif
#endif #endif
for (int i=0;i<NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT;i++) for (int i=0;i<NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT;i++)
unsafe { unsafe {
//write_via_xc_ptr_indexed(samples,i,0);
ptr_samples[i] = 0; ptr_samples[i] = 0;
} }
for (int i=0; i<NUM_USB_CHAN_OUT; i++)
{ {
int num_mixes = DEFAULT_FREQ > 96000 ? 2 : MAX_MIX_COUNT; samples_to_device_map_array[i] = i;
for (int i=0;i<NUM_USB_CHAN_OUT;i++)
{
//samples_to_device_map_array[i] = i;
asm("stw %0, %1[%2]":: "r"(i), "r"(samples_to_device_map), "r"(i));
}
} }
#ifdef OUT_VOLUME_IN_MIXER #if (OUT_VOLUME_IN_MIXER)
for (int i=0;i<NUM_USB_CHAN_OUT;i++) for (int i=0; i<NUM_USB_CHAN_OUT; i++)
{ unsafe{
write_via_xc_ptr_indexed(multOut, i, MAX_VOL); multOut[i] = MAX_VOL;
} }
#endif #endif
#ifdef IN_VOLUME_IN_MIXER #if (IN_VOLUME_IN_MIXER)
for (int i=0;i<NUM_USB_CHAN_IN;i++) for (int i=0; i<NUM_USB_CHAN_IN; i++)
{ unsafe{
write_via_xc_ptr_indexed(multIn, i, MAX_VOL); multIn[i] = MAX_VOL;
} }
#endif #endif
for (int i=0;i<NUM_USB_CHAN_IN;i++) for (int i=0; i<NUM_USB_CHAN_IN; i++)
{ unsafe{
write_via_xc_ptr_indexed(samples_to_host_map, i, NUM_USB_CHAN_OUT + i); samples_to_host_map[i] = XUA_MIXER_OFFSET_IN + i;
} }
#if MAX_MIX_COUNT> 0 #if (MAX_MIX_COUNT> 0)
for (int i=0;i<MAX_MIX_COUNT;i++) for (int i=0;i<MAX_MIX_COUNT;i++)
for (int j=0;j<MIX_INPUTS;j++) for (int j=0;j<MIX_INPUTS;j++)
{ unsafe{
#ifndef FAST_MIXER #if (FAST_MIXER == 0)
write_word_to_mix_map(i,j, j < 16 ? j : j + 2); mix_map[i * MIX_INPUTS + j] = (j < 16 ? j : j + 2);
#endif #endif
write_word_to_mix_mult(i,j, i==j ? db_to_mult(0, 8, 25) : 0); mix_mult[i * MIX_INPUTS + j] = (i==j ? db_to_mult(0, XUA_MIXER_DB_FRAC_BITS, XUA_MIXER_MULT_FRAC_BITS) : 0);
} }
#endif #endif

View File

@@ -110,10 +110,8 @@ void ConfigAudioPorts(
} }
set_clock_fall_delay(clk_audio_bclk, bClkDelay_fall); set_clock_fall_delay(clk_audio_bclk, bClkDelay_fall);
#endif #endif
#if (I2S_CHANS_DAC != 0) #if (I2S_CHANS_DAC != 0)
/* Clock I2S output data ports from b-clock clock block */ /* Clock I2S output data ports from b-clock clock block */
for(int i = 0; i < I2S_WIRES_DAC; i++) for(int i = 0; i < I2S_WIRES_DAC; i++)

View File

@@ -1,6 +1,23 @@
# Copyright 2022 XMOS LIMITED. # Copyright 2022-2023 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1. # This Software is subject to the terms of the XMOS Public Licence: Version 1.
import pytest import pytest
import time
@pytest.fixture()
def test_file(request):
return str(request.node.fspath)
@pytest.fixture(scope="session") # Use same seed for whole run
def test_seed(request):
seed = str(int(time.time()))
# We dont need the following since pytest will print the values of our fixtures on a failure
# capmanager = request.config.pluginmanager.getplugin("capturemanager")
# with capmanager.global_and_fixture_disabled():
# print("Using seed: "+ seed)
return seed
def pytest_addoption(parser): def pytest_addoption(parser):

View File

@@ -0,0 +1,45 @@
# Copyright 2023 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
import pytest
import Pyxsim
from Pyxsim import testers
import os
import sys
def do_test(options, capfd, test_file, test_seed):
testname, _ = os.path.splitext(os.path.basename(test_file))
binary = f"{testname}/bin/{testname}.xe"
tester = testers.ComparisonTester(open("pass.expect"))
max_cycles = 15000000
simargs = [
"--max-cycles",
str(max_cycles),
]
build_options = []
build_options += ["TEST_SEED=" + str(test_seed)]
result = Pyxsim.run_on_simulator(
binary,
tester=tester,
build_options=build_options,
simargs=simargs,
capfd=capfd,
instTracing=options.enabletracing,
vcdTracing=options.enablevcdtracing,
)
return result
def test_mixer_routing_input(options, capfd, test_file, test_seed):
result = do_test(options, capfd, test_file, test_seed)
assert result

View File

@@ -0,0 +1,19 @@
DEBUG ?= 0
ifeq ($(DEBUG),1)
TEST_DEBUG_FLAGS = -g -DDEBUG_PRINT_ENABLE=1
else
TEST_DEBUG_FLAGS =
endif
TEST_FLAGS = -DTEST_SEED=$(TEST_SEED) $(TEST_DEBUG_FLAGS)
XCC_FLAGS = -O3 $(TEST_FLAGS)
TARGET = test_xs3_600.xn
USED_MODULES = lib_xua lib_logging lib_random
XMOS_MAKE_PATH ?= ../..
-include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common

View File

@@ -0,0 +1,243 @@
// Copyright 2022-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
/* Tests that routing of mixer inputs behaves as expected
*
* The device supports MAX_MIX_COUNT mixers each with MIX_INPUTS inputs.
*
* This test also assumes/checks that the default routing into each of the MIX_INPUTS inputs into
* each of the M mixer units is as follows:
*
* MIXER[0]:
* USB_FROM_HOST[0] -> MIXER[0].INPUT[0]
* USB_FROM_HOST[1] -> MIXER[0].INPUT[1]
* ...
USB_TO_HOST[0] -> MIXER[0].INPUT[NUM_USB_CHAN_OUT]
USB_TO_HOST[1] -> MIXER[0].INPUT[NUM_USB_CHAN_OUT+1]
...
* MIXER[MAX_MIX_COUNT-1]:
* USB_FROM_HOST[0] -> MIXER[MAX_MIX_COUNT-1].INPUT[0]
* USB_FROM_HOST[1] -> MIXER[MAX_MIX_COUNT-1].INPUT[1]
* ...
*
*/
#include <stdint.h>
#include <stddef.h>
#include "platform.h"
#include "xua.h"
#include "debug_print.h"
#include "assert.h"
#include "random.h"
#ifndef TEST_ITERATIONS
#define TEST_ITERATIONS (300)
#endif
#include "./../test_mixer_routing_output/src/mixer_test_shared.h"
struct ModelMixer
{
uint32_t deviceMap[NUM_USB_CHAN_OUT];
uint32_t hostMap[NUM_USB_CHAN_IN];
uint32_t mixMap_input[MAX_MIX_COUNT];
uint32_t mixMap_src[MAX_MIX_COUNT];
uint32_t mixOutput[MAX_MIX_COUNT];
};
void InitModel(struct ModelMixer &modelMixer)
{
for(size_t i = 0; i < NUM_USB_CHAN_OUT; i++)
{
modelMixer.deviceMap[i] = i;
}
for(size_t i = 0; i < NUM_USB_CHAN_IN; i++)
{
modelMixer.hostMap[i] = NUM_USB_CHAN_OUT+i;
}
for(size_t i = 0; i < MAX_MIX_COUNT; i++)
{
// This test only allows for one "active" input to each mixer
modelMixer.mixMap_src[i] = i;
modelMixer.mixMap_input[i] = i;
uint32_t sample = i;
SET_SOURCE(sample, SRC_HOST);
SET_CHANNEL(sample, i);
modelMixer.mixOutput[i] = sample;
}
}
void GenExpectedSamples(struct ModelMixer &modelMixer,
uint32_t modelOut[NUM_USB_CHAN_OUT],
uint32_t modelIn[NUM_USB_CHAN_IN])
{
/* First generate model mix outputs - run MIX tiles to allow mix inputs derived from mix outputs to propagate */
for(int j = 0; j < MAX_MIX_COUNT; j++)
{
for(size_t i = 0; i < MAX_MIX_COUNT; i++)
{
int src = modelMixer.mixMap_src[i];
modelMixer.mixOutput[i] = CreateSample(modelMixer.mixOutput, src);
}
}
for(size_t i = 0; i<NUM_USB_CHAN_OUT; i++)
{
int src = modelMixer.deviceMap[i];
modelOut[i] = CreateSample(modelMixer.mixOutput, src);
}
for(size_t i = 0; i<NUM_USB_CHAN_IN; i++)
{
int src = modelMixer.hostMap[i];
modelIn[i] = CreateSample(modelMixer.mixOutput, src);
}
}
void MapMixerInput(int mix, int input, int src, struct ModelMixer &modelMixer, chanend c_mix_ctl,
chanend c_stim_ah, chanend c_stim_de, uint32_t modelIn[], uint32_t modelOut[])
{
debug_printf("Mapping mix %d input %d", mix, input);
debug_printf(" from %d", src);
PrintSourceString(src);
debug_printf("\n");
/* This test only allows for one input to travel "untouched" to the mix output - since this test doesn't model the actual mixing.
* Because of this we must also mod the mixer weights, not just the mixer input map.
* If we simply just apply an update to the mixer input mapping it would not produce an observable difference on the mixer output
*/
/* Set previously "activated" input weight to 0 */
debug_printf("Setting mix %d, weight %d to 0\n", mix, modelMixer.mixMap_input[mix]);
SendTrigger(c_stim_ah, 1);
UpdateMixerWeight(c_mix_ctl, mix, modelMixer.mixMap_input[mix], 0);
/* Set new "activated" input wright to max (i.e. x1) */
debug_printf("Setting mix %d, weight %d to %x\n", mix, input, XUA_MIXER_MAX_MULT);
SendTrigger(c_stim_ah, 1);
UpdateMixerWeight(c_mix_ctl, mix, input, XUA_MIXER_MAX_MULT);
/* Update mixer input in model */
modelMixer.mixMap_src[mix] = src;
modelMixer.mixMap_input[mix] = input;
/* Run twice to allow mix inputs derived from mix outputs to propagate */
GenExpectedSamples(modelMixer, modelOut, modelIn);
/* Finally update the acutal mixer input map */
SendTrigger(c_stim_ah, 1);
UpdateMixMap(c_mix_ctl, mix, input, src);
SendTrigger(c_stim_ah, 1);
SendExpected(c_stim_ah, c_stim_de, modelOut, modelIn);
}
/* This task configures the routing and maintains a model of the expected routing output
* it provides this to the Fake AudioHub and Fake Decouple tasks such that they can self check
*/
void stim(chanend c_stim_ah, chanend c_stim_de, chanend c_mix_ctl)
{
uint32_t modelOut[NUM_USB_CHAN_OUT];
uint32_t modelIn[NUM_USB_CHAN_IN];
random_generator_t rg = random_create_generator_from_seed(TEST_SEED);
struct ModelMixer modelMixer;
InitModel(modelMixer);
GenExpectedSamples(modelMixer, modelOut, modelIn);
/* There is single sample delay between the two mixer cores, so trigger twice to flush though a block
* of zero samples */
SendTrigger(c_stim_ah, 2);
/* Send expected samples to AH and DE and run checks */
SendExpected(c_stim_ah, c_stim_de, modelOut, modelIn);
/* Firstly route mixer outputs to the audio interfaces (we could have chosen host)
* such that we can observe and check the outputs from the mixer
*/
for(size_t i = 0; i < MAX_MIX_COUNT; i++)
{
int map = SET_SAMPLES_TO_DEVICE_MAP;
assert(i < NUM_USB_CHAN_OUT);
int dst = i;
int src = NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN+i; // mix0, mix1..
debug_printf("Mapping output to AudioIF: %d ", dst);
PrintDestString(map, dst);
debug_printf(" from %d", src);
PrintSourceString(src);
debug_printf("\n");
SendTrigger(c_stim_ah, 1);
/* Update the mixer */
UpdateMixerOutputRouting(c_mix_ctl, map, dst, src);
/* Update the model */
modelMixer.deviceMap[dst] = src;
}
/* Send expected samples to fake AudioHub and Decouple for checking */
SendExpected(c_stim_ah, c_stim_de, modelOut, modelIn);
for(int testIter = 0; testIter < TEST_ITERATIONS; testIter++)
{
/* Make a random update to the routing - route a random source to a random mix input */
unsigned mix = random_get_random_number(rg) % MAX_MIX_COUNT;
unsigned input = random_get_random_number(rg) % MIX_INPUTS;
/* Note, we don't currently support a mix input dervived from another mix
* This is not trivial to test since the current mixer implementation only allows for one
* config update per "trigger"
*/
unsigned src = random_get_random_number(rg) % NUM_USB_CHAN_IN + NUM_USB_CHAN_OUT;
debug_printf("Iteration: %d\n", testIter);
MapMixerInput(mix, input, src, modelMixer, c_mix_ctl, c_stim_ah, c_stim_de, modelIn, modelOut);
}
/* Send kill messages to Fake AudioHub & Fake Decouple */
outct(c_stim_ah, XS1_CT_END);
inct(c_stim_ah);
outct(c_stim_de, XS1_CT_END);
inct(c_stim_de);
printstrln("PASS");
exit(0);
}
int main()
{
chan c_dec_mix;
chan c_mix_aud;
chan c_mix_ctl;
chan c_stim_ah;
chan c_stim_de;
par
{
Fake_XUA_Buffer_Decouple(c_dec_mix, c_stim_de);
Fake_XUA_AudioHub(c_mix_aud, c_stim_ah);
/* Mixer from lib_xua */
mixer(c_dec_mix, c_mix_aud, c_mix_ctl);
stim(c_stim_ah, c_stim_de, c_mix_ctl);
}
/* TODO to hit this we need to fully close down i.e. kill mixer */
return 0;
}

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<Network xmlns="http://www.xmos.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.xmos.com http://www.xmos.com">
<Declarations>
<Declaration>tileref tile[2]</Declaration>
</Declarations>
<Packages>
<Package id="0" Type="XS3-UnA-1024-FB265">
<Nodes>
<Node Id="0" InPackageId="0" Type="XS3-L16A-1024" Oscillator="24MHz" SystemFrequency="600MHz" ReferenceFrequency="100MHz">
<Tile Number="0" Reference="tile[0]"/>
<Tile Number="1" Reference="tile[1]"/>
</Node>
</Nodes>
</Package>
</Packages>
<JTAGChain>
<JTAGDevice NodeId="0"/>
</JTAGChain>
</Network>

View File

@@ -0,0 +1,45 @@
// Copyright 2016-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef _XUA_CONF_H_
#define _XUA_CONF_H_
#define NUM_USB_CHAN_OUT (10)
#define NUM_USB_CHAN_IN (10)
#define I2S_CHANS_DAC (10)
#define I2S_CHANS_ADC (10)
#define EXCLUDE_USB_AUDIO_MAIN
#define MIXER (1)
#define MAX_MIX_COUNT (8)
#define UAC_FORCE_FEEDBACK_EP (0)
#define XUA_NUM_PDM_MICS 0
#define XUD_TILE 1
#define AUDIO_IO_TILE 0
#ifndef MCLK_441
#define MCLK_441 (512 * 44100)
#endif
#ifndef MCLK_48
#define MCLK_48 (512 * 48000)
#endif
#define MIN_FREQ (44100)
#define MAX_FREQ (192000)
#define SPDIF_TX_INDEX 0
#define VENDOR_STR "XMOS"
#define VENDOR_ID 0x20B1
#define PRODUCT_STR_A2 "Test device"
#define PRODUCT_STR_A1 "Test device"
#define PID_AUDIO_1 1
#define PID_AUDIO_2 2
#define AUDIO_CLASS 2
#define AUDIO_CLASS_FALLBACK 0
#define BCD_DEVICE 0x1234
#define XUA_DFU_EN 0
#define MIC_DUAL_ENABLED 1 //Use single thread, dual PDM mic
#define XUA_MIC_FRAME_SIZE 240
#endif

View File

@@ -0,0 +1,45 @@
# Copyright 2023 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
import pytest
import Pyxsim
from Pyxsim import testers
import os
import sys
def do_test(options, capfd, test_file, test_seed):
testname, _ = os.path.splitext(os.path.basename(test_file))
binary = f"{testname}/bin/{testname}.xe"
tester = testers.ComparisonTester(open("pass.expect"))
max_cycles = 15000000
simargs = [
"--max-cycles",
str(max_cycles),
]
build_options = []
build_options += ["TEST_SEED=" + str(test_seed)]
result = Pyxsim.run_on_simulator(
binary,
tester=tester,
build_options=build_options,
simargs=simargs,
capfd=capfd,
instTracing=options.enabletracing,
vcdTracing=options.enablevcdtracing,
)
return result
def test_mixer_routing_output(options, capfd, test_file, test_seed):
result = do_test(options, capfd, test_file, test_seed)
assert result

View File

@@ -0,0 +1,19 @@
DEBUG ?= 0
ifeq ($(DEBUG),1)
TEST_DEBUG_FLAGS = -g -DDEBUG_PRINT_ENABLE=1
else
TEST_DEBUG_FLAGS =
endif
TEST_FLAGS = -DTEST_SEED=$(TEST_SEED) $(TEST_DEBUG_FLAGS) -DXUD_WEAK_API=1
XCC_FLAGS = -O3 $(TEST_FLAGS)
TARGET = test_xs3_600.xn
USED_MODULES = lib_xua lib_logging lib_random
XMOS_MAKE_PATH ?= ../..
-include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common

View File

@@ -0,0 +1,200 @@
// Copyright 2022-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
/* This tests checks the parsing of control requests to endpoint 0 cause the correct changes in mixer output routing */
#include <stdint.h>
#include <stddef.h>
#include "platform.h"
#include "xua.h"
#include "debug_print.h"
#include "assert.h"
#include "xud.h"
#include "usbaudio20.h"
#include "random.h"
#ifndef TEST_ITERATIONS
#define TEST_ITERATIONS (100)
#endif
#include "./../test_mixer_routing_output/src/mixer_test_shared.h"
/* Mixer input mapping - from xua_endpoint0.c */
extern unsigned char mixSel[MAX_MIX_COUNT][MIX_INPUTS];
/* From xua_ep0_uacreqs.xc */
int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, chanend ?c_audioControl, chanend ?c_mix_ctl, chanend ?c_clk_ctl);
/* From xua_endpoint0.c */
void InitLocalMixerState();
int g_src = 0;
/* Override func in lib_xud/src/user/client/XUD_EpFunctions.c for testing purposes */
XUD_Result_t XUD_GetBuffer(XUD_ep ep_out, unsigned char buffer[], REFERENCE_PARAM(unsigned, length))
{
buffer[0] = g_src;
return XUD_RES_OKAY;
}
XUD_Result_t XUD_DoSetRequestStatus(XUD_ep ep_in)
{
return XUD_RES_OKAY;
}
XUD_Result_t XUD_SetBuffer_EpMax(XUD_ep ep_in, unsigned char buffer[], unsigned datalength, unsigned epMax)
{
assert(g_src == buffer[0]);
assert(datalength == 1);
return XUD_RES_OKAY;
}
unsafe
{
extern int volatile * const unsafe samples_to_device_map;
extern int volatile * const unsafe samples_to_host_map;
}
void Fake_Endpoint0(chanend c_mix_ctl)
{
XUD_ep ep0_out; /* Never initialised but not used */
XUD_ep ep0_in; /* Never initialised but not used */
USB_SetupPacket_t sp;
random_generator_t rg = random_create_generator_from_seed(TEST_SEED);
InitLocalMixerState();
sp.bmRequestType.Type = USB_BM_REQTYPE_TYPE_CLASS; // Note, parsing of this won't be tested since we call AudioClassRequests directly
sp.bmRequestType.Recipient = USB_BM_REQTYPE_RECIP_INTER; // Note, parsing of this won't be tested since we call AudioClassRequests directly
for(int testIter = 0; testIter < TEST_ITERATIONS; testIter++)
{
int unitId = ID_XU_MIXSEL;
unsigned mix = (random_get_random_number(rg) % (MAX_MIX_COUNT + 1)); // Mixs indexed from 1
unsigned input = random_get_random_number(rg) % MIX_INPUTS;
/* Note, we don't currently support a mix input dervived from another mix
* This is not trivial to test since the current mixer implementation only allows for one
* config update per "trigger"
*/
unsigned src = random_get_random_number(rg) % (NUM_USB_CHAN_IN + NUM_USB_CHAN_OUT);
debug_printf("Mapping mix %d input %d", mix, input);
debug_printf(" from %d", src);
PrintSourceString(src);
debug_printf("\n");
/* Create Control request data for routing change */
int cs = mix;
int cn = input;
sp.bmRequestType.Direction = USB_BM_REQTYPE_DIRECTION_H2D;
sp.bRequest = CUR;
sp.wValue = cn | (cs << 8);
sp.wIndex = (unitId << 8);
sp.wLength = 1;
g_src = src; /* This will get picked up by out implementation of XUD_GetBuffer */
/* Call the function used by Endpoint0 to parse the control data and update the mixer output routing */
AudioClassRequests_2(ep0_out, ep0_in, sp, null, c_mix_ctl, null);
/* Note, there is a race risk here. This could be resolved by adding a handshake to UpdateMixerOutputRouting() etc */
/* Now check the mixer setting have been modified as expected. To do this we inspect "internal"
* mixer and endpoint 0 state.
*
* Going forward we might wish to enhance the mixer API such that it can be tested as a black box.
* This would require the addition of "GET" API over it's ctrl channel
*/
sp.bmRequestType.Direction = USB_BM_REQTYPE_DIRECTION_D2H;
if(mix == 0)
{
/* If mix is 0 then we need to check that all mixers have been updated */
for(int i = 0; i < MAX_MIX_COUNT; i++)
{
assert(g_src == mixSel[i][cn]);
/* Need to read back from each mixer individually */
sp.wValue = cn | ((i + 1)<< 8);
AudioClassRequests_2(ep0_out, ep0_in, sp, null, c_mix_ctl, null);
}
}
else
{
assert(g_src == mixSel[cs-1][cn]);
/* Test read back. Note, the checking is in our overridden implementation of XUD_SetBuffer_EpMax*/
AudioClassRequests_2(ep0_out, ep0_in, sp, null, c_mix_ctl, null);
}
}
printstrln("PASS");
exit(0);
}
void Fake_XUA_AudioHub_CtrlTest(chanend c_mix_aud)
{
int readBuffNo = 0;
unsigned underflowWord = 0;
/* Continually send/receive samples to/from mixer, no checking of samples since this is purely a control test */
while(1)
{
unsigned command = DoSampleTransfer(c_mix_aud, readBuffNo, underflowWord);
}
}
void Fake_XUA_Buffer_Decouple_CtrlTest(chanend c_dec_mix)
{
unsigned samplesIn[NUM_USB_CHAN_IN];
unsigned underflowSample;
/* Continually send/receive samples to/from mixer, no checking of samples since this is purely a control test */
while(1)
{
select
{
case inuint_byref(c_dec_mix, underflowSample):
for(int i = 0; i < NUM_USB_CHAN_OUT; i++)
{
outuint(c_dec_mix, 0);
}
for(int i = 0; i < NUM_USB_CHAN_IN; i++)
{
samplesIn[i] = inuint(c_dec_mix);
}
break;
}
}
}
int main()
{
chan c_dec_mix;
chan c_mix_aud;
chan c_mix_ctl;
par
{
/* We need "fake" versions of the AudioHub and Decouple to keep the mixer running and taking updates via
* it's control channel */
Fake_XUA_Buffer_Decouple_CtrlTest(c_dec_mix);
Fake_XUA_AudioHub_CtrlTest(c_mix_aud);
/* Mixer from lib_xua */
mixer(c_dec_mix, c_mix_aud, c_mix_ctl);
Fake_Endpoint0(c_mix_ctl);
}
/* TODO to hit this we need to fully close down i.e. kill mixer */
return 0;
}

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<Network xmlns="http://www.xmos.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.xmos.com http://www.xmos.com">
<Declarations>
<Declaration>tileref tile[2]</Declaration>
</Declarations>
<Packages>
<Package id="0" Type="XS3-UnA-1024-FB265">
<Nodes>
<Node Id="0" InPackageId="0" Type="XS3-L16A-1024" Oscillator="24MHz" SystemFrequency="600MHz" ReferenceFrequency="100MHz">
<Tile Number="0" Reference="tile[0]"/>
<Tile Number="1" Reference="tile[1]"/>
</Node>
</Nodes>
</Package>
</Packages>
<JTAGChain>
<JTAGDevice NodeId="0"/>
</JTAGChain>
</Network>

View File

@@ -0,0 +1,45 @@
// Copyright 2016-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef _XUA_CONF_H_
#define _XUA_CONF_H_
#define NUM_USB_CHAN_OUT (10)
#define NUM_USB_CHAN_IN (10)
#define I2S_CHANS_DAC (10)
#define I2S_CHANS_ADC (10)
#define EXCLUDE_USB_AUDIO_MAIN
#define MIXER (1)
#define MAX_MIX_COUNT (8)
#define UAC_FORCE_FEEDBACK_EP (0)
#define XUA_NUM_PDM_MICS 0
#define XUD_TILE 1
#define AUDIO_IO_TILE 0
#ifndef MCLK_441
#define MCLK_441 (512 * 44100)
#endif
#ifndef MCLK_48
#define MCLK_48 (512 * 48000)
#endif
#define MIN_FREQ (44100)
#define MAX_FREQ (192000)
#define SPDIF_TX_INDEX 0
#define VENDOR_STR "XMOS"
#define VENDOR_ID 0x20B1
#define PRODUCT_STR_A2 "Test device"
#define PRODUCT_STR_A1 "Test device"
#define PID_AUDIO_1 1
#define PID_AUDIO_2 2
#define AUDIO_CLASS 2
#define AUDIO_CLASS_FALLBACK 0
#define BCD_DEVICE 0x1234
#define XUA_DFU_EN 0
#define MIC_DUAL_ENABLED 1 //Use single thread, dual PDM mic
#define XUA_MIC_FRAME_SIZE 240
#endif

View File

@@ -0,0 +1,45 @@
# Copyright 2023 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
import pytest
import Pyxsim
from Pyxsim import testers
import os
import sys
def do_test(options, capfd, test_file, test_seed):
testname, _ = os.path.splitext(os.path.basename(test_file))
binary = f"{testname}/bin/{testname}.xe"
tester = testers.ComparisonTester(open("pass.expect"))
max_cycles = 15000000
simargs = [
"--max-cycles",
str(max_cycles),
]
build_options = []
build_options += ["TEST_SEED=" + str(test_seed)]
result = Pyxsim.run_on_simulator(
binary,
tester=tester,
build_options=build_options,
simargs=simargs,
capfd=capfd,
instTracing=options.enabletracing,
vcdTracing=options.enablevcdtracing,
)
return result
def test_mixer_routing_output(options, capfd, test_file, test_seed):
result = do_test(options, capfd, test_file, test_seed)
assert result

View File

@@ -0,0 +1,19 @@
DEBUG ?= 0
ifeq ($(DEBUG),1)
TEST_DEBUG_FLAGS = -g -DDEBUG_PRINT_ENABLE=1
else
TEST_DEBUG_FLAGS =
endif
TEST_FLAGS = -DTEST_SEED=$(TEST_SEED) $(TEST_DEBUG_FLAGS)
XCC_FLAGS = -O3 $(TEST_FLAGS)
TARGET = test_xs3_600.xn
USED_MODULES = lib_xua lib_logging lib_random
XMOS_MAKE_PATH ?= ../..
-include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common

View File

@@ -0,0 +1,224 @@
// Copyright 2022-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
/* Tests that routing of mixer outputs behaves as expected
*
* "Outputs" from the device are to the USB host of one of the various audio interaces supported.
* This test assumes/checks the default routing for the USB host & audio interfaces is as follows:
*
* USB_FROM_HOST[0] -> AUD_INTERFACE_OUTPUT[0]
* USB_FROM_HOST[1] -> AUD_INTERFACE_OUTPUT[1]
* ...
* USB_TO_HOST[0] <- AUD_INTERFACE_INPUT[0]
* USB_TO_HOST[1] <- AUD_INTERFACE_INPUT[1]
* ...
*
* This test also assumes/checks that the default routing into each of the MIX_INPUTS inputs into
* each of the M mixer units is as follows:
*
* MIXER[0]:
* USB_FROM_HOST[0] -> MIXER[0].INPUT[0]
* USB_FROM_HOST[1] -> MIXER[0].INPUT[1]
* ...
USB_TO_HOST[0] -> MIXER[0].INPUT[NUM_USB_CHAN_OUT]
USB_TO_HOST[1] -> MIXER[0].INPUT[NUM_USB_CHAN_OUT+1]
...
* MIXER[MAX_MIX_COUNT-1]:
* USB_FROM_HOST[0] -> MIXER[MAX_MIX_COUNT-1].INPUT[0]
* USB_FROM_HOST[1] -> MIXER[MAX_MIX_COUNT-1].INPUT[1]
* ...
*
* (If the number of mixer inputs > NUM_USB_CHAN_OUT then see ordering in comment regarding
* SOURCE_COUNT below)
*
* By default none of the MAX_MIX_COUNT output from the mixers are routed anywwhere, but this test ensures
* that they can be.
*
* This test assumes that none of the mixer weights are changed.
* This test does not test changing the inputs to the mixer.
*/
#include <stdint.h>
#include <stddef.h>
#include "platform.h"
#include "xua.h"
#include "debug_print.h"
#include "assert.h"
#include "random.h"
#ifndef TEST_ITERATIONS
#define TEST_ITERATIONS (100)
#endif
#include "./mixer_test_shared.h"
void UpdateModel(uint32_t modelOut[CHANNEL_MAP_AUD_SIZE], uint32_t modelMixerOut[MAX_MIX_COUNT], uint32_t modelIn[NUM_USB_CHAN_IN],
int map, int dst, int src)
{
unsigned sample = 0;
if(src == (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT))
{
SET_SOURCE(sample, SRC_OFF);
}
else if(src >= (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN))
{
src -= (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN);
sample = modelMixerOut[src];
}
else if (src >= NUM_USB_CHAN_IN)
{
SET_SOURCE(sample, SRC_AUDIF);
src -= NUM_USB_CHAN_OUT;
SET_CHANNEL(sample, src);
}
else
{
SET_SOURCE(sample, SRC_HOST);
SET_CHANNEL(sample, src);
}
switch(map)
{
case SET_SAMPLES_TO_DEVICE_MAP:
modelOut[dst] = sample;
break;
case SET_SAMPLES_TO_HOST_MAP:
modelIn[dst] = sample;
break;
default:
assert(0);
break;
}
}
/* This task configures the routing and maintains a model of the expected routing output
* it provides this to the Fake AudioHub and Fake Decouple tasks such that they can self check
*/
void stim(chanend c_stim_ah, chanend c_stim_de, chanend c_mix_ctl)
{
uint32_t modelOut[CHANNEL_MAP_AUD_SIZE];
uint32_t modelIn[CHANNEL_MAP_USB_SIZE];
uint32_t modelMixerOut[MAX_MIX_COUNT];
uint32_t testCmd[] = {SET_SAMPLES_TO_HOST_MAP, SET_SAMPLES_TO_DEVICE_MAP};
random_generator_t rg = random_create_generator_from_seed(TEST_SEED);
/* By default the mixer should output samples from USB host unmodified
* See mixer.xc L780
*/
for(size_t i = 0; i < MAX_MIX_COUNT; i++)
{
uint32_t sample = 0;
SET_SOURCE(sample, SRC_HOST);
SET_CHANNEL(sample, i);
modelMixerOut[i] = sample;
}
/* Init modelOut for default routing */
/* Default routing is USB[0] -> AUD_IF[0] etc */
for(size_t i = 0; i < CHANNEL_MAP_AUD_SIZE; i++)
{
uint32_t sample = 0;
SET_SOURCE(sample, SRC_HOST);
SET_CHANNEL(sample, i);
modelOut[i] = sample;
}
/* Init modelIn for default routing */
/* Default routing is AUD_IF[0] -> USB[0] etc */
for(size_t i = 0; i < NUM_USB_CHAN_IN; i++)
{
uint32_t sample = 0;
SET_SOURCE(sample, SRC_AUDIF);
SET_CHANNEL(sample, i);
modelIn[i] = sample;
}
/* Check default routing */
/* Send expected to AudioHub */
SendTrigger(c_stim_ah, 2);
SendExpected(c_stim_ah, c_stim_de, modelOut, modelIn);
for(int testIter = 0; testIter < TEST_ITERATIONS; testIter++)
{
/* Make a random update to the routing - route a random source to a random destination */
unsigned map = testCmd[random_get_random_number(rg) % (sizeof(testCmd)/sizeof(testCmd[0]))];
unsigned dst = random_get_random_number(rg) % CHANNEL_MAP_AUD_SIZE; // TODO this should be CHANNEL_MAP_USB_SIZE for SET_SAMPLES_TO_HOST_MAP
unsigned src = random_get_random_number(rg) % (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT);
switch(map)
{
case SET_SAMPLES_TO_DEVICE_MAP:
debug_printf("Mapping output to AudioIF: %d", dst);
PrintDestString(map, dst);
debug_printf(" from %d", src);
PrintSourceString(src);
debug_printf("\n");
/* Update the mixer */
SendTrigger(c_stim_ah, 1);
UpdateMixerOutputRouting(c_mix_ctl, map, dst, src);
break;
case SET_SAMPLES_TO_HOST_MAP:
debug_printf("Mapping output to Host : %d", dst);
PrintDestString(map, dst);
debug_printf(" from %d", src);
PrintSourceString(src);
debug_printf("\n");
/* Update the mixer */
SendTrigger(c_stim_ah, 1);
UpdateMixerOutputRouting(c_mix_ctl, map, dst, src);
break;
default:
printstr("ERROR: Bad cmd in stim(): ");
printintln(map);
break;
}
/* Update the model */
UpdateModel(modelOut, modelMixerOut, modelIn, map, dst, src);
SendExpected(c_stim_ah, c_stim_de, modelOut, modelIn);
}
/* Send kill messages to Fake AudioHub & Fake Decouple */
outct(c_stim_ah, XS1_CT_END);
inct(c_stim_ah);
outct(c_stim_de, XS1_CT_END);
inct(c_stim_de);
printstrln("PASS");
exit(0);
}
int main()
{
chan c_dec_mix;
chan c_mix_aud;
chan c_mix_ctl;
chan c_stim_ah;
chan c_stim_de;
par
{
Fake_XUA_Buffer_Decouple(c_dec_mix, c_stim_de);
Fake_XUA_AudioHub(c_mix_aud, c_stim_ah);
/* Mixer from lib_xua */
mixer(c_dec_mix, c_mix_aud, c_mix_ctl);
stim(c_stim_ah, c_stim_de, c_mix_ctl);
}
/* TODO to hit this we need to fully close down i.e. kill mixer */
return 0;
}

View File

@@ -0,0 +1,325 @@
// Copyright 2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef TEST_SEED
#error TEST_SEED must be defined!
#endif
/* A limitation of the design is that the number of routable output destinations cannot be larger than NUM_USB_CHAN_OUT.
* This is due to the transfer samples from Mixer to AudioHub tasks being in blocks of NUM_USB_CHAN_OUT.
* This is not normally an issue - since every physical output interface channel on the device is normally derived from a
* USB channel from the host, but it certainly is a restriction.
*/
#define CHANNEL_MAP_AUD_SIZE NUM_USB_CHAN_OUT
#define CHANNEL_MAP_USB_SIZE NUM_USB_CHAN_IN
/* Number of channel sources, the channel ordering is as follows
* i.e.
* [0:NUM_USB_CHAN_OUT-1] : Channels from USB Host
* [NUM_USB_CHAN_OUT:NUM_USB_CHAN_IN-1] : Channels from Audio Interfaces
* [NUM_USB_CHAN_N:MAX_MIX_COUNT-1] : Channels from Mixers
* [MAX_MIX_COUNT]: "Off" (Essentially samples always 0)
*/
/* Note, One larger for an "off" channel for mixer sources" */
#define SOURCE_COUNT (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT + 1)
#define SET_EXPECTED (9)
#define TRIGGER (7)
// Test sample format:
// byte[0]: Sample counter
// byte[1]: Channel
// byte[3]: Source (HOST:1/AUD IF:0)
#define SRC_HOST (2)
#define SRC_AUDIF (1)
#define SRC_OFF (0) // Important that this is 0 since mixer will generate 0 samples for 'off'
#define GET_COUNT(x) ((x>>8) & 0xff)
#define GET_CHANNEL(x) ((x >> 16) & 0xff)
#define GET_SOURCE(x) ((x >> 24) & 0xff)
#define SET_COUNT(x, y) y = y & 0xff; x = x | (y<<8);
#define SET_CHANNEL(x, y) y = y & 0xff; x = x | (y<<16);
#define SET_SOURCE(x, y) x = x | (y<<24);
void exit(int);
#pragma select handler
static inline void testct_byref(chanend c, unsigned &isCt)
{
isCt = testct(c);
}
void SendTrigger(chanend c_stim_ah, int count)
{
for(int i = 0; i < count; i++)
outuint(c_stim_ah, TRIGGER);
}
uint32_t CreateSample(uint32_t modelMixerOutput[], int src)
{
uint32_t sample = 0;
if(src == (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT))
{
SET_SOURCE(sample, SRC_OFF);
}
else if(src >= (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN))
{
src -= (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN);
sample = modelMixerOutput[src];
}
else if (src >= NUM_USB_CHAN_IN)
{
SET_SOURCE(sample, SRC_AUDIF);
src -= NUM_USB_CHAN_OUT;
SET_CHANNEL(sample, src);
}
else
{
SET_SOURCE(sample, SRC_HOST);
SET_CHANNEL(sample, src);
}
return sample;
}
void PrintSourceString(unsigned source)
{
debug_printf(" ");
if(source < NUM_USB_CHAN_OUT)
{
debug_printf("(DEVICE IN - HOST%d)", source);
}
else if(source < (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN))
{
debug_printf("(DEVICE IN - AudioIF %d)", source - NUM_USB_CHAN_OUT);
}
else if(source < (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT))
{
debug_printf("(MIX %d)", source - NUM_USB_CHAN_OUT - NUM_USB_CHAN_IN);
}
else
debug_printf("(off)");
debug_printf(" ");
}
void PrintDestString(unsigned map, unsigned dest)
{
switch(map)
{
case SET_SAMPLES_TO_DEVICE_MAP:
debug_printf("(DEVICE OUT - AudioIF)");
break;
case SET_SAMPLES_TO_HOST_MAP:
debug_printf("(DEVICE OUT - HOST)");
break;
}
}
void PrintSample(unsigned sample)
{
debug_printf("SOURCE: ");
if(GET_SOURCE(sample) == SRC_HOST)
debug_printf("HOST ");
else if(GET_SOURCE(sample) == SRC_AUDIF)
debug_printf("AUDIF ");
else if(GET_SOURCE(sample) == SRC_OFF)
debug_printf("OFF ");
else
debug_printf("UNKNOWN ");
debug_printf("CHANNEL: %d", GET_CHANNEL(sample));
}
/* Required by lib_xua */
void AudioHwInit()
{
return;
}
/* Required by lib_xua */
void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC)
{
return;
}
/* From xua_ep0_uacreqs.xc */
void UpdateMixerOutputRouting(chanend c_mix_ctl, unsigned map, unsigned dst, unsigned src);
void UpdateMixMap(chanend c_mix_ctl, int mix, int input, int src);
void UpdateMixerWeight(chanend c_mix_ctl, int mix, int index, unsigned val);
void CheckBlock(unsigned samplesOut[], uint32_t expectedOut[], size_t len)
{
int fail = 0;;
for(int j = 0; j < len; j++)
{
debug_printf("%d: Expected: ", j);
PrintSample(expectedOut[j]);
debug_printf("\n");
if(expectedOut[j] != samplesOut[j])
{
printstr("ERROR: Actual: ");
PrintSample(samplesOut[j]);
debug_printf(" (%x)", samplesOut[j]);
printstr("\n");
fail = 1;
}
//assert(expectedOut[j] == samplesOut[j]);
}
assert(!fail);
}
/* Sending expected also causes fake_audiohub and fake_decouple to run sample checks */
void SendExpected(chanend c_stim_ah, chanend c_stim_de, uint32_t modelOut[], uint32_t modelIn[])
{
/* Send expected to AudioHub */
outuint(c_stim_ah, SET_EXPECTED);
for(int i = 0; i < CHANNEL_MAP_AUD_SIZE; i++)
{
outuint(c_stim_ah, modelOut[i]);
}
/* Wait for handshake back and move on to next test */
inuint(c_stim_ah);
/* Send expected to Decouple */
outuint(c_stim_de, SET_EXPECTED);
for(int i = 0; i < NUM_USB_CHAN_IN; i++)
{
outuint(c_stim_de, modelIn[i]);
}
/* Wait for handshake back and move on to next test */
inuint(c_stim_de);
}
/* From xua_audiohub.xc */
extern unsigned samplesOut[NUM_USB_CHAN_OUT];
extern unsigned samplesIn[2][NUM_USB_CHAN_IN];
#include "xua_audiohub_st.h"
int Fake_XUA_AudioHub(chanend c_mix_aud, chanend c_stim)
{
int readBuffNo = 0;
unsigned underflowWord = 0;
uint32_t expectedOut[NUM_USB_CHAN_OUT];
unsigned ct = 0;
unsigned cmd = 0;
for(size_t i = 0; i < NUM_USB_CHAN_IN; i++)
{
/* Note, we only used readBufNo = 0 */
unsigned sample = 0;
SET_SOURCE(sample, SRC_AUDIF);
SET_CHANNEL(sample, i);
samplesIn[0][i] = sample;
}
while(!ct)
{
select
{
case testct_byref(c_stim, ct):
if(!ct)
{
cmd = inuint(c_stim);
switch(cmd)
{
case SET_EXPECTED:
for(int j = 0; j < NUM_USB_CHAN_OUT; j++)
{
expectedOut[j] = inuint(c_stim);
}
debug_printf("AudioHub:\n");
CheckBlock(samplesOut, expectedOut, NUM_USB_CHAN_OUT);
/* Handshake back */
outuint(c_stim, 0);
break;
case TRIGGER:
/* This will populate samplesOut and send out samplesIn[readBuffNo] */
unsigned command = DoSampleTransfer(c_mix_aud, readBuffNo, underflowWord);
break;
default:
printstr("ERROR: bad cmd in Fake_XUA_AudioHub: ");
printintln(cmd);
assert(0);
break;
}
}
break;
}
}
outct(c_stim, XS1_CT_END);
inct(c_stim);
return 0;
}
int Fake_XUA_Buffer_Decouple(chanend c_dec_mix, chanend c_stim)
{
uint32_t expectedSamplesIn[NUM_USB_CHAN_IN];
unsigned samplesIn[NUM_USB_CHAN_IN];
unsigned ct;
unsigned underflowSample;
while(!ct)
{
select
{
case inuint_byref(c_dec_mix, underflowSample):
for(int i = 0; i < NUM_USB_CHAN_OUT; i++)
{
unsigned sample = 0;
SET_SOURCE(sample, SRC_HOST);
SET_CHANNEL(sample, i);
outuint(c_dec_mix, sample);
}
for(int i = 0; i < NUM_USB_CHAN_IN; i++)
{
samplesIn[i] = inuint(c_dec_mix);
}
break;
case testct_byref(c_stim, ct):
if(!ct)
{
inuint(c_stim); // TODO don't really need this
/* Get expected */
for(int j = 0; j < NUM_USB_CHAN_IN; j++)
{
expectedSamplesIn[j] = inuint(c_stim);
}
debug_printf("Decouple:\n");
CheckBlock(samplesIn, expectedSamplesIn, NUM_USB_CHAN_IN);
/* Handshake back */
outuint(c_stim, 0);
}
break;
}
}
outct(c_stim, XS1_CT_END);
inct(c_stim);
return 0;
}

Some files were not shown because too many files have changed in this diff Show More