Compare commits

..

189 commits

Author SHA1 Message Date
fruchti 6b60117356 Add GLUT to nix shell 2024-06-04 21:47:47 +02:00
fruchti a9baea6e4d Fix libusb header paths 2024-06-04 20:00:20 +02:00
fruchti 7a8d24c872 Add nix shell 2024-06-04 20:00:20 +02:00
fruchti f9129b3957 Add LED matrix control through USB iso transfers 2024-06-04 20:00:17 +02:00
CNLohr 237d0534fa Update clang to not require Windows SDK. 2023-07-29 13:40:58 -04:00
cnlohr bf21e3bc67 Fix overflows. 2023-01-23 00:53:21 -05:00
cnlohr 138001ac4b A few tweaks. 2023-01-20 04:55:33 -05:00
cnlohr 966fea1756 lowspec phase 2: More of the CC algorithm. 2023-01-20 04:46:34 -05:00
cnlohr 9984ec1e15 Move really lowspec tests over. 2023-01-20 03:13:34 -05:00
cnlohr 2630cd19ba Head to head comparison. 2023-01-20 02:35:01 -05:00
cnlohr 95435d2918 Add note about careful selection of samplerate 2023-01-20 02:18:55 -05:00
cnlohr 0e78f44265 Add some mega lospec tests. 2023-01-20 02:14:22 -05:00
Cai Biesinger 45b483b96d Fixing cnfa module referencing nonexistant commit 2022-12-06 16:31:53 -07:00
CNLohr 34093a0ead Update CNFA 2022-11-12 14:07:53 -08:00
CNLohr 6dab01fb35 Fix build on unusual version of mingw. 2022-11-12 13:57:47 -08:00
Samuel Ellicott 72c880f701
Fix Windows build and build instructions 2022-10-22 01:21:21 -04:00
Sam Ellicott 300e50c33f Build and cleanup colorchord-opengl by default.
- Remove binary from tracked files
2022-10-21 23:04:18 -04:00
Sam Ellicott 9dc4b12708 Remove FPS counter on command line
- Don't show debug info by default
- Other minor formatting changes
2022-10-21 23:02:24 -04:00
Sam Ellicott ba996232a4 Default background is black 2022-10-21 22:57:59 -04:00
Sam Ellicott 744f65defb All files included in Windows build
- Displays that only work on linux have a compile guard
2022-10-21 22:57:12 -04:00
Sam Ellicott 66a577ef82 Edit default configurations
- Better documentation of settings
- Default settings work on Windows and Linux
- Move all configurations other than
  default to example_configs directory
- Modify main.c to handle config updates
2022-10-21 22:51:41 -04:00
CNLohr b349438aa7
Update LICENSE 2022-09-06 21:16:01 -07:00
Sam Ellicott 79c2a54e8b
Merge pull request #127 from Eugene8388608/master
Fixed color in DisplayRadialPoles
2021-12-12 09:54:58 -05:00
Eugene8388608 0883886fa2
Add files via upload 2021-12-12 02:24:35 +03:00
Sam Ellicott e7c3c6c362
Merge pull request #126 from Eugene8388608/master
Waveform was rotated 180 degrees and local code cleanup
2021-11-24 11:44:03 -05:00
Eugene8388608 ce43258505
Add files via upload 2021-11-24 00:53:59 +03:00
Eugene8388608 6dd5695403
Add files via upload 2021-11-24 00:29:42 +03:00
Sam Ellicott 0a44c60024
Update README.md
Fix path in Windows compile instructions
2021-11-11 20:17:28 -05:00
Sam Ellicott f6bfcb8bf9
Merge pull request #125 from Eugene8388608/master
Fix color representation bugs
2021-11-06 00:38:29 -04:00
Eugene8388608 551e857fb1
Add files via upload 2021-11-05 23:26:14 +03:00
Sam Ellicott 1ccf79205f
Merge pull request #123 from cnlohr/rawdraw-update
Rawdraw update
2021-06-07 20:47:29 -04:00
Sam Ellicott c800b70640 Update colorchord binary 2021-06-07 20:42:36 -04:00
Sam Ellicott da7190f0fa Make default to compile with openGL support 2021-06-07 20:39:35 -04:00
Sam Ellicott 0fa56bb434 Bump rawdraw version 2021-06-07 20:18:26 -04:00
Sam Ellicott 2feb2e9432 Bump rawdraw version 2021-06-05 00:12:57 -04:00
Sam Ellicott ffe467c2dd Define constants for various application colors 2021-06-05 00:12:38 -04:00
Sam Ellicott 9210cca20e Remove old, dead, code 2021-06-05 00:10:13 -04:00
Sam Ellicott 28612cadd8 Update to master branch of rawdraw
- Add HandleDestroy stub function
- Update HSVtoHEX function to RGBA
- Start moving over to CNFGColor()
2021-06-04 22:42:50 -04:00
Sam Ellicott 65a5a33c22 Slight update to the build instructions 2021-06-04 22:14:13 -04:00
Sam Ellicott 56007d28eb Updated devrecord
- Made windows loopback device the default
- Added instructions on how to do the same on Linux
2021-06-04 21:57:34 -04:00
Sam Ellicott 737ae717bb Fix tcc build on Windows 2021-06-03 08:28:59 -04:00
Sam Ellicott deeec1b716 Fix Windows build 2021-06-03 00:08:49 -04:00
Sam Ellicott 27ed88b5de
Merge pull request #122 from cnlohr/code-cleanup
Merge code cleanup branch
2021-05-22 15:50:20 -04:00
Sam Ellicott e87d16e905 Update Makefile with an opengl option 2021-05-22 15:47:42 -04:00
Sam Ellicott 08eb40a900 Fix formatting issues 2021-05-22 14:45:23 -04:00
Sam Ellicott 36ef604baf Update clang-format 2021-05-22 14:44:59 -04:00
Sam Ellicott e853558f7b bump CNFA submodule
Fix CNFA driver not being loaded with clang on Windows [Issue 121](https://github.com/cnlohr/colorchord/issues/121)
This was caused by improper #ifdef guards on driver initilization code
in CNFA.
2021-04-29 05:33:53 -04:00
CNLohr 82d44ae6b5
Merge pull request #120 from wesleygas/master
WLED Integration and example config
2021-04-10 23:52:08 -04:00
wesleygas 2fbc910415 WLED Integration and example config 2021-04-04 23:49:59 -03:00
Sam Ellicott b6b3039459 Fix allignment issue 2021-03-13 15:55:43 -05:00
Sam Ellicott cb9b6bc569 Use spaces for allignment instead of tabs 2021-03-13 14:23:52 -05:00
Sam Ellicott ad23066976 Fix some allignment issues noticed in github 2021-03-13 14:12:19 -05:00
Sam Ellicott 793e72df42 Fix some missed updated variable names 2021-03-13 14:02:53 -05:00
Sam Ellicott 2f89063b6a Clean up main.c 2021-03-13 13:58:40 -05:00
Sam Ellicott 472615bbf7 Update .clang-format with more rules 2021-03-13 13:58:17 -05:00
Sam Ellicott b28c4d2c40 Add .clang-format file 2021-03-13 10:54:50 -05:00
CNLohr 9cae6dc38d
Merge pull request #118 from cnlohr/esp8266-docs
Esp8266 docs
2021-02-13 11:34:42 -08:00
Sam Ellicott 892771700f Set default ESP_ROOT
-ESP_ROOT=~/esp8266/esp-open-sdk
2021-02-13 11:24:21 -05:00
Sam Ellicott cae295d989 Updated README files for Colorchord Embedded 2021-02-13 11:23:31 -05:00
CNLohr f8fb768415
Merge pull request #116 from CaiB/master
Fixing rare out-of-bounds crash
2021-01-01 04:24:39 -05:00
Cai Biesinger 6e5e659047 Fixing rare out-of-bounds crash 2020-12-31 20:35:13 -08:00
Sam Ellicott 947649ab89
Merge pull request #115 from timgates42/bugfix_typo_thoroughly
docs: fix simple typo, thurroughly -> thoroughly
2020-12-17 13:52:59 -06:00
Tim Gates 51eafab39d
docs: fix simple typo, thurroughly -> thoroughly
There is a small typo in embedded8266/README.md.

Should read `thoroughly` rather than `thurroughly`.
2020-12-18 06:41:30 +11:00
CNLohr d94c3eac94
Merge pull request #114 from hazcoper/fix-mul_defined_embeddedbins
Fix bug(?) if dft.c
2020-11-29 21:24:12 -08:00
CNLohr 7140c116ac
Merge pull request #113 from NikkyAI/non_linux_unix
Allow building on non-Linux Unix systems.
2020-11-29 20:54:21 -08:00
hazcoper f368da416a Fix bug(?) if dft.c 2020-11-01 23:33:42 +00:00
cnlohr b065da7474 Add radial display mode. 2020-10-25 21:50:54 -07:00
CNLohr a3eabc33eb
Update README.md 2020-10-05 12:47:22 -04:00
CNLohr 4e7f80e895
Update README.md 2020-10-05 12:46:32 -04:00
nia 19b70322ef Allow building on non-Linux Unix systems.
- Use pkg-config to find required libraries and include paths.
- Only include extra Linux libraries on Linux.
- Add libusb fallback code to hidapi.c
2020-10-03 09:38:38 +02:00
cnlohr 1b7675ddd7 Add output gamma 2020-09-26 14:48:47 -07:00
CNLohr 0121375d61 Make netlight dmx loop 2020-09-22 18:54:32 -04:00
CNLohr 11fe3a9b38 Update with DMX-512 output. Matching project here: https://github.com/cnlohr/esp8266_dmx_explorer 2020-09-20 20:30:55 -04:00
CNLohr bb2cb0dd9e Update colorchord on the STM32f303 2020-08-05 13:28:14 -04:00
CNLohr 2146a5c3f5 Make STM32F303x6/x8 vs xB/xC 2020-08-05 04:41:18 -04:00
cnlohr fdabac3b0d Fix inability to operate in headless mode. 2020-08-01 20:13:02 -07:00
cnlohr b6be6c14a4 Fix flipped argument order and update CNFA 2020-07-05 23:43:26 -07:00
CaiB 204fa77004 Correcting config parameter name. Fixes #108 2020-05-24 15:17:32 +09:00
NikkyAI d66addd256
switch out cc for $(CC) in Makefile 2020-05-22 11:12:27 +02:00
CNLohr f1eb47d0aa
Merge pull request #107 from CaiB/master
Fix a few build issues on Linux
2020-05-21 20:37:25 -07:00
Sam Ellicott 4b405029ae Merge branch 'master' of github.com:CaiB/colorchord 2020-05-21 23:13:24 -04:00
Sam Ellicott 5a6e342a23 Fix Linux build issues 2020-05-21 23:13:20 -04:00
Sam Ellicott 2cc03c3123
Merge pull request #7 from cnlohr/master
Merge in changes again
2020-05-21 23:10:50 -04:00
cnlohr 4626c40991 Bump submodule 2020-05-21 19:52:47 -07:00
cnlohr 052d1b32d0 Merge branch 'master' of https://github.com/cnlohr/colorchord 2020-05-21 19:52:26 -07:00
cnlohr aa4e95681c Update swapped order of ops in CNFA. 2020-05-21 19:52:10 -07:00
Sam Ellicott 26b6173853
Merge pull request #6 from cnlohr/master
Merge in changes from upstream
2020-05-21 22:40:55 -04:00
CNLohr 46156fe9e0
Update compile.bat 2020-05-21 02:38:22 -07:00
CNLohr 303d164e43 Update default.conf
Fix default dev recording and explain
2020-05-21 06:20:06 -07:00
CNLohr 99aefdef87 Bump ColorChord on Windows 2020-05-21 06:13:26 -07:00
cnlohr 0d1401cfa6 Some cleanup from merge. 2020-05-20 22:38:19 -07:00
CNLohr a4dd3dc4e1
Merge pull request #105 from CaiB/master
Add official WASAPI support to colorchord
2020-05-20 22:07:09 -07:00
Sam Ellicott 69dcc03f39 Slightly change default config 2020-05-21 00:03:53 -04:00
Sam Ellicott 4eb0eb8d4a Misc fixes:
-Make the submodule match upstream.
-Update Windows makefile to work.
2020-05-20 23:59:59 -04:00
Sam Ellicott 98623aa299 Fix CNFA initilization call. 2020-05-20 22:40:53 -04:00
Sam Ellicott 3274984ebf bump submodule version 2020-05-20 22:39:50 -04:00
Sam Ellicott d8578fe6d9 Fix TCC being dumb 2020-05-20 20:34:37 -04:00
Sam Ellicott c8eff4975b
Merge pull request #5 from NikkyAI/feature/android-network
add DisplayNetwork support to android
2020-05-20 18:55:58 -04:00
nikky 556338cfcb add DisplayNetwork support to android 2020-05-20 21:34:57 +02:00
CaiB bf95be1226 Clarifying config comments 2020-05-20 14:42:39 +09:00
CaiB d291fc224a Merging part 2 2020-05-20 14:40:52 +09:00
CaiB 003a1f4f8a Marging 2020-05-20 14:38:56 +09:00
Sam Ellicott 33f33ed9fd Oops, that could have been embareassing. 2020-05-20 00:10:59 -04:00
Sam Ellicott 779651da4b Remove VS-Code files 2020-05-20 00:04:14 -04:00
Sam Ellicott 4cc141bff1 Add more comments to the default configuration 2020-05-20 00:01:30 -04:00
Sam Ellicott bb0c7a0dc8 Add in NULL for the opaque object for CNFA. 2020-05-19 23:59:04 -04:00
Sam Ellicott 776a5dc5f5 bump submodule version 2020-05-19 23:55:16 -04:00
Sam Ellicott a2359e1f02 Modify build scripts 2020-05-19 23:39:10 -04:00
Sam Ellicott 2b0bb5a78d Redefine strdup to make Windows happy 2020-05-19 23:19:58 -04:00
CaiB cbc15a30c0 Ignoring clang output 2020-05-19 20:45:55 +09:00
CaiB 64c6c508b0 Updating CNFA and removing excessive console output on compile. 2020-05-19 20:41:20 +09:00
CaiB 80944b289f Update Windows build info in readme 2020-05-19 17:58:24 +09:00
CaiB 163e53d086 Merge branch 'cnlohr-master' 2020-05-19 17:40:10 +09:00
CaiB 64a01179ef Merge branch 'master' of https://github.com/cnlohr/colorchord into cnlohr-master
Fixing merge conflicts
2020-05-19 17:39:29 +09:00
Sam Ellicott 6fdbe1df89 Edit the default configuration 2020-05-18 22:15:19 -04:00
cnlohr 3c2c0d7fc9 Update .gitignore to ignore object files. 2020-05-18 15:47:12 -07:00
cnlohr 8449e772a0 Bump submodule 2020-05-18 15:46:22 -07:00
cnlohr 9d11a76f69 Switch search paths + update CNFA 2020-05-18 15:29:35 -07:00
cnlohr 911f60ae56 Update submodules 2020-05-18 15:25:20 -07:00
cnlohr df06ca12d7 Bump to new CNFA subsystem 2020-05-18 14:05:24 -07:00
Sam Ellicott af7afd6af5 Make vscode settings file 2020-05-17 19:25:17 -04:00
Sam Ellicott d10255bb61 Make clang not use windows headers 2020-05-17 19:24:43 -04:00
Sam Ellicott cddec68e5b Remove debugging printf. 2020-05-17 19:23:35 -04:00
Sam Ellicott 9029c87c96 Make a function to manually register parameters. 2020-05-17 19:22:57 -04:00
Sam Ellicott ffc4ac5d41 Force float constants for TCC 2020-05-17 19:19:21 -04:00
cnlohr 9c85dd87f2 TAG FOR RELEASE 8
Fix name of config file.
2020-05-17 05:41:13 -07:00
cnlohr 4dfeb870b7 Allow ColorChord to run down to Android 5.1 (SDK 22) 2020-05-17 05:11:32 -07:00
cnlohr 13ab20b1fc bump submodule 2020-05-17 03:06:42 -07:00
CaiB 92f765817c Adding new required DLL reference, and updating CNFA 2020-05-17 17:28:16 +09:00
CaiB e1896cf8d9 Updating CNFA 2020-05-17 15:46:51 +09:00
CaiB b6e9523895 Small tweaks to platform-dependent behaviour 2020-05-17 15:14:03 +09:00
cnlohr fb212a8d2f Try supporting back to version 26. 2020-05-16 22:57:17 -07:00
Cai Biesinger c6f64773a7
Merge pull request #1 from sellicott/master
Merging in Sam's Work
2020-05-17 13:29:15 +09:00
Sam Ellicott c7f058b72b bump submodule version 2020-05-16 03:21:25 -04:00
Sam Ellicott 2bbeb2d7df fixed bad URL 2020-05-16 02:55:01 -04:00
Sam Ellicott 4c229fbe27 switch submodule to https 2020-05-16 02:50:20 -04:00
Sam Ellicott 0288cec713 bump submodule 2020-05-16 02:42:46 -04:00
Sam Ellicott 3402711e6f Slightly change build scripts 2020-05-16 02:41:20 -04:00
Sam Ellicott f9ed862508 Make function prototype match implemented function 2020-05-16 02:40:45 -04:00
Sam Ellicott 1eb42353bd Added logging output 2020-05-16 02:40:17 -04:00
Sam Ellicott 4c3b7b294a Call necessary registration functions 2020-05-16 02:39:22 -04:00
Sam Ellicott 328852d9e9 fix reversal in fread input arguments 2020-05-16 02:35:49 -04:00
Sam Ellicott c51221b4fd Added necessary library to build 2020-05-15 18:52:57 -04:00
Sam Ellicott 184dd91a67 this still needs some cleanup 2020-05-15 02:15:38 -04:00
Sam Ellicott 457f979723 cnfa points to my repo 2020-05-15 02:15:21 -04:00
Sam Ellicott c27b90a1f2 updated tcc build script 2020-05-15 02:14:39 -04:00
Sam Ellicott 5fcc4b0881 Added script to compile with clang on Windows 2020-05-14 22:47:59 -04:00
Sam Ellicott d77eced8c5 Change sleep function 2020-05-14 22:47:20 -04:00
Sam Ellicott 3a19bf682b Merge branch 'master' of github.com:sellicott/colorchord 2020-05-14 21:05:51 -04:00
Sam Ellicott ce1c86fa1b Make CNFA module point to CaiB's repo 2020-05-14 21:01:23 -04:00
Sam Ellicott 84ccaf60e0 Update Makefile to compile with clang 2020-05-14 21:01:23 -04:00
Sam Ellicott 4a3d06397b Update main.c so that it matches latest code 2020-05-14 21:01:23 -04:00
Sam Ellicott d4e19a6a12 Added makefile to compile on Windows with clang 2020-05-14 21:01:23 -04:00
CNLohr 8cd62f98b2
Update TheoryOfCCDFT.md 2020-05-14 17:04:08 -07:00
cnlohr b1cbdc65c1 Undo version bump 2020-05-14 16:11:03 -07:00
cnlohr 0655094184 Use more universal android version by default. 2020-05-14 16:06:06 -07:00
cnlohr 22779c4a69 add docs 2020-05-14 13:59:18 -07:00
cnlohr 7207a8e560 Bump rawdrawandroid submodule 2020-05-13 23:19:55 -07:00
cnlohr 09f450a2a8 Fix compile on non-android platforms. 2020-05-13 23:14:21 -07:00
Sam Ellicott 72d8e1dce9 Make CNFA module point to CaiB's repo 2020-05-14 01:50:50 -04:00
Sam Ellicott 3030d2a873 Update Makefile to compile with clang 2020-05-14 01:49:01 -04:00
Sam Ellicott 7c6f7c6aaf Update main.c so that it matches latest code 2020-05-14 01:48:19 -04:00
Sam Ellicott ee621d9e75 Added makefile to compile on Windows with clang 2020-05-14 01:35:57 -04:00
cnlohr a6e629669c Fix build rev 2020-05-13 14:48:35 -07:00
cnlohr 8520283ad2 bump submodule 2020-05-12 03:41:07 -07:00
cnlohr 68c0ff8277 Bump submodules + fix back button configuration. 2020-05-12 03:39:05 -07:00
cnlohr 3ed6237c3b Cleanup and improve back button behavior 2020-05-12 03:31:47 -07:00
cnlohr 8db7e74cf2 bump rev 2020-05-11 22:17:48 -07:00
cnlohr 3f68bfd624 Fix app name 2020-05-11 22:00:02 -07:00
cnlohr cfc0003311 Update submodule 2020-05-11 15:01:49 -07:00
cnlohr 4cfc47e7af Fix access to external storage. 2020-05-11 14:52:28 -07:00
cnlohr c891563efd Allow other configuration schemes. 2020-05-11 14:25:25 -07:00
cnlohr 6c6e724325 Merge branch 'master' of https://github.com/cnlohr/colorchord 2020-05-11 14:00:43 -07:00
CNLohr ba3b608c17 Fix Windows compile. 2020-05-11 18:15:10 -07:00
cnlohr f90b1e12fd cleanup system button behavior. 2020-05-11 14:00:25 -07:00
cnlohr 58c7e0c224 Add ability for ColorChord-android to read external file. 2020-05-11 00:10:13 -07:00
cnlohr 24cdfc96e2 Actually prep for ColorChord being an example package. 2020-05-11 00:05:09 -07:00
cnlohr 534c2186f0 cleanup when Android is suspending our app. 2020-05-10 21:28:24 -07:00
cnlohr 79089566a1 make ColorChord work on Android. 2020-05-10 21:23:04 -07:00
cnlohr 241733ab9a Merge branch 'master' of https://github.com/cnlohr/colorchord 2020-05-10 17:16:19 -07:00
cnlohr 3d922b514c Rearchitect colorchord using CNFA + Rawdraw modules. 2020-05-10 17:15:59 -07:00
CNLohr 3fd52c4aba
Merge pull request #101 from angyongen/patch-1
Update adc.c
2020-05-04 06:20:36 -07:00
cnlohr e243c7200b Cleanup extra printfs 2020-05-03 07:18:13 -07:00
CNLohr acf2e9c180
Merge pull request #103 from cnlohr/androidtest
Get closer (but not there) to a working Android version.
2020-05-03 07:15:22 -07:00
cnlohr 825d356bb6 Update Makefile. 2020-05-03 07:14:30 -07:00
cnlohr 90dd695309 Update sound_android for use with rawdrawandroid. 2020-05-03 06:54:38 -07:00
cnlohr 03c938b453 Forgot to add some files that were used a while ago. 2020-05-02 20:47:13 -07:00
angyongen 0f17c793e2
Update adc.c
Please see the email I sent you.
2020-02-11 20:58:19 +08:00
cnlohr abca7b6407 new shot at androidbase 2019-11-17 05:53:02 -05:00
113 changed files with 5873 additions and 3068 deletions

44
.clang-format Normal file
View file

@ -0,0 +1,44 @@
# Basic style rules by Charles Lohr
BasedOnStyle: Microsoft
AccessModifierOffset: -4
AlwaysBreakTemplateDeclarations: 'Yes'
AlignAfterOpenBracket: DontAlign
AlignTrailingComments: 'true'
AllowAllParametersOfDeclarationOnNextLine: 'false'
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Allman
BreakConstructorInitializers: BeforeComma
ColumnLimit: '120'
ExperimentalAutoDetectBinPacking: 'false'
NamespaceIndentation: None
MaxEmptyLinesToKeep: 2
PointerAlignment: Right
SpaceInEmptyParentheses: 'false'
SpacesInAngles: 'true'
# this is just atrocious
#SpacesInCStyleCastParentheses: 'true'
SpacesInContainerLiterals: 'true'
SpacesInParentheses: 'true'
SpacesInSquareBrackets: 'true'
TabWidth: '4'
UseTab: AlignWithSpaces
FixNamespaceComments: 'true'
IndentCaseLabels: 'true'
# Additions by Sam Ellicott
ColumnLimit: 100
# From general dislike from Charles and Cai, I am turning these off
#AlignConsecutiveAssignments: true
#AlignConsecutiveMacros: true
#AlignTrailingComments: true
#AlignOperands: AlignAfterOperator
# Turn off alignment
AlignConsecutiveAssignments: false
AlignConsecutiveMacros: false
AlignTrailingComments: false
AlignEscapedNewlines: Left
AlignOperands: AlignAfterOperator
AllowShortBlocksOnASingleLine: Always
AllowShortIfStatementsOnASingleLine: WithoutElse
AllowShortCaseLabelsOnASingleLine: true
AllowShortLoopsOnASingleLine: true

7
.gitignore vendored
View file

@ -1,2 +1,7 @@
colorchord2/windows/colorchord.def
colorchord2/colorchord.def
colorchord2/colorchord.def
*.o
**/*.exp
**/*.ilk
**/*.pdb
colorchord2/colorchord.lib

9
.gitmodules vendored
View file

@ -1,3 +1,12 @@
[submodule "embedded8266/esp82xx"]
path = embedded8266/esp82xx
url = https://github.com/cnlohr/esp82xx.git
[submodule "colorchord2/rawdraw"]
path = colorchord2/rawdraw
url = https://github.com/cntools/rawdraw
[submodule "colorchord2/android/rawdrawandroid"]
path = colorchord2/android/rawdrawandroid
url = https://github.com/cnlohr/rawdrawandroid
[submodule "colorchord2/cnfa"]
path = colorchord2/cnfa
url = https://github.com/cntools/cnfa.git

12
LICENSE
View file

@ -1,4 +1,14 @@
Copyright (c) 2015, Charles Lohr (CNLohr)
Copyright (c) 2012-2022, Charles Lohr (CNLohr), et. al.
This license covers the core of ColorChord, but not the ESP8266 or STM32
embedded builds, for them, they have a separate license.
All major contributors have agreed to allowing ColorChord to be distributed
in either CC0, MIT-x11 or NewBSD license terms as of September, 2022.
Depending on the user's preferences and needs. You may feel free to copy
and use ColorChord in your project under ther terms listed below (NewBSD
license) or any of the afore-mentioned licenses.
All rights reserved.
Redistribution and use in source and binary forms, with or without

View file

@ -1,6 +1,25 @@
ColorChord
==========
Table of Contents
-----------------
- [ColorChord](#colorchord)
- [Table of Contents](#table-of-contents)
- [What is ColorChord?](#what-is-colorchord)
- [Background](#background)
- [Current State of Affairs](#current-state-of-affairs)
- [ColorChord: Embedded](#colorchord-embedded)
- [Build Instructions](#build-instructions)
- [Building ColorChord Embedded](#building-colorchord-embedded)
- [Building with Linux](#building-with-linux)
- [Building with Windows](#building-with-windows)
- [clang](#clang)
- [TCC](#tcc)
- [MSYS2](#msys2)
- [Using](#using)
- [Additional Videos](#additional-videos)
What is ColorChord?
-------------------
@ -42,14 +61,36 @@ ColorChord: Embedded
There is work on an embedded version of ColorChord, which avoids floating point operations anywhere in the output pipeline. Though I have made efforts to port it to AVRs, it doesn't seem feasable to operate on AVRs without some shifty tricks which I'd like to avoid, so I have retargeted my efforts to 32-bit systems, such as the STM32F303, STM32F407, and (somehow) the ESP8266. ColorChord Embedded uses a different codebase, located in the [embeddedcommon](/embeddedcommon) and distributed among the various embedded* folders.
Build Instructions
==================
Building ColorChord Embedded
----------------------------
The embedded version of Colorchord has a different build system than the desktop versions of Colorchord. See the build instructions for each of the supported embedded architectures in their respective folders
- [esp8266](embedded8266/README.md#build-instructions)
- [stm32f303](embeddedstm32f303/README.md)
- [stm32f407](embeddedstm32f407/) - TODO Add readme
Building with Linux
-----------------
Use `apt-get` to install the following packages for Debian/Ubuntu/Mint:
From the linux terminal use `apt-get` to install the following packages for Debian/Ubuntu/Mint:
```
apt-get install libpulse-dev libasound2-dev libx11-dev libxext-dev libxinerama-dev libusb-1.0-0-dev libudev-dev
apt-get install build-essential libpulse-dev libasound2-dev libx11-dev libxext-dev libxinerama-dev libusb-1.0-0-dev libudev-dev
```
To get colorchord, type:
```
git clone --recurse-submodules https://github.com/cnlohr/colorchord
```
This will download the colorchord source code into the colorchord directory
You really want to be using colorchord2, so we need to move to it's build directory to compile the source code.
```
cd colorchord/colorchord2
```
To make colorchord, type:
```
@ -58,6 +99,33 @@ make
Building with Windows
-------------------
There are 3 options available for building on Windows, clang, or TCC, MSYS2.
### clang
Start by [downloading](https://clang.llvm.org/) the clang compiler, and installing it.
You can also install using `winget` using the following command in PowerShell
```
winget install -e --id LLVM.LLVM
```
If you have the Windows SDK installed, you should not need to do any additional work.
If you do not, you'll want to either [install it](https://developer.microsoft.com/en-US/windows/downloads/windows-11-sdk/) to get the official headers. Realistically, it's easyist to get the headers by installing Visual Studio Communitiy and selecting the C/C++ Desktop Application devlopment package and installing it from there [Visual Studio](https://visualstudio.microsoft.com/).
Once the Windows headers are installed run the clang batch script, and it should output to `colorchord2/colorchord.exe`.
```
./compile-clang.bat
```
### TCC
Start by [downloading TCC](http://savannah.nongnu.org/projects/tinycc/), and extracting it to `C:\tcc`.
You can put it anywhere you like, but the compile script defaults to this location. If you install
to the default location, you can skip the next step.
Edit the batch script at `colorchord2/windows/compile.bat`:
- Edit line 17 (`CC`) to be the location where you put TCC. If there are spaces in the path, wrap the entire path in quotes.
Note that TCC is not able to use the Windows SDK, and as such using the unofficial headers is required, and automatically enabled when compiling with TCC. If you encounter issues, try the clang method above instead. TCC does not support open-gl rendering and is limited to software rendering.
### MSYS2
With either 64bit or 32bit [MSYS2](https://msys2.github.io/) installed, run the _MSYS2 MSYS_ launcher and use `pacman` to set up a MinGW32 toolchain, if you don't have one already:
```
pacman -S mingw-w64-i686-toolchain
@ -82,6 +150,7 @@ To run colorchord, use the following syntax:
If you edit default.conf while the program is running and resave it, it will use the settings in the newly saved file.
Note that the colorchord executable file is located in the colorchord2 directory in the colorchord repository.
Additional Videos
-----------------

View file

@ -0,0 +1,14 @@
PROJS:=basic_oldstyle_test
all : $(PROJS)
CFLAGS:=-I../../colorchord2/rawdraw -g
LDFLAGS:=-lGL -lm -lpthread -lX11
$(PROJS): %: %.c
gcc -o $@ $^ $(CFLAGS) $(LDFLAGS)
clean :
rm -rf $(PROJS)

View file

@ -0,0 +1,452 @@
/*
An experiment in very, very low-spec ColorChord. This technique foregoes
multiplies.
Approach 1:
This approach uses a table, like several colorchord algorithms to only
process one octave each cycle. It then uses a table to decide how often
to process each bin. It performs that bin at 4x the bin's sampling
frequency, in quadrature. So that it performs the +real, +imag, -real,
-imag operations over each cycle.
You can observe an overtone at the current bin - 1.5 octaves! This is
expected, since, it's the inverse of what the DFT of a square wave would
be.
That is a minor drawback, but the **major** drawback is that any DC
offset, OR lower frequencies present when computing higher frequencies will
induce a significant ground flutter, and make results really inaccurate.
This will need to be addressed before the algorithm is ready for prime-time.
NOTE: To explore:
1) Consider SAMPLE_Q to possibly use all 4 cycles - though this will
add latency, it will be more "accurate" --> YES! This helps!
2) Use the cursor to move left and right to sweep tone and up and down
to change DC-bias. This exhibits this algo's shortcoming.
TODO: Can we somehow zero out the DC offset?
DISCOVERY: This approach (octave-step in octave) is very sensitive FSPS
Theories:
* If we force all 4 quadrature value to be the same number of
integration cycles, does that solve it? --> NO! IT GETS MESSY (see Approach 1B)
*/
#include <stdio.h>
#include <math.h>
#define CNFG_IMPLEMENTATION
#define CNFGOGL
#include "rawdraw_sf.h"
#include "os_generic.h"
uint32_t EHSVtoHEX( uint8_t hue, uint8_t sat, uint8_t val );
int binmode = 0;
int additionaltone = 0;
int lastx = 200;
int lasty = 1000;
// Keep notimes as small as possible
static int timul( int adder, int notimes )
{
int sum = 0;
while( notimes )
{
if ( notimes & 1) sum += adder;
adder <<= 1;
notimes >>= 1;
}
return sum;
}
int main()
{
#define FSPS 22100 // Careful, this matters! It is selected to avoid periodic peaks encountered with DC offsets.
#define OCTAVES 6
#define BPERO 32
#define BASE_FREQ 22.5
#define QUADRATURE_STEP_DENOMINATOR 16384
// This ultimately determines smoothness + q.
#define COMPLEX_IIR 3
#define TEST_SAMPLES 1024
int16_t samples[TEST_SAMPLES];
int i;
CNFGSetup( "Example App", 1024, 768 );
// Precomputed Tables
int8_t whichoctave[2<<OCTAVES];
for( i = 0; i < (2<<OCTAVES); i++ )
{
int j;
for( j = 0; j < OCTAVES; j++ )
{
if( i & (1<<j) ) break;
}
if( j == OCTAVES )
whichoctave[i] = -1;
else
whichoctave[i] = OCTAVES - j - 1;
}
// Make a running counter to count up by this amount every cycle.
// If the new number > 2048, then perform a quadrature step.
int32_t flipdistance[BPERO];
for( i = 0; i < BPERO; i++ )
{
double freq = pow( 2, (double)i / (double)BPERO ) * (BASE_FREQ/2.0);
double pfreq = pow( 2, OCTAVES ) * freq;
double spacing = (FSPS / 2) / pfreq / 4;
flipdistance[i] = QUADRATURE_STEP_DENOMINATOR * spacing;
// Spacing = "quadrature every X samples"
//printf( "%f %d\n", spacing, flipdistance[i] );
}
struct bindata
{
int32_t quadrature_timing_last;
uint32_t last_accumulated_value[2];
int32_t real_imaginary_running[2];
uint32_t magsum;
uint8_t quadrature_state;
uint8_t reserved1, reserved2, reserved3;
} bins[BPERO*OCTAVES] = { 0 };
// This is for timing. Not accumulated data.
uint32_t octave_timing[OCTAVES] = { 0 };
uint32_t sample_accumulator = 0;
int frameno = 0;
double dLT = OGGetAbsoluteTime();
int samplenoIn = 0;
int sampleno = 0;
double ToneOmega = 0;
double ToneOmega2 = 0;
int ops;
while( CNFGHandleInput() )
{
short w, h;
CNFGGetDimensions( &w, &h );
CNFGClearFrame();
frameno++;
float freq =
//pow( 2, (frameno%600)/100.0 ) * 25;
pow( 2, (lastx)/100.0 ) * 22.5;
//101;
float freq2 =
//pow( 2, (frameno%600)/100.0 ) * 25;
pow( 2, (500)/100.0 ) * 22.5;
//101;
for( i = 0; i < TEST_SAMPLES; i++ )
{
samples[i] = (lasty/5 + sin( ToneOmega ) * 127 + (additionaltone?(sin(ToneOmega2)*128):0))/2;// + (rand()%128)-64;
ToneOmega += 1 / (double)FSPS * (double)freq * 3.14159 * 2.0;
ToneOmega2 += 1 / (double)FSPS * (double)freq2 * 3.14159 * 2.0;
}
char cts[1024];
sprintf( cts, "%f %d %f binmode: %d", freq, sampleno, ops/(double)sampleno, binmode );
CNFGColor( 0xffffffff );
CNFGPenX = 2;
CNFGPenY = 2;
CNFGDrawText( cts, 2 );
while( OGGetAbsoluteTime() < dLT + TEST_SAMPLES / (double)FSPS );
dLT += TEST_SAMPLES / (double)FSPS;
for( i = 0; i < TEST_SAMPLES; i++ )
{
sample_accumulator += samples[i];
int octave = whichoctave[(sampleno)&((2<<OCTAVES)-1)];
sampleno++;
if( octave < 0 )
{
// A "free cycle" this happens every 1/(2^octaves)
continue;
}
#define WATCHBIN 1
int b;
int binno = octave * BPERO;
int ocative_time = octave_timing[octave] += QUADRATURE_STEP_DENOMINATOR;
for( b = 0; b < BPERO; b++, binno++ )
{
ops+=5;
struct bindata * thisbin = &bins[binno];
if( ocative_time - thisbin->quadrature_timing_last > 0 )
{
ops+=20;
thisbin->quadrature_timing_last += flipdistance[b];
// This code will get appropriately executed every quadrature update.
int qstate = thisbin->quadrature_state = ( thisbin->quadrature_state + 1 ) % 4;
int last_q_bin = ( qstate & 1 );
int delta;
if( binmode )
{
delta = sample_accumulator - thisbin->last_accumulated_value[0];
thisbin->last_accumulated_value[0] = sample_accumulator;
delta *= 1.5;
}
else
{
delta = sample_accumulator - thisbin->last_accumulated_value[last_q_bin];
thisbin->last_accumulated_value[last_q_bin] = sample_accumulator;
}
// Qstate =
// (0) = +Cos, (1) = +Sin, (2) = -Cos, (3) = -Sin
if( qstate & 2 ) delta *= -1;
// Update real and imaginary components with delta.
int running = thisbin->real_imaginary_running[last_q_bin];
running = running + delta;
thisbin->real_imaginary_running[last_q_bin] = running;
// Only perform on full quadrature completions.
if( qstate == 0 )
{
ops+=20;
int newmagR = thisbin->real_imaginary_running[0];
int newmagI = thisbin->real_imaginary_running[1];
thisbin->real_imaginary_running[0] = newmagR - (newmagR>>COMPLEX_IIR);
thisbin->real_imaginary_running[1] = newmagI - (newmagI>>COMPLEX_IIR);
// Super-cheap, non-multiply, approximate complex vector magnitude calculation.
newmagR = (newmagR<0)?-newmagR:newmagR;
newmagI = (newmagI<0)?-newmagI:newmagI;
int newmag =
//sqrt(newmagR*newmagR + newmagI*newmagI );
newmagR > newmagI ? newmagR + (newmagI>>1) : newmagI + (newmagR>>1);
thisbin->magsum = newmag >> (OCTAVES-octave);
}
}
}
}
int folded_bins[BPERO];
// Taper and fold (happens when we want to update lights)
for( i = 0; i < BPERO; i++ )
{
int b = i;
int running;
running = bins[b].magsum * i / BPERO;
b+=BPERO;
int j;
for( j = 1; j < OCTAVES-1; j++ )
{
running += bins[b].magsum;
b+=BPERO;
}
running += bins[b].magsum * ( BPERO - i ) / BPERO;
folded_bins[i] = running * pow( 2, (i%BPERO) / (double)BPERO ); //XXX TODO: Make this fixed.
}
int lx, ly;
#if 0
// Boxcar filter
int fuzzed_bins[BPERO];
int fuzzed_bins2[BPERO];
// Taper and fold (happens when we want to update lights)
for( i = 0; i < BPERO; i++ )
{
fuzzed_bins[i] = (folded_bins[i] + (folded_bins[(i+1)%BPERO]>>0) + (folded_bins[(i+BPERO-1)%BPERO]>>0))>>1;
}
// Taper and fold (happens when we want to update lights)
for( i = 0; i < BPERO; i++ )
{
fuzzed_bins2[i] = (fuzzed_bins[i] + (folded_bins[(i+1)%BPERO]>>0) + (fuzzed_bins[(i+BPERO-1)%BPERO]>>0))>>1;
}
// Filter agian.
for( i = 0; i < BPERO; i++ )
{
fuzzed_bins[i] = (fuzzed_bins2[i] + (fuzzed_bins2[(i+1)%BPERO]>>0) + (fuzzed_bins2[(i+BPERO-1)%BPERO]>>0))>>1;
}
// Taper and fold (happens when we want to update lights)
for( i = 0; i < BPERO; i++ )
{
fuzzed_bins2[i] = (fuzzed_bins[i] + (folded_bins[(i+1)%BPERO]>>0) + (fuzzed_bins[(i+BPERO-1)%BPERO]>>0))>>1;
fuzzed_bins2[i] = fuzzed_bins2[i]>>1;
}
#else
int fuzzed_bins2[BPERO];
int fziir = folded_bins[i];
// Forward IIR
int j;
#define FUZZ_IIR 2
for( j = 0; j < 4; j++ )
for( i = 0; i < BPERO; i++ )
{
// Initiate IIR
fziir = fziir - (fziir>>FUZZ_IIR) + folded_bins[i];
}
for( i = 0; i < BPERO; i++ )
{
fziir = fziir - (fziir>>FUZZ_IIR) + folded_bins[i];;
fuzzed_bins2[i] = fziir;
}
// reverse IIR.
for( j = 0; j < 4; j++ )
for( i = BPERO-1; i >= 0; i-- )
{
// Initiate IIR
fziir = fziir - (fziir>>FUZZ_IIR) + fuzzed_bins2[i];
}
for( i = BPERO-1; i >= 0; i-- )
{
fziir = fziir - (fziir>>FUZZ_IIR) + fuzzed_bins2[i];
fuzzed_bins2[i] = fziir;
}
int minfuzz = fziir;
for( i = 0; i < BPERO; i++ )
{
if( minfuzz > fuzzed_bins2[i] ) minfuzz = fuzzed_bins2[i];
}
for( i = 0; i < BPERO; i++ )
{
fuzzed_bins2[i] = ( fuzzed_bins2[i] - minfuzz ) >> FUZZ_IIR;
}
#endif
for( i = 0; i < BPERO*OCTAVES; i++ )
{
CNFGColor( (EHSVtoHEX( (i * 256 / BPERO)&0xff, 255, 255 ) << 8) | 0xff );
float mag = (float)bins[i].magsum * pow( 2, (i%BPERO) / (double)BPERO );
int y = 768 - mag/20;
if( i ) CNFGTackSegment( i*4, y, lx*4, ly );
lx = i; ly= y;
}
for( i = 0; i < BPERO*2; i++ )
{
CNFGColor( (EHSVtoHEX( (i * 256 / BPERO)&0xff, 255, 255 ) << 8) | 0xff );
float mag = (float)folded_bins[i%BPERO];
int y = 600 - mag/100;
if( i ) CNFGTackSegment( i*8, y, lx*8, ly );
lx = i; ly= y;
}
for( i = 0; i < BPERO*2; i++ )
{
CNFGColor( (EHSVtoHEX( (i * 256 / BPERO)&0xff, 255, 255 ) << 8) | 0xff );
float mag = (float)fuzzed_bins2[i%BPERO];
int y = 500 - mag/100;
if( i ) CNFGTackSegment( i*8, y, lx*8, ly );
lx = i; ly= y;
}
CNFGSwapBuffers();
}
}
void HandleKey( int keycode, int bDown ) { if( bDown ) { if( keycode == 'a' ) binmode =!binmode; if( keycode == 'b' ) additionaltone = ! additionaltone; } }
void HandleButton( int x, int y, int button, int bDown ) { }
void HandleMotion( int x, int y, int mask ) { lastx = x; lasty = y; }
void HandleDestroy() { }
uint32_t EHSVtoHEX( uint8_t hue, uint8_t sat, uint8_t val )
{
#define SIXTH1 43
#define SIXTH2 85
#define SIXTH3 128
#define SIXTH4 171
#define SIXTH5 213
uint16_t or = 0, og = 0, ob = 0;
hue -= SIXTH1; //Off by 60 degrees.
//TODO: There are colors that overlap here, consider
//tweaking this to make the best use of the colorspace.
if( hue < SIXTH1 ) //Ok: Yellow->Red.
{
or = 255;
og = 255 - ((uint16_t)hue * 255) / (SIXTH1);
}
else if( hue < SIXTH2 ) //Ok: Red->Purple
{
or = 255;
ob = (uint16_t)hue*255 / SIXTH1 - 255;
}
else if( hue < SIXTH3 ) //Ok: Purple->Blue
{
ob = 255;
or = ((SIXTH3-hue) * 255) / (SIXTH1);
}
else if( hue < SIXTH4 ) //Ok: Blue->Cyan
{
ob = 255;
og = (hue - SIXTH3)*255 / SIXTH1;
}
else if( hue < SIXTH5 ) //Ok: Cyan->Green.
{
og = 255;
ob = ((SIXTH5-hue)*255) / SIXTH1;
}
else //Green->Yellow
{
og = 255;
or = (hue - SIXTH5) * 255 / SIXTH1;
}
uint16_t rv = val;
if( rv > 128 ) rv++;
uint16_t rs = sat;
if( rs > 128 ) rs++;
//or, og, ob range from 0...255 now.
//Need to apply saturation and value.
or = (or * val)>>8;
og = (og * val)>>8;
ob = (ob * val)>>8;
//OR..OB == 0..65025
or = or * rs + 255 * (256-rs);
og = og * rs + 255 * (256-rs);
ob = ob * rs + 255 * (256-rs);
//printf( "__%d %d %d =-> %d\n", or, og, ob, rs );
or >>= 8;
og >>= 8;
ob >>= 8;
return or | (og<<8) | ((uint32_t)ob<<16);
}

View file

@ -0,0 +1,14 @@
PROJS:=test_using_square_wave_octave_approach test_using_work_selection_heap test_using_work_selection_table test_using_square_wave_octave_approach_stipple
all : $(PROJS)
CFLAGS:=-I../colorchord2/rawdraw -g
LDFLAGS:=-lGL -lm -lpthread -lX11
$(PROJS): %: %.c
gcc -o $@ $^ $(CFLAGS) $(LDFLAGS)
clean :
rm -rf $(PROJS)

View file

@ -0,0 +1,301 @@
/*
An experiment in very, very low-spec ColorChord. This technique foregoes
multiplies.
Approach 1B --- This variant locks the number of updates for any given
integration to exactly the quadrature size. It was to test theory 1.
Approach 1 is square_wave_octave_approac_stipple.
*/
#include <stdio.h>
#include <math.h>
#define CNFG_IMPLEMENTATION
#define CNFGOGL
#include "rawdraw_sf.h"
#include "os_generic.h"
uint32_t EHSVtoHEX( uint8_t hue, uint8_t sat, uint8_t val );
int lastx = 200;
int lasty = 1000;
int main()
{
#define FSPS 16000
#define OCTAVES 6
#define BPERO 24
#define BASE_FREQ 22.5
#define QUADRATURE_STEP_DENOMINATOR 16384
// Careful: It makes a lot of sense to explore these relationships.
#define SAMPLE_Q 4
#define MAG_IIR 0
#define RUNNING_IIR 31
#define COMPLEX_IIR 2
#define TEST_SAMPLES 256
int16_t samples[TEST_SAMPLES];
int i;
CNFGSetup( "Example App", 1024, 768 );
// Precomputed Tables
int8_t whichoctave[2<<OCTAVES];
for( i = 0; i < (2<<OCTAVES); i++ )
{
int j;
for( j = 0; j < OCTAVES; j++ )
{
if( i & (1<<j) ) break;
}
if( j == OCTAVES )
whichoctave[i] = -1;
else
whichoctave[i] = OCTAVES - j - 1;
}
// Make a running counter to count up by this amount every cycle.
// If the new number > 2048, then perform a quadrature step.
int32_t flipdistance[BPERO];
int binstothis[BPERO*OCTAVES] = { 0 };
int nextloopbins[BPERO*OCTAVES] = { 0 };
for( i = 0; i < BPERO; i++ )
{
double freq = pow( 2, (double)i / (double)BPERO ) * (BASE_FREQ/2.0);
double pfreq = pow( 2, OCTAVES ) * freq;
double spacing = (FSPS / 2) / pfreq / 4;
flipdistance[i] = QUADRATURE_STEP_DENOMINATOR * spacing;
// Spacing = "quadrature every X samples"
//printf( "%f %d\n", spacing, flipdistance[i] );
//flipdistance[i] = QUADRATURE_STEP_DENOMINATOR * (int)(spacing+0.5);
binstothis[i] = (int)(spacing+0.5);
nextloopbins[i] = binstothis[i];
}
// This is for timing. Not accumulated data.
int32_t quadrature_timing_last[BPERO*OCTAVES] = { 0 };
uint8_t quadrature_state[BPERO*OCTAVES] = { 0 };
uint32_t last_accumulated_value[BPERO*OCTAVES*2] = { 0 };
uint32_t octave_timing[OCTAVES] = { 0 };
int32_t real_imaginary_running[BPERO*OCTAVES*2] = { 0 };
uint32_t sample_accumulator = 0;
int32_t qcount[BPERO*OCTAVES] = { 0 };
int32_t magsum[BPERO*OCTAVES] = { 0 };
int frameno = 0;
double dLT = OGGetAbsoluteTime();
int samplenoIn = 0;
int sampleno = 0;
double ToneOmega = 0;
while( CNFGHandleInput() )
{
CNFGClearFrame();
frameno++;
float freq =
//pow( 2, (frameno%600)/100.0 ) * 25;
pow( 2, (lastx)/100.0 ) * lastx;
//101;
for( i = 0; i < TEST_SAMPLES; i++ )
{
samples[i] = lasty/5 + sin( ToneOmega ) * 127;// + (rand()%128)-64;
ToneOmega += 1 / (double)FSPS * (double)freq * 3.14159 * 2.0;
}
char cts[1024];
sprintf( cts, "%f %d", freq, sampleno );
CNFGColor( 0xffffffff );
CNFGPenX = 2;
CNFGPenY = 2;
CNFGDrawText( cts, 2 );
while( OGGetAbsoluteTime() < dLT + TEST_SAMPLES / (double)FSPS );
dLT += TEST_SAMPLES / (double)FSPS;
for( i = 0; i < TEST_SAMPLES; i++ )
{
sample_accumulator += samples[i];
int octave = whichoctave[(sampleno)&((2<<OCTAVES)-1)];
sampleno++;
if( octave < 0 )
{
// A "free cycle" this happens every 1/2^octaves
continue;
}
#define WATCHBIN -1
int b;
int binno = octave * BPERO;
int ocative_time = octave_timing[octave] += QUADRATURE_STEP_DENOMINATOR;
for( b = 0; b < BPERO; b++, binno++ )
{
if( binno == WATCHBIN )
{
printf( "%d %d %d %6d %6d %6d\n", ocative_time, quadrature_timing_last[binno], quadrature_state[0], real_imaginary_running[0], real_imaginary_running[1], magsum[0] );
}
if( --nextloopbins[binno] <= 0 )
{
// This code will get appropriately executed every quadrature update.
int qstate = quadrature_state[binno] = ( quadrature_state[binno] + 1 ) % 4;
int last_q_bin = (binno * 2) + ( qstate & 1 );
int delta = sample_accumulator - last_accumulated_value[last_q_bin];
last_accumulated_value[last_q_bin] = sample_accumulator;
if( binno == WATCHBIN )
printf( "Delta: %d\n", delta );
// Qstate =
// (0) = +Cos, (1) = +Sin, (2) = -Cos, (3) = -Sin
if( qstate & 2 ) delta *= -1;
// Update real and imaginary components with delta.
int running = real_imaginary_running[last_q_bin];
running = running - (running>>RUNNING_IIR) + delta;
real_imaginary_running[last_q_bin] = running;
int q = ++qcount[binno];
if( q == SAMPLE_Q ) // Effective Q factor.
{
qcount[binno] = 0;
int newmagR = real_imaginary_running[(binno * 2)];
int newmagI = real_imaginary_running[(binno * 2)+1];
real_imaginary_running[(binno * 2)] = newmagR - (newmagR>>COMPLEX_IIR);
real_imaginary_running[(binno * 2)+1] = newmagI - (newmagI>>COMPLEX_IIR);
// Super-cheap, non-multiply, approximate complex vector magnitude calculation.
newmagR = (newmagR<0)?-newmagR:newmagR;
newmagI = (newmagI<0)?-newmagI:newmagI;
int newmag =
//sqrt(newmagR*newmagR + newmagI*newmagI );
newmagR > newmagI ? newmagR + (newmagI>>1) : newmagI + (newmagR>>1);
int lastmag = magsum[binno];
magsum[binno] = lastmag - (lastmag>>MAG_IIR) + newmag;
quadrature_timing_last[binno] += flipdistance[b]*4;
int nextsteps = -(ocative_time - quadrature_timing_last[binno])/QUADRATURE_STEP_DENOMINATOR;
binstothis[binno] = nextsteps/4;
}
nextloopbins[binno] = binstothis[binno];
}
}
}
int lx, ly;
for( i = 0; i < BPERO*OCTAVES; i++ )
{
CNFGColor( (EHSVtoHEX( (i * 256 / BPERO)&0xff, 255, 255 ) << 8) | 0xff );
float real = real_imaginary_running[i*2+0];
float imag = real_imaginary_running[i*2+1];
float mag = sqrt( real * real + imag * imag );
mag = (float)magsum[i] * pow( 2, i / (double)BPERO );
int y = 768 - ((int)(mag / FSPS * 10) >> MAG_IIR);
if( i ) CNFGTackSegment( i*4, y, lx*4, ly );
lx = i; ly= y;
//printf( "%d %d\n", real_imaginary_running[i*2+0], real_imaginary_running[i*2+1] );
}
CNFGSwapBuffers();
}
}
void HandleKey( int keycode, int bDown ) { }
void HandleButton( int x, int y, int button, int bDown ) { }
void HandleMotion( int x, int y, int mask ) { lastx = x; lasty = y; }
void HandleDestroy() { }
uint32_t EHSVtoHEX( uint8_t hue, uint8_t sat, uint8_t val )
{
#define SIXTH1 43
#define SIXTH2 85
#define SIXTH3 128
#define SIXTH4 171
#define SIXTH5 213
uint16_t or = 0, og = 0, ob = 0;
hue -= SIXTH1; //Off by 60 degrees.
//TODO: There are colors that overlap here, consider
//tweaking this to make the best use of the colorspace.
if( hue < SIXTH1 ) //Ok: Yellow->Red.
{
or = 255;
og = 255 - ((uint16_t)hue * 255) / (SIXTH1);
}
else if( hue < SIXTH2 ) //Ok: Red->Purple
{
or = 255;
ob = (uint16_t)hue*255 / SIXTH1 - 255;
}
else if( hue < SIXTH3 ) //Ok: Purple->Blue
{
ob = 255;
or = ((SIXTH3-hue) * 255) / (SIXTH1);
}
else if( hue < SIXTH4 ) //Ok: Blue->Cyan
{
ob = 255;
og = (hue - SIXTH3)*255 / SIXTH1;
}
else if( hue < SIXTH5 ) //Ok: Cyan->Green.
{
og = 255;
ob = ((SIXTH5-hue)*255) / SIXTH1;
}
else //Green->Yellow
{
og = 255;
or = (hue - SIXTH5) * 255 / SIXTH1;
}
uint16_t rv = val;
if( rv > 128 ) rv++;
uint16_t rs = sat;
if( rs > 128 ) rs++;
//or, og, ob range from 0...255 now.
//Need to apply saturation and value.
or = (or * val)>>8;
og = (og * val)>>8;
ob = (ob * val)>>8;
//OR..OB == 0..65025
or = or * rs + 255 * (256-rs);
og = og * rs + 255 * (256-rs);
ob = ob * rs + 255 * (256-rs);
//printf( "__%d %d %d =-> %d\n", or, og, ob, rs );
or >>= 8;
og >>= 8;
ob >>= 8;
return or | (og<<8) | ((uint32_t)ob<<16);
}

View file

@ -0,0 +1,323 @@
/*
An experiment in very, very low-spec ColorChord. This technique foregoes
multiplies.
Approach 1:
This approach uses a table, like several colorchord algorithms to only
process one octave each cycle. It then uses a table to decide how often
to process each bin. It performs that bin at 4x the bin's sampling
frequency, in quadrature. So that it performs the +real, +imag, -real,
-imag operations over each cycle.
You can observe an overtone at the current bin - 1.5 octaves! This is
expected, since, it's the inverse of what the DFT of a square wave would
be.
That is a minor drawback, but the **major** drawback is that any DC
offset, OR lower frequencies present when computing higher frequencies will
induce a significant ground flutter, and make results really inaccurate.
This will need to be addressed before the algorithm is ready for prime-time.
NOTE: To explore:
1) Consider SAMPLE_Q to possibly use all 4 cycles - though this will
add latency, it will be more "accurate" --> YES! This helps!
2) Use the cursor to move left and right to sweep tone and up and down
to change DC-bias. This exhibits this algo's shortcoming.
TODO: Can we somehow zero out the DC offset?
DISCOVERY: This approach (octave-step in octave) is very sensitive FSPS
Theories:
* If we force all 4 quadrature value to be the same number of
integration cycles, does that solve it? --> NO! IT GETS MESSY (see Approach 1B)
*/
#include <stdio.h>
#include <math.h>
#define CNFG_IMPLEMENTATION
#define CNFGOGL
#include "rawdraw_sf.h"
#include "os_generic.h"
uint32_t EHSVtoHEX( uint8_t hue, uint8_t sat, uint8_t val );
int lastx = 200;
int lasty = 1000;
int main()
{
#define FSPS 22100 // Careful, this matters! It is selected to avoid periodic peaks encountered with DC offsets.
#define OCTAVES 6
#define BPERO 24
#define BASE_FREQ 22.5
#define QUADRATURE_STEP_DENOMINATOR 16384
// Careful: It makes a lot of sense to explore these relationships.
#define SAMPLE_Q 4
#define MAG_IIR 0
#define RUNNING_IIR 31
#define COMPLEX_IIR 2
#define TEST_SAMPLES 256
int16_t samples[TEST_SAMPLES];
int i;
CNFGSetup( "Example App", 1024, 768 );
// Precomputed Tables
int8_t whichoctave[2<<OCTAVES];
for( i = 0; i < (2<<OCTAVES); i++ )
{
int j;
for( j = 0; j < OCTAVES; j++ )
{
if( i & (1<<j) ) break;
}
if( j == OCTAVES )
whichoctave[i] = -1;
else
whichoctave[i] = OCTAVES - j - 1;
}
// Make a running counter to count up by this amount every cycle.
// If the new number > 2048, then perform a quadrature step.
int32_t flipdistance[BPERO];
for( i = 0; i < BPERO; i++ )
{
double freq = pow( 2, (double)i / (double)BPERO ) * (BASE_FREQ/2.0);
double pfreq = pow( 2, OCTAVES ) * freq;
double spacing = (FSPS / 2) / pfreq / 4;
flipdistance[i] = QUADRATURE_STEP_DENOMINATOR * spacing;
// Spacing = "quadrature every X samples"
//printf( "%f %d\n", spacing, flipdistance[i] );
}
// This is for timing. Not accumulated data.
int32_t quadrature_timing_last[BPERO*OCTAVES] = { 0 };
uint8_t quadrature_state[BPERO*OCTAVES] = { 0 };
uint32_t last_accumulated_value[BPERO*OCTAVES*2] = { 0 };
uint32_t octave_timing[OCTAVES] = { 0 };
int32_t real_imaginary_running[BPERO*OCTAVES*2] = { 0 };
uint32_t sample_accumulator = 0;
int32_t qcount[BPERO*OCTAVES] = { 0 };
int32_t magsum[BPERO*OCTAVES] = { 0 };
int frameno = 0;
double dLT = OGGetAbsoluteTime();
int samplenoIn = 0;
int sampleno = 0;
double ToneOmega = 0;
int ops;
while( CNFGHandleInput() )
{
CNFGClearFrame();
frameno++;
float freq =
//pow( 2, (frameno%600)/100.0 ) * 25;
pow( 2, (lastx)/100.0 ) * lastx;
//101;
for( i = 0; i < TEST_SAMPLES; i++ )
{
samples[i] = lasty/5 + sin( ToneOmega ) * 127;// + (rand()%128)-64;
ToneOmega += 1 / (double)FSPS * (double)freq * 3.14159 * 2.0;
}
char cts[1024];
sprintf( cts, "%f %d %f", freq, sampleno, ops/(double)sampleno );
CNFGColor( 0xffffffff );
CNFGPenX = 2;
CNFGPenY = 2;
CNFGDrawText( cts, 2 );
while( OGGetAbsoluteTime() < dLT + TEST_SAMPLES / (double)FSPS );
dLT += TEST_SAMPLES / (double)FSPS;
if( 0 )
{
memset( real_imaginary_running, 0, sizeof( real_imaginary_running ) );
memset( last_accumulated_value, 0, sizeof( last_accumulated_value ) );
memset( quadrature_timing_last, 0, sizeof( quadrature_timing_last ) );
memset( quadrature_state, 0, sizeof( quadrature_state ) );
memset( octave_timing, 0, sizeof( octave_timing ) );
sample_accumulator = 0;
}
for( i = 0; i < TEST_SAMPLES; i++ )
{
sample_accumulator += samples[i];
int octave = whichoctave[(sampleno)&((2<<OCTAVES)-1)];
sampleno++;
if( octave < 0 )
{
// A "free cycle" this happens every 1/(2^octaves)
continue;
}
#define WATCHBIN 1
int b;
int binno = octave * BPERO;
int ocative_time = octave_timing[octave] += QUADRATURE_STEP_DENOMINATOR;
for( b = 0; b < BPERO; b++, binno++ )
{
ops+=5;
if( ocative_time - quadrature_timing_last[binno] > 0 )
{
ops+=20;
quadrature_timing_last[binno] += flipdistance[b];
// This code will get appropriately executed every quadrature update.
int qstate = quadrature_state[binno] = ( quadrature_state[binno] + 1 ) % 4;
int last_q_bin = (binno * 2) + ( qstate & 1 );
int delta = sample_accumulator - last_accumulated_value[last_q_bin];
last_accumulated_value[last_q_bin] = sample_accumulator;
// Qstate =
// (0) = +Cos, (1) = +Sin, (2) = -Cos, (3) = -Sin
if( qstate & 2 ) delta *= -1;
// Update real and imaginary components with delta.
int running = real_imaginary_running[last_q_bin];
running = running - (running>>RUNNING_IIR) + delta;
real_imaginary_running[last_q_bin] = running;
int q = ++qcount[binno];
if( q == SAMPLE_Q ) // Effective Q factor.
{
ops+=20;
qcount[binno] = 0;
int newmagR = real_imaginary_running[(binno * 2)];
int newmagI = real_imaginary_running[(binno * 2)+1];
real_imaginary_running[(binno * 2)] = newmagR - (newmagR>>COMPLEX_IIR);
real_imaginary_running[(binno * 2)+1] = newmagI - (newmagI>>COMPLEX_IIR);
// Super-cheap, non-multiply, approximate complex vector magnitude calculation.
newmagR = (newmagR<0)?-newmagR:newmagR;
newmagI = (newmagI<0)?-newmagI:newmagI;
int newmag =
//sqrt(newmagR*newmagR + newmagI*newmagI );
newmagR > newmagI ? newmagR + (newmagI>>1) : newmagI + (newmagR>>1);
int lastmag = magsum[binno];
magsum[binno] = lastmag - (lastmag>>MAG_IIR) + newmag;
}
}
}
}
int lx, ly;
for( i = 0; i < BPERO*OCTAVES; i++ )
{
CNFGColor( (EHSVtoHEX( (i * 256 / BPERO)&0xff, 255, 255 ) << 8) | 0xff );
float real = real_imaginary_running[i*2+0];
float imag = real_imaginary_running[i*2+1];
float mag = sqrt( real * real + imag * imag );
mag = (float)magsum[i] * pow( 2, i / (double)BPERO );
int y = 768 - ((int)(mag / FSPS * 10) >> MAG_IIR);
if( i ) CNFGTackSegment( i*4, y, lx*4, ly );
lx = i; ly= y;
//printf( "%d %d\n", real_imaginary_running[i*2+0], real_imaginary_running[i*2+1] );
}
CNFGSwapBuffers();
}
}
void HandleKey( int keycode, int bDown ) { }
void HandleButton( int x, int y, int button, int bDown ) { }
void HandleMotion( int x, int y, int mask ) { lastx = x; lasty = y; }
void HandleDestroy() { }
uint32_t EHSVtoHEX( uint8_t hue, uint8_t sat, uint8_t val )
{
#define SIXTH1 43
#define SIXTH2 85
#define SIXTH3 128
#define SIXTH4 171
#define SIXTH5 213
uint16_t or = 0, og = 0, ob = 0;
hue -= SIXTH1; //Off by 60 degrees.
//TODO: There are colors that overlap here, consider
//tweaking this to make the best use of the colorspace.
if( hue < SIXTH1 ) //Ok: Yellow->Red.
{
or = 255;
og = 255 - ((uint16_t)hue * 255) / (SIXTH1);
}
else if( hue < SIXTH2 ) //Ok: Red->Purple
{
or = 255;
ob = (uint16_t)hue*255 / SIXTH1 - 255;
}
else if( hue < SIXTH3 ) //Ok: Purple->Blue
{
ob = 255;
or = ((SIXTH3-hue) * 255) / (SIXTH1);
}
else if( hue < SIXTH4 ) //Ok: Blue->Cyan
{
ob = 255;
og = (hue - SIXTH3)*255 / SIXTH1;
}
else if( hue < SIXTH5 ) //Ok: Cyan->Green.
{
og = 255;
ob = ((SIXTH5-hue)*255) / SIXTH1;
}
else //Green->Yellow
{
og = 255;
or = (hue - SIXTH5) * 255 / SIXTH1;
}
uint16_t rv = val;
if( rv > 128 ) rv++;
uint16_t rs = sat;
if( rs > 128 ) rs++;
//or, og, ob range from 0...255 now.
//Need to apply saturation and value.
or = (or * val)>>8;
og = (og * val)>>8;
ob = (ob * val)>>8;
//OR..OB == 0..65025
or = or * rs + 255 * (256-rs);
og = og * rs + 255 * (256-rs);
ob = ob * rs + 255 * (256-rs);
//printf( "__%d %d %d =-> %d\n", or, og, ob, rs );
or >>= 8;
og >>= 8;
ob >>= 8;
return or | (og<<8) | ((uint32_t)ob<<16);
}

View file

@ -0,0 +1,367 @@
/*
An experiment in very, very low-spec ColorChord. This technique foregoes
multiplies.
Approach 2:
Similar approach to Approach 1, in that this uses square waves and quarter
wavelength segments to quadrature encode, but instead of using an octave
at a time, it instead creates a heap to work through every sample.
That way, the error induced by sample stutter is minimized and the square
waves are as accurate as possible.
WARNING: With this approach, operations can 'bunch up' so that you may
need to clear many, many ops in a single cycle, so it is not at all
appropirate for being run in an interrupt.
Another benefit: If sample rate is large, no time is spent working on
samples that don't need work. This is better for a sparse set of ops.
TODO: Can we do this approach, but with a fixed table to instruct when to
perform every bin?
GENERAL OBSERVATION FOR ALL VERSIONS: (applicableto all) If we integrate
only bumps for sin/cos, it seems to have different noise properties.
May be beneficial!
*/
#include <stdio.h>
#include <math.h>
#define CNFG_IMPLEMENTATION
#define CNFGOGL
#include "rawdraw_sf.h"
#include "os_generic.h"
uint32_t EHSVtoHEX( uint8_t hue, uint8_t sat, uint8_t val );
int lastx = 200;
int lasty = 1000;
#define FSPS 12000
#define OCTAVES 6
#define BPERO 24
#define BASE_FREQ 22.5
#define QUADRATURE_STEP_DENOMINATOR 16384
// Careful: It makes a lot of sense to explore these relationships.
#define SAMPLE_Q 4
#define MAG_IIR 0
#define COMPLEX_IIR 2
#define RUNNING_IIR 31
#define TEST_SAMPLES 256
int32_t next_heap_events[OCTAVES*BPERO*2] = { 0 };
int sineshape;
// This will sort the head node back down into the heap, so the heap will
// remain a min-heap. This is done in log(n) time. But, with our data, it
// experimentally only needs to run for 6.47 iterations per call on average
// assuming 24 BPERO and 6 OCTAVES.
int heapsteps = 0;
int reheaps = 0;
void PercolateHeap( int32_t current_time )
{
reheaps++;
int this_index = 0;
int this_val = next_heap_events[0];
do
{
heapsteps++;
int left = (this_index * 2 + 1);
int right = (this_index * 2 + 2);
// At end. WARNING: This heap algorithm is only useful if it's an even number of things.
if( right >= OCTAVES*BPERO ) return;
int leftval = next_heap_events[left*2];
int rightval = next_heap_events[right*2];
int diffleft = leftval - this_val;
int diffright = rightval - this_val;
//printf( "RESORT ID %d / INDEX %d / [%d %d] %d %d %d %d\n", next_heap_events[this_index*2+1], this_index, diffleft, diffright, leftval, rightval, left, right );
if( diffleft > 0 && diffright > 0 )
{
// The heap is sorted. We're done.
return;
}
// Otherwise we have to pick an edge to sort on.
if( diffleft <= diffright )
{
//printf( "LEFT %d / %d\n", left, this_val );
int swapevent = next_heap_events[left*2+1];
next_heap_events[left*2+1] = next_heap_events[this_index*2+1];
next_heap_events[this_index*2+1] = swapevent;
next_heap_events[this_index*2+0] = leftval;
next_heap_events[left*2+0] = this_val;
this_index = left;
}
else
{
//printf( "RIGHT %d\n", right );
int swapevent = next_heap_events[right*2+1];
next_heap_events[right*2+1] = next_heap_events[this_index*2+1];
next_heap_events[this_index*2+1] = swapevent;
next_heap_events[this_index*2+0] = rightval;
next_heap_events[right*2+0] = this_val;
this_index = right;
}
} while( 1 );
}
int main()
{
int16_t samples[TEST_SAMPLES];
int i;
CNFGSetup( "Example App", 1024, 768 );
// Make a running counter to count up by this amount every cycle.
// If the new number > 2048, then perform a quadrature step.
int32_t flipdistance[BPERO*OCTAVES];
for( i = 0; i < BPERO*OCTAVES; i++ )
{
double freq = pow( 2, (double)i / (double)BPERO ) * (BASE_FREQ/2.0);
double pfreq = freq;
double spacing = (FSPS / 2) / pfreq / 4;
flipdistance[i] = QUADRATURE_STEP_DENOMINATOR * spacing;
// Spacing = "quadrature every X samples"
next_heap_events[i*2+1] = i;
}
// This is for timing. Not accumulated data.
uint8_t quadrature_state[BPERO*OCTAVES] = { 0 };
uint32_t last_accumulated_value[BPERO*OCTAVES*2] = { 0 };
int32_t real_imaginary_running[BPERO*OCTAVES*2] = { 0 };
uint32_t sample_accumulator = 0;
int32_t time_accumulator = 0;
int32_t qcount[BPERO*OCTAVES] = { 0 };
int32_t magsum[BPERO*OCTAVES] = { 0 };
int frameno = 0;
double dLT = OGGetAbsoluteTime();
double ToneOmega = 0;
while( CNFGHandleInput() )
{
CNFGClearFrame();
frameno++;
float freq =
//pow( 2, (frameno%600)/100.0 ) * 25;
pow( 2, (lastx)/100.0 ) * lastx;
//101;
for( i = 0; i < TEST_SAMPLES; i++ )
{
samples[i] = lasty/5 + sin( ToneOmega ) * 127;// + (rand()%128)-64;
ToneOmega += 1 / (double)FSPS * (double)freq * 3.14159 * 2.0;
}
char cts[1024];
sprintf( cts, "%f %d %f %f SINESHAPE: %d", freq, time_accumulator, (double)heapsteps / (double)reheaps, (double)reheaps/(time_accumulator/QUADRATURE_STEP_DENOMINATOR), sineshape );
CNFGColor( 0xffffffff );
CNFGPenX = 2;
CNFGPenY = 2;
CNFGDrawText( cts, 2 );
while( OGGetAbsoluteTime() < dLT + TEST_SAMPLES / (double)FSPS );
dLT += TEST_SAMPLES / (double)FSPS;
#define WATCHBIN -1
for( i = 0; i < TEST_SAMPLES; i++ )
{
sample_accumulator += samples[i];
time_accumulator += QUADRATURE_STEP_DENOMINATOR;
while( (time_accumulator - next_heap_events[0]) > 0 )
{
// Event has occurred.
int binno = next_heap_events[1];
//printf( "%d %d\n", binno, next_heap_events[0] );
next_heap_events[0] += flipdistance[binno];
PercolateHeap( time_accumulator );
//int j;
//for( j = 0; j < OCTAVES*BPERO; j++ ) printf( "[%d %d]", next_heap_events[j*2+0], next_heap_events[j*2+1] );
//printf( "\n" );
int qstate = quadrature_state[binno] = ( quadrature_state[binno] + 1 ) % 4;
int last_q_bin = (binno * 2) + ( qstate & 1 );
int delta;
if( !sineshape )
{
delta = sample_accumulator - last_accumulated_value[last_q_bin];
last_accumulated_value[last_q_bin] = sample_accumulator;
}
else
{
// TESTING: Sine Shape - this only integrates bumps for sin/cos
// instead of full triangle waves.
// Observation: For higher frequency bins, this seems to help
// a lot.
// side-benefit, this takes less RAM.
// BUT BUT! It messes with lower frequencies, making them uglier.
delta = sample_accumulator - last_accumulated_value[binno];
last_accumulated_value[binno] = sample_accumulator;
delta *= 1.4; // Just to normalize the results of the test (not for production)
}
if( binno == WATCHBIN )
printf( "Delta: %d\n", delta );
// Qstate =
// (0) = +Cos, (1) = +Sin, (2) = -Cos, (3) = -Sin
if( qstate & 2 ) delta *= -1;
// Update real and imaginary components with delta.
int running = real_imaginary_running[last_q_bin];
running = running - (running>>RUNNING_IIR) + delta;
real_imaginary_running[last_q_bin] = running;
int q = ++qcount[binno];
if( q == SAMPLE_Q ) // Effective Q factor.
{
qcount[binno] = 0;
int newmagR = real_imaginary_running[(binno * 2)];
int newmagI = real_imaginary_running[(binno * 2)+1];
real_imaginary_running[(binno * 2)] = newmagR - (newmagR>>COMPLEX_IIR);
real_imaginary_running[(binno * 2)+1] = newmagI - (newmagI>>COMPLEX_IIR);
// Super-cheap, non-multiply, approximate complex vector magnitude calculation.
newmagR = (newmagR<0)?-newmagR:newmagR;
newmagI = (newmagI<0)?-newmagI:newmagI;
int newmag =
//sqrt(newmagR*newmagR + newmagI*newmagI );
newmagR > newmagI ? newmagR + (newmagI>>1) : newmagI + (newmagR>>1);
int lastmag = magsum[binno];
magsum[binno] = lastmag - (lastmag>>MAG_IIR) + newmag;
}
}
}
int lx, ly;
for( i = 0; i < BPERO*OCTAVES; i++ )
{
CNFGColor( (EHSVtoHEX( (i * 256 / BPERO)&0xff, 255, 255 ) << 8) | 0xff );
float real = real_imaginary_running[i*2+0];
float imag = real_imaginary_running[i*2+1];
float mag = sqrt( real * real + imag * imag );
mag = (float)magsum[i] * pow( 2, i / (double)BPERO );
int y = 768 - ((int)(mag / FSPS * 10) >> MAG_IIR);
if( i ) CNFGTackSegment( i*4, y, lx*4, ly );
lx = i; ly= y;
//printf( "%d %d\n", real_imaginary_running[i*2+0], real_imaginary_running[i*2+1] );
}
CNFGSwapBuffers();
}
}
void HandleKey( int keycode, int bDown ) { if( keycode == 'a' && bDown ) sineshape = !sineshape; }
void HandleButton( int x, int y, int button, int bDown ) { }
void HandleMotion( int x, int y, int mask ) { lastx = x; lasty = y; }
void HandleDestroy() { }
uint32_t EHSVtoHEX( uint8_t hue, uint8_t sat, uint8_t val )
{
#define SIXTH1 43
#define SIXTH2 85
#define SIXTH3 128
#define SIXTH4 171
#define SIXTH5 213
uint16_t or = 0, og = 0, ob = 0;
hue -= SIXTH1; //Off by 60 degrees.
//TODO: There are colors that overlap here, consider
//tweaking this to make the best use of the colorspace.
if( hue < SIXTH1 ) //Ok: Yellow->Red.
{
or = 255;
og = 255 - ((uint16_t)hue * 255) / (SIXTH1);
}
else if( hue < SIXTH2 ) //Ok: Red->Purple
{
or = 255;
ob = (uint16_t)hue*255 / SIXTH1 - 255;
}
else if( hue < SIXTH3 ) //Ok: Purple->Blue
{
ob = 255;
or = ((SIXTH3-hue) * 255) / (SIXTH1);
}
else if( hue < SIXTH4 ) //Ok: Blue->Cyan
{
ob = 255;
og = (hue - SIXTH3)*255 / SIXTH1;
}
else if( hue < SIXTH5 ) //Ok: Cyan->Green.
{
og = 255;
ob = ((SIXTH5-hue)*255) / SIXTH1;
}
else //Green->Yellow
{
og = 255;
or = (hue - SIXTH5) * 255 / SIXTH1;
}
uint16_t rv = val;
if( rv > 128 ) rv++;
uint16_t rs = sat;
if( rs > 128 ) rs++;
//or, og, ob range from 0...255 now.
//Need to apply saturation and value.
or = (or * val)>>8;
og = (og * val)>>8;
ob = (ob * val)>>8;
//OR..OB == 0..65025
or = or * rs + 255 * (256-rs);
og = og * rs + 255 * (256-rs);
ob = ob * rs + 255 * (256-rs);
//printf( "__%d %d %d =-> %d\n", or, og, ob, rs );
or >>= 8;
og >>= 8;
ob >>= 8;
return or | (og<<8) | ((uint32_t)ob<<16);
}

View file

@ -0,0 +1,321 @@
/*
An experiment in very, very low-spec ColorChord. This technique foregoes
multiplies.
Approach 3: Based on Approach 2, but using a work selection table.
This won't work for the ch32v003, since the minimum practical table for 6
octaves at 24BPERO with 12kSPS and bottom freq of 22.5 is about 80kB.
*/
#include <stdio.h>
#include <math.h>
#define CNFG_IMPLEMENTATION
#define CNFGOGL
#include "rawdraw_sf.h"
#include "os_generic.h"
uint32_t EHSVtoHEX( uint8_t hue, uint8_t sat, uint8_t val );
int lastx = 200;
int lasty = 1000;
#define FSPS 16000
#define OCTAVES 6
#define BPERO 24
#define BASE_FREQ 22.5
#define QUADRATURE_STEP_DENOMINATOR 16384
// Careful: It makes a lot of sense to explore these relationships.
#define SAMPLE_Q 4
#define MAG_IIR 0
#define COMPLEX_IIR 2
#define RUNNING_IIR 31
#define TEST_SAMPLES 256
int sineshape;
// This will sort the head node back down into the heap, so the heap will
// remain a min-heap. This is done in log(n) time. But, with our data, it
// experimentally only needs to run for 6.47 iterations per call on average
// assuming 24 BPERO and 6 OCTAVES.
int main()
{
int16_t samples[TEST_SAMPLES];
int i;
CNFGSetup( "Example App", 1024, 768 );
// Not size of table (that's usually larger) but # of samples
// to record the work instructions for.
#define WORKLOOP 6144
// Make a running counter to count up by this amount every cycle.
// If the new number > 2048, then perform a quadrature step.
double spacings[BPERO*OCTAVES];
double runningspace[BPERO*OCTAVES];
for( i = 0; i < BPERO*OCTAVES; i++ )
{
double freq = pow( 2, (double)i / (double)BPERO ) * (BASE_FREQ/2.0);
double pfreq = freq;
double spacing = (FSPS / 2) / pfreq / 4;
// make spacings line up to a denominator of workloop, this makes it
// so you don't get a werid jump at the end of the work loop.
double wdt = WORKLOOP / spacing;
printf( "%f %f %f\n", wdt, spacing, WORKLOOP / (double)((int)wdt+0.5) );
wdt = (int)(wdt+0.5);
spacing = WORKLOOP / wdt;
spacings[i] = spacing;
}
uint8_t worktable[WORKLOOP*BPERO*OCTAVES];
int worktablelen = 0;
for( i = 0; i < WORKLOOP; i++ )
{
int j;
for( j = 0; j < BPERO*OCTAVES; j++ )
{
if( i >= runningspace[j] )
{
runningspace[j] += spacings[j];
worktable[worktablelen++] = j;
}
}
worktable[worktablelen++] = 255;
}
// This is for timing. Not accumulated data.
uint8_t quadrature_state[BPERO*OCTAVES] = { 0 };
uint32_t last_accumulated_value[BPERO*OCTAVES*2] = { 0 };
int32_t real_imaginary_running[BPERO*OCTAVES*2] = { 0 };
uint32_t sample_accumulator = 0;
int32_t time_accumulator = 0;
int32_t qcount[BPERO*OCTAVES] = { 0 };
int32_t magsum[BPERO*OCTAVES] = { 0 };
int frameno = 0;
double dLT = OGGetAbsoluteTime();
double ToneOmega = 0;
int worktableplace = 0;
while( CNFGHandleInput() )
{
CNFGClearFrame();
frameno++;
float freq =
//pow( 2, (frameno%600)/100.0 ) * 25;
pow( 2, (lastx)/100.0 ) * lastx;
//101;
for( i = 0; i < TEST_SAMPLES; i++ )
{
samples[i] = lasty/5 + sin( ToneOmega ) * 127;// + (rand()%128)-64;
ToneOmega += 1 / (double)FSPS * (double)freq * 3.14159 * 2.0;
}
char cts[1024];
sprintf( cts, "%f %d SINESHAPE: %d WT %d", freq, time_accumulator, sineshape , worktablelen);
CNFGColor( 0xffffffff );
CNFGPenX = 2;
CNFGPenY = 2;
CNFGDrawText( cts, 2 );
while( OGGetAbsoluteTime() < dLT + TEST_SAMPLES / (double)FSPS );
dLT += TEST_SAMPLES / (double)FSPS;
#define WATCHBIN -1
for( i = 0; i < TEST_SAMPLES; i++ )
{
sample_accumulator += samples[i];
time_accumulator += QUADRATURE_STEP_DENOMINATOR;
while( 1 )
{
int wtp = worktable[worktableplace];
worktableplace = worktableplace + 1;
if( worktableplace >= worktablelen ) worktableplace = 0;
if( wtp == 255 ) break;
// Event has occurred.
int binno = wtp;
//int j;
//for( j = 0; j < OCTAVES*BPERO; j++ ) printf( "[%d %d]", next_heap_events[j*2+0], next_heap_events[j*2+1] );
//printf( "\n" );
int qstate = quadrature_state[binno] = ( quadrature_state[binno] + 1 ) % 4;
int last_q_bin = (binno * 2) + ( qstate & 1 );
int delta;
if( !sineshape )
{
delta = sample_accumulator - last_accumulated_value[last_q_bin];
last_accumulated_value[last_q_bin] = sample_accumulator;
}
else
{
// TESTING: Sine Shape - this only integrates bumps for sin/cos
// instead of full triangle waves.
// Observation: For higher frequency bins, this seems to help
// a lot.
// side-benefit, this takes less RAM.
// BUT BUT! It messes with lower frequencies, making them uglier.
delta = sample_accumulator - last_accumulated_value[binno];
last_accumulated_value[binno] = sample_accumulator;
delta *= 1.4; // Just to normalize the results of the test (not for production)
}
if( binno == WATCHBIN )
printf( "Delta: %d\n", delta );
// Qstate =
// (0) = +Cos, (1) = +Sin, (2) = -Cos, (3) = -Sin
if( qstate & 2 ) delta *= -1;
// Update real and imaginary components with delta.
int running = real_imaginary_running[last_q_bin];
running = running - (running>>RUNNING_IIR) + delta;
real_imaginary_running[last_q_bin] = running;
int q = ++qcount[binno];
if( q == SAMPLE_Q ) // Effective Q factor.
{
qcount[binno] = 0;
int newmagR = real_imaginary_running[(binno * 2)];
int newmagI = real_imaginary_running[(binno * 2)+1];
real_imaginary_running[(binno * 2)] = newmagR - (newmagR>>COMPLEX_IIR);
real_imaginary_running[(binno * 2)+1] = newmagI - (newmagI>>COMPLEX_IIR);
// Super-cheap, non-multiply, approximate complex vector magnitude calculation.
newmagR = (newmagR<0)?-newmagR:newmagR;
newmagI = (newmagI<0)?-newmagI:newmagI;
int newmag =
//sqrt(newmagR*newmagR + newmagI*newmagI );
newmagR > newmagI ? newmagR + (newmagI>>1) : newmagI + (newmagR>>1);
int lastmag = magsum[binno];
magsum[binno] = lastmag - (lastmag>>MAG_IIR) + newmag;
}
}
}
int lx, ly;
for( i = 0; i < BPERO*OCTAVES; i++ )
{
CNFGColor( (EHSVtoHEX( (i * 256 / BPERO)&0xff, 255, 255 ) << 8) | 0xff );
float real = real_imaginary_running[i*2+0];
float imag = real_imaginary_running[i*2+1];
float mag = sqrt( real * real + imag * imag );
mag = (float)magsum[i] * pow( 2, i / (double)BPERO );
int y = 768 - ((int)(mag / FSPS * 10) >> MAG_IIR);
if( i ) CNFGTackSegment( i*4, y, lx*4, ly );
lx = i; ly= y;
//printf( "%d %d\n", real_imaginary_running[i*2+0], real_imaginary_running[i*2+1] );
}
CNFGSwapBuffers();
}
}
void HandleKey( int keycode, int bDown ) { if( keycode == 'a' && bDown ) sineshape = !sineshape; }
void HandleButton( int x, int y, int button, int bDown ) { }
void HandleMotion( int x, int y, int mask ) { lastx = x; lasty = y; }
void HandleDestroy() { }
uint32_t EHSVtoHEX( uint8_t hue, uint8_t sat, uint8_t val )
{
#define SIXTH1 43
#define SIXTH2 85
#define SIXTH3 128
#define SIXTH4 171
#define SIXTH5 213
uint16_t or = 0, og = 0, ob = 0;
hue -= SIXTH1; //Off by 60 degrees.
//TODO: There are colors that overlap here, consider
//tweaking this to make the best use of the colorspace.
if( hue < SIXTH1 ) //Ok: Yellow->Red.
{
or = 255;
og = 255 - ((uint16_t)hue * 255) / (SIXTH1);
}
else if( hue < SIXTH2 ) //Ok: Red->Purple
{
or = 255;
ob = (uint16_t)hue*255 / SIXTH1 - 255;
}
else if( hue < SIXTH3 ) //Ok: Purple->Blue
{
ob = 255;
or = ((SIXTH3-hue) * 255) / (SIXTH1);
}
else if( hue < SIXTH4 ) //Ok: Blue->Cyan
{
ob = 255;
og = (hue - SIXTH3)*255 / SIXTH1;
}
else if( hue < SIXTH5 ) //Ok: Cyan->Green.
{
og = 255;
ob = ((SIXTH5-hue)*255) / SIXTH1;
}
else //Green->Yellow
{
og = 255;
or = (hue - SIXTH5) * 255 / SIXTH1;
}
uint16_t rv = val;
if( rv > 128 ) rv++;
uint16_t rs = sat;
if( rs > 128 ) rs++;
//or, og, ob range from 0...255 now.
//Need to apply saturation and value.
or = (or * val)>>8;
og = (og * val)>>8;
ob = (ob * val)>>8;
//OR..OB == 0..65025
or = or * rs + 255 * (256-rs);
og = og * rs + 255 * (256-rs);
ob = ob * rs + 255 * (256-rs);
//printf( "__%d %d %d =-> %d\n", or, og, ob, rs );
or >>= 8;
og >>= 8;
ob >>= 8;
return or | (og<<8) | ((uint32_t)ob<<16);
}

View file

@ -8,7 +8,7 @@
#include <math.h>
#include <string.h>
#include "color.h"
#include "DrawFunctions.h"
#include "CNFG.h"
//Uses: note_amplitudes2[note] for how many lights to use.
//Uses: note_amplitudes_out[note] for how bright it should be.
@ -54,7 +54,7 @@ static void DPOUpdate(void * id, struct NoteFinder*nf)
{
index = x+y*d->xn;
}
CNFGColor( OutLEDs[index*3+0] | (OutLEDs[index*3+1] <<8)|(OutLEDs[index*3+2] <<16) );
CNFGColor( (OutLEDs[index*3+0] <<24) | (OutLEDs[index*3+1] <<16) | (OutLEDs[index*3+2] <<8) | 0xff );
float dx = (x) * cw;
float dy = (y) * ch;
@ -63,7 +63,7 @@ static void DPOUpdate(void * id, struct NoteFinder*nf)
else
CNFGTackRectangle( dx, dy, dx+cw+.5, dy+ch+.5 );
}
CNFGColor( 0xffffff );
CNFGColor( 0xffffffff );
}
static void DPOParams(void * id )

View file

@ -1,5 +1,10 @@
//Copyright 2015 <>< Charles Lohr under the ColorChord License.
// only compile file if we are one a unix system
#if defined( WINDOWS ) || defined( USE_WINDOWS ) || defined( WIN32 ) || defined( WIN64 ) || \
defined( _WIN32 ) || defined( _WIN64 )
#warning Windows does not support the DisplayDMX module
#else
#include "outdrivers.h"
#include "notefinder.h"
#include <stdio.h>
@ -123,5 +128,4 @@ static struct DriverInstances * DisplayDMX()
}
REGISTER_OUT_DRIVER(DisplayDMX);
#endif

View file

@ -1,5 +1,10 @@
//Copyright 2015 <>< Charles Lohr under the ColorChord License.
// only compile file if we are one a unix system
#if defined( WINDOWS ) || defined( USE_WINDOWS ) || defined( WIN32 ) || defined( WIN64 ) || \
defined( _WIN32 ) || defined( _WIN64 )
#warning Windows does not support the DisplayFileWrite module
#else
#include "outdrivers.h"
#include "notefinder.h"
#include <stdio.h>
@ -8,7 +13,7 @@
#include <math.h>
#include <string.h>
#include "color.h"
#include "DrawFunctions.h"
#include "CNFG.h"
#include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#include <fcntl.h> /* For O_* constants */
@ -116,5 +121,4 @@ static struct DriverInstances * DisplayFileWrite(const char * parameters)
}
REGISTER_OUT_DRIVER(DisplayFileWrite);
#endif

View file

@ -117,7 +117,7 @@ static void * LEDOutThread( void * v )
}
}
led->readyFlag = 0;
printf( "." ); fflush( stdout );
//printf( "." ); fflush( stdout );
}
OGUSleep(100);
}

View file

@ -8,7 +8,6 @@
#include <math.h>
#include <string.h>
#include "color.h"
#include "DrawFunctions.h"
#if defined(WIN32) || defined(WINDOWS)
#include <windows.h>
@ -17,6 +16,7 @@
#endif
#define MSG_NOSIGNAL 0
#else
#include <unistd.h>
#define closesocket( x ) close( x )
#include <sys/socket.h>
#include <netinet/in.h>
@ -30,7 +30,10 @@ struct DPODriver
{
int leds;
int skipfirst;
int wled_realtime;
int wled_timeout;
int fliprg;
int flipgb;
int firstval;
int port;
int is_rgby;
@ -99,6 +102,17 @@ static void DPOUpdate(void * id, struct NoteFinder*nf)
d->firstval = 0;
i = 0;
if(d->wled_realtime)
{
//Packet byte 0 is 2 (DRGB mode) or 3 (DRGBW mode) if is_rgby
buffer[i] = 2 + d->is_rgby;
lbuff[i++] = 2 + d->is_rgby;
//Packet byte 1 is the time in seconds the controller will wait to take control after it stops receiving data
buffer[i] = d->wled_timeout;
lbuff[i++] = d->wled_timeout;
}
while( i < d->skipfirst )
{
lbuff[i] = d->firstval;
@ -148,6 +162,15 @@ static void DPOUpdate(void * id, struct NoteFinder*nf)
lbuff[i++] = OutLEDs[j*3+2]; //BLUE
}
}
else if( d->flipgb )
{
for( j = 0; j < d->leds; j++ )
{
lbuff[i++] = OutLEDs[j*3+0]; //RED
lbuff[i++] = OutLEDs[j*3+1]; //GREEN
lbuff[i++] = OutLEDs[j*3+2]; //BLUE
}
}
else
{
for( j = 0; j < d->leds; j++ )
@ -188,7 +211,7 @@ static void DPOUpdate(void * id, struct NoteFinder*nf)
}
}
int r = sendto( d->socket, buffer, i, MSG_NOSIGNAL,(const struct sockaddr *) &d->servaddr, sizeof( d->servaddr ) );
int r = sendto( d->socket, (const char*) buffer, i, MSG_NOSIGNAL,(const struct sockaddr *) &d->servaddr, sizeof( d->servaddr ) );
if( r < 0 )
{
fprintf( stderr, "Send fault.\n" );
@ -206,10 +229,13 @@ static void DPOParams(void * id )
d->leds = 10; RegisterValue( "leds", PAINT, &d->leds, sizeof( d->leds ) );
d->skipfirst = 1; RegisterValue( "skipfirst", PAINT, &d->skipfirst, sizeof( d->skipfirst ) );
d->wled_realtime = 0;RegisterValue( "wled_realtime", PAINT, &d->wled_realtime, sizeof( d->wled_realtime ) );
d->wled_timeout = 2;RegisterValue( "wled_timeout", PAINT, &d->wled_timeout, sizeof( d->wled_timeout ) );
d->port = 7777; RegisterValue( "port", PAINT, &d->port, sizeof( d->port ) );
d->firstval = 0; RegisterValue( "firstval", PAINT, &d->firstval, sizeof( d->firstval ) );
RegisterValue( "address", PABUFFER, d->address, sizeof( d->address ) );
d->fliprg = 0; RegisterValue( "fliprg", PAINT, &d->fliprg, sizeof( d->fliprg ) );
d->flipgb = 0; RegisterValue( "flipgb", PAINT, &d->flipgb, sizeof( d->flipgb ) );
d->is_rgby = 0; RegisterValue( "rgby", PAINT, &d->is_rgby, sizeof( d->is_rgby ) );
d->skittlequantity=0;RegisterValue( "skittlequantity", PAINT, &d->skittlequantity, sizeof( d->skittlequantity ) );
d->socket = -1;

View file

@ -11,7 +11,7 @@
#include <math.h>
#include <string.h>
#include "color.h"
#include "DrawFunctions.h"
#include "CNFG.h"
//Uses: note_amplitudes2[note] for how many lights to use.
//Uses: note_amplitudes_out[note] for how bright it should be.
@ -121,26 +121,6 @@ static void DPOUpdate(void * id, struct NoteFinder*nf)
}
}
/* float cw = ((float)screenx) / d->xn;
float ch = ((float)screeny) / d->yn;
for( i = 0; i < d->note_peaks; i++ )
{
struct LINote * l = &d->notes[i];
int j;
float sat = nf->note_amplitudes_out[i] * d->satamp;
if( sat > 1 ) sat = 1;
CNFGDialogColor = CCtoHEX( nf->note_positions[i] / nf->freqbins, 1.0, sat );
for( j = 0; j < l->nrleds; j++ )
{
int id = l->ledids[j];
float x = (id % d->xn) * cw;
float y = (id / d->xn) * ch;
CNFGDrawBox( x, y, x+cw, y+ch );
}
}*/
int led = 0;
for( i = 0; i < d->note_peaks; i++ )
{
@ -150,9 +130,9 @@ static void DPOUpdate(void * id, struct NoteFinder*nf)
if( sat > 1 ) sat = 1;
uint32_t color = CCtoHEX( nf->note_positions[i] / nf->freqbins, 1.0, sat );
OutLEDs[led*3+0] = color & 0xff;
OutLEDs[led*3+1] = ( color >> 8 ) & 0xff;
OutLEDs[led*3+2] = ( color >> 16 ) & 0xff;
OutLEDs[led*3+0] = ( color >> 24 ) & 0xff;
OutLEDs[led*3+1] = ( color >> 16 ) & 0xff;
OutLEDs[led*3+2] = ( color >> 8 ) & 0xff;
led++;
}

View file

@ -8,7 +8,7 @@
#include <math.h>
#include <string.h>
#include "color.h"
#include "DrawFunctions.h"
#include "CNFG.h"
//Uses: note_amplitudes2[note] for how many lights to use.
//Uses: note_amplitudes_out[note] for how bright it should be.
@ -31,7 +31,8 @@ static void DPOUpdate(void * id, struct NoteFinder*nf)
int i;
float cw = ((float)(screenx)) / 2.0;
float ch = ((float)(screeny)) / 2.0;
RDPoint pts[4];
RDPoint pts[6];
float sizeA = sqrtf( screenx * screenx + screeny * screeny ) * d->pie_min;
float sizeB = sqrtf( screenx * screenx + screeny * screeny ) * d->pie_max;
for( i = 0; i < d->leds; i++ )
@ -44,15 +45,21 @@ static void DPOUpdate(void * id, struct NoteFinder*nf)
pts[1].y = ch + sin(angA) * sizeB;
pts[2].x = cw + cos(angB) * sizeB;
pts[2].y = ch + sin(angB) * sizeB;
pts[3].x = cw + cos(angB) * sizeA;
pts[3].y = ch + sin(angB) * sizeA;
CNFGColor( OutLEDs[i*3+0] | (OutLEDs[i*3+1] <<8)|(OutLEDs[i*3+2] <<16) );
CNFGTackPoly( pts, 4 );
pts[3].x = cw + cos(angA) * sizeA;
pts[3].y = ch + sin(angA) * sizeA;
pts[4].x = cw + cos(angB) * sizeB;
pts[4].y = ch + sin(angB) * sizeB;
pts[5].x = cw + cos(angB) * sizeA;
pts[5].y = ch + sin(angB) * sizeA;
CNFGColor( ( OutLEDs[i*3+0] <<24) | (OutLEDs[i*3+1] <<16) | (OutLEDs[i*3+2] <<8) | 0xff );
CNFGTackPoly( pts, 3 );
CNFGTackPoly( pts+3, 3 );
}
CNFGColor( 0xffffff );
CNFGColor( 0xffffffff );
}
static void DPOParams(void * id )

View file

@ -0,0 +1,153 @@
//Copyright 2015 <>< Charles Lohr under the ColorChord License.
#include "outdrivers.h"
#include "notefinder.h"
#include <stdio.h>
#include "parameters.h"
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include "color.h"
#include "CNFG.h"
//Uses: note_amplitudes2[note] for how many lights to use.
//Uses: note_amplitudes_out[note] for how bright it should be.
extern short screenx, screeny;
struct DPRDriver
{
float centeroffset;
float radialscale;
float polewidth;
int radialmode;
};
static void DPRUpdate(void * id, struct NoteFinder*nf)
{
struct DPRDriver * d = (struct DPRDriver*)id;
if( d->radialmode == 0 )
{
int pole = 0;
int freqbins = nf->freqbins;
for( pole = 0; pole < freqbins; pole++ )
{
float angle = pole / (float)freqbins * 3.14159 * 2;
float cx = screenx/2;
float cy = screeny/2;
cx += cos( angle ) * d->centeroffset;
cy += sin( angle ) * d->centeroffset;
float extentx = cx + cos(angle) * d->radialscale * nf->folded_bins[pole];
float extenty = cy + sin(angle) * d->radialscale * nf->folded_bins[pole];
float orthox = sin(angle) * d->polewidth;
float orthoy = -cos(angle) * d->polewidth;
float p1x = cx + orthox;
float p1y = cy + orthoy;
float p2x = cx - orthox;
float p2y = cy - orthoy;
float p3x = extentx + orthox;
float p3y = extenty + orthoy;
float p4x = extentx - orthox;
float p4y = extenty - orthoy;
CNFGColor( CCtoHEX( (float)pole / (float)freqbins, 1.0, 1.0 ) );
RDPoint pts[6] = {
{ p1x, p1y },
{ p2x, p2y },
{ p3x, p3y },
{ p4x, p4y },
{ p3x, p3y },
{ p2x, p2y },
};
CNFGTackPoly( pts, 3 );
CNFGTackPoly( pts+3, 3 );
CNFGColor( 0x000000ff );
CNFGTackSegment( p1x, p1y, p2x, p2y);
CNFGTackSegment( p2x, p2y, p4x, p4y);
CNFGTackSegment( p3x, p3y, p1x, p1y);
CNFGTackSegment( p4x, p4y, p3x, p3y);
}
}
else if( d->radialmode == 1 )
{
int pole = 0;
int polen = 0;
int freqbins = nf->freqbins;
for( pole = 0; pole < freqbins; pole++ )
{
polen = (pole+1)%freqbins;
float angleT = pole / (float)freqbins * 3.14159 * 2;
float angleN = polen / (float)freqbins * 3.14159 * 2;
float cx = screenx/2;
float cy = screeny/2;
float p1x = cx + cos( angleT ) * d->centeroffset;
float p1y = cy + sin( angleT ) * d->centeroffset;
float p2x = cx + cos( angleN ) * d->centeroffset;
float p2y = cy + sin( angleN ) * d->centeroffset;
float binval = nf->folded_bins[pole];
float binvalN = nf->folded_bins[polen];
float p3x = cx + cos( angleT ) * (d->radialscale * binval + d->centeroffset);
float p3y = cy + sin( angleT ) * (d->radialscale * binval + d->centeroffset);
float p4x = cx + cos( angleN ) * (d->radialscale * binvalN + d->centeroffset);
float p4y = cy + sin( angleN ) * (d->radialscale * binvalN + d->centeroffset);
CNFGColor( CCtoHEX( (float)pole / (float)freqbins, 1.0, 1.0 ) );
RDPoint pts[6] = {
{ p1x, p1y },
{ p2x, p2y },
{ p3x, p3y },
{ p4x, p4y },
{ p3x, p3y },
{ p2x, p2y },
};
CNFGTackPoly( pts, 3 );
CNFGTackPoly( pts+3, 3 );
CNFGColor( 0x000000ff );
CNFGTackSegment( p1x, p1y, p2x, p2y);
CNFGTackSegment( p2x, p2y, p4x, p4y);
CNFGTackSegment( p3x, p3y, p1x, p1y);
CNFGTackSegment( p4x, p4y, p3x, p3y);
}
}
CNFGColor( 0xffffffff );
}
static void DPRParams(void * id )
{
struct DPRDriver * d = (struct DPRDriver*)id;
d->centeroffset = 160; RegisterValue( "centeroffset", PAFLOAT, &d->centeroffset, sizeof( d->centeroffset ) );
d->radialscale = 1000; RegisterValue( "radialscale", PAFLOAT, &d->radialscale, sizeof( d->radialscale ) );
d->polewidth = 20; RegisterValue( "polewidth", PAFLOAT, &d->polewidth, sizeof( d->polewidth ) );
d->radialmode = 0; RegisterValue( "radialmode", PAINT, &d->radialmode, sizeof( d->radialmode ) );
}
static struct DriverInstances * DisplayRadialPoles(const char * parameters)
{
struct DriverInstances * ret = malloc( sizeof( struct DriverInstances ) );
struct DPRDriver * d = ret->id = malloc( sizeof( struct DPRDriver ) );
memset( d, 0, sizeof( struct DPRDriver ) );
ret->Func = DPRUpdate;
ret->Params = DPRParams;
DPRParams( d );
return ret;
}
REGISTER_OUT_DRIVER(DisplayRadialPoles);

View file

@ -1,5 +1,10 @@
//Copyright 2015 <>< Charles Lohr under the ColorChord License.
// only compile file if we are one a unix system
#if defined( WINDOWS ) || defined( USE_WINDOWS ) || defined( WIN32 ) || defined( WIN64 ) || \
defined( _WIN32 ) || defined( _WIN64 )
#warning Windows does not support the DisplaySHM module
#else
#include "outdrivers.h"
#include "notefinder.h"
#include <stdio.h>
@ -8,7 +13,7 @@
#include <math.h>
#include <string.h>
#include "color.h"
#include "DrawFunctions.h"
#include "CNFG.h"
#include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#include <fcntl.h> /* For O_* constants */
@ -117,5 +122,4 @@ static struct DriverInstances * DisplaySHM(const char * parameters)
}
REGISTER_OUT_DRIVER(DisplaySHM);
#endif

View file

@ -1,5 +1,10 @@
//Copyright 2015 <>< Charles Lohr under the ColorChord License.
// only compile file if we are one a unix system
#if defined( WINDOWS ) || defined( USE_WINDOWS ) || defined( WIN32 ) || defined( WIN64 ) || \
defined( _WIN32 ) || defined( _WIN64 )
#warning Windows does not support the DisplayUSB2812 module
#else
#include "outdrivers.h"
#include "notefinder.h"
#include <stdio.h>
@ -166,5 +171,4 @@ static struct DriverInstances * DisplayUSB2812()
}
REGISTER_OUT_DRIVER(DisplayUSB2812);
#endif

View file

@ -0,0 +1,197 @@
//Copyright 2015 <>< Charles Lohr under the ColorChord License.
#include "outdrivers.h"
#include "notefinder.h"
#include <stdio.h>
#include <string.h>
#include "parameters.h"
#include <stdlib.h>
#include <libusb-1.0/libusb.h>
#include "color.h"
#include <math.h>
#include <unistd.h>
struct LEDOutDriver
{
struct libusb_device_handle *devh;
int did_init;
int zigzag;
int total_leds;
int array;
float outamp;
uint8_t * last_leds;
volatile int readyFlag;
volatile int transferring;
int xn;
int yn;
int rot90;
};
static void isocb(struct libusb_transfer *tf)
{
struct LEDOutDriver *led = (struct LEDOutDriver*)tf->user_data;
led->transferring = 0;
}
static void * LEDOutThread( void * v )
{
struct LEDOutDriver * led = (struct LEDOutDriver*)v;
while(1)
{
while(!led->readyFlag || !led->did_init || !led->devh) usleep(100);
int transfers = ((led->total_leds + 19) / 20);
int totallength = transfers * 64;
struct libusb_transfer *tf = libusb_alloc_transfer(transfers);
tf->dev_handle = led->devh;
tf->flags = LIBUSB_TRANSFER_FREE_TRANSFER;
tf->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS;
tf->num_iso_packets = transfers;
libusb_set_iso_packet_lengths(tf, 64);
tf->endpoint = 1;
tf->flags = 0;
tf->buffer = led->last_leds;
tf->callback = isocb;
tf->length = totallength;
tf->user_data = led;
if(libusb_submit_transfer(tf))
{
led->did_init = 0;
printf( "Fault sending LEDs.\n" );
}
led->transferring = 1;
while(led->transferring)
{
libusb_handle_events(NULL);
}
led->readyFlag = 0;
}
return 0;
}
static void LEDUpdate(void * id, struct NoteFinder*nf)
{
int i;
struct LEDOutDriver * led = (struct LEDOutDriver*)id;
if( !led->did_init )
{
led->did_init = 1;
if( libusb_init(NULL) < 0 )
{
fprintf( stderr, "Error: Could not initialize libUSB\n" );
// exit( -99 );
}
led->devh = libusb_open_device_with_vid_pid( NULL, 0x16c0, 0x05dc );
if( !led->devh )
{
fprintf( stderr, "Error: Cannot find device.\n" );
// exit( -98 );
}
}
while( led->readyFlag ) usleep(100);
//Advance the LEDs to this position when outputting the values.
for( i = 0; i < led->total_leds; i++ )
{
int source = i;
if( !led->array )
{
int sx, sy;
if( led->rot90 )
{
sy = i % led->yn;
sx = i / led->yn;
}
else
{
sx = i % led->xn;
sy = i / led->xn;
}
if( led->zigzag )
{
if( led->rot90 )
{
if( sx & 1 )
{
sy = led->yn - sy - 1;
}
}
else
{
if( sy & 1 )
{
sx = led->xn - sx - 1;
}
}
}
if( led->rot90 )
{
source = sx + sy * led->xn;
}
else
{
source = sx + sy * led->yn;
}
}
led->last_leds[i*3+0 + (i / 20 + 1) * 4] = OutLEDs[source*3+1] * led->outamp;
led->last_leds[i*3+1 + (i / 20 + 1) * 4] = OutLEDs[source*3+0] * led->outamp;
led->last_leds[i*3+2 + (i / 20 + 1) * 4] = OutLEDs[source*3+2] * led->outamp;
if(i % 20 == 0)
{
led->last_leds[(i / 20) * 64 + 0] = i;
led->last_leds[(i / 20) * 64 + 1] = i >> 8;
led->last_leds[(i / 20) * 64 + 2] = i >> 16;
led->last_leds[(i / 20) * 64 + 3] = i >> 24;
}
}
led->readyFlag = 1;
}
static void LEDParams(void * id )
{
struct LEDOutDriver * led = (struct LEDOutDriver*)id;
led->total_leds = GetParameterI( "leds", 300 );
led->last_leds = (uint8_t*)malloc( led->total_leds * 3 + 1 + ((led->total_leds + 19) / 20) * 4 + 64);
led->outamp = .1; RegisterValue( "ledoutamp", PAFLOAT, &led->outamp, sizeof( led->outamp ) );
led->zigzag = 0; RegisterValue( "zigzag", PAINT, &led->zigzag, sizeof( led->zigzag ) );
led->xn = 16; RegisterValue( "lightx", PAINT, &led->xn, sizeof( led->xn ) );
led->yn = 9; RegisterValue( "lighty", PAINT, &led->yn, sizeof( led->yn ) );
led->rot90 = 0; RegisterValue( "rot90", PAINT, &led->rot90, sizeof( led->rot90 ) );
led->array = 0; RegisterValue( "ledarray", PAINT, &led->array, sizeof( led->array ) );
led->did_init = 0;
}
static struct DriverInstances * DisplayUSBIsochronous()
{
struct DriverInstances * ret = malloc( sizeof( struct DriverInstances ) );
memset( ret, 0, sizeof( struct DriverInstances ) );
struct LEDOutDriver * led = ret->id = malloc( sizeof( struct LEDOutDriver ) );
ret->Func = LEDUpdate;
ret->Params = LEDParams;
OGCreateThread( LEDOutThread, led );
led->readyFlag = 0;
LEDParams( led );
return ret;
}
REGISTER_OUT_DRIVER(DisplayUSBIsochronous);

View file

@ -1,276 +0,0 @@
/*
Copyright (c) 2010, 2011, 2015 <>< Charles Lohr
This file may be licensed under the NewBSD License OR The license found
here:
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
#include "DrawFunctions.h"
#include <stdio.h>
int CNFGPenX, CNFGPenY;
uint32_t CNFGBGColor;
uint32_t CNFGLastColor;
uint32_t CNFGDialogColor; //background for boxes
const unsigned short FontCharMap[256] = {
65535, 0, 10, 20, 32, 44, 56, 68, 70, 65535, 65535, 80, 92, 65535, 104, 114,
126, 132, 138, 148, 156, 166, 180, 188, 200, 206, 212, 218, 224, 228, 238, 244,
65535, 250, 254, 258, 266, 278, 288, 302, 304, 310, 316, 324, 328, 226, 252, 330,
332, 342, 348, 358, 366, 372, 382, 392, 400, 410, 420, 424, 428, 262, 432, 436,
446, 460, 470, 486, 496, 508, 516, 522, 536, 542, 548, 556, 568, 572, 580, 586,
598, 608, 622, 634, 644, 648, 654, 662, 670, 682, 692, 700, 706, 708, 492, 198,
714, 716, 726, 734, 742, 750, 760, 768, 782, 790, 794, 802, 204, 810, 820, 384,
828, 836, 844, 850, 860, 864, 872, 880, 890, 894, 902, 908, 916, 920, 928, 934,
65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535,
65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535,
944, 948, 960, 970, 986, 996, 1000, 1016, 1020, 1026, 1034, 1042, 364, 1046, 1056, 1058,
1066, 1072, 1080, 932, 1092, 1100, 68, 1110, 1114, 1116, 1124, 1132, 1142, 1154, 1170, 1180,
1192, 1204, 1218, 1234, 1248, 1264, 1276, 1290, 1300, 1310, 1322, 1334, 1342, 1350, 1360, 1370,
1384, 1396, 1406, 1416, 1428, 1442, 1454, 1458, 1472, 1480, 1488, 1498, 1508, 1520, 1530, 1544,
1556, 1568, 1582, 1598, 1612, 1628, 1642, 1654, 1666, 1678, 1692, 1706, 1710, 1714, 1720, 1726,
1738, 1752, 1762, 1772, 1784, 1798, 1810, 1816, 1826, 1836, 1846, 1858, 1870, 1880, 1890, 65535, };
const unsigned char FontCharData[1902] = {
0x00, 0x01, 0x20, 0x21, 0x03, 0x23, 0x23, 0x14, 0x14, 0x83, 0x00, 0x01, 0x20, 0x21, 0x04, 0x24,
0x24, 0x13, 0x13, 0x84, 0x01, 0x21, 0x21, 0x23, 0x23, 0x14, 0x14, 0x03, 0x03, 0x01, 0x11, 0x92,
0x11, 0x22, 0x22, 0x23, 0x23, 0x14, 0x14, 0x03, 0x03, 0x02, 0x02, 0x91, 0x01, 0x21, 0x21, 0x23,
0x23, 0x01, 0x03, 0x21, 0x03, 0x01, 0x12, 0x94, 0x03, 0x23, 0x13, 0x14, 0x23, 0x22, 0x22, 0x11,
0x11, 0x02, 0x02, 0x83, 0x12, 0x92, 0x12, 0x12, 0x01, 0x21, 0x21, 0x23, 0x23, 0x03, 0x03, 0x81,
0x03, 0x21, 0x21, 0x22, 0x21, 0x11, 0x03, 0x14, 0x14, 0x23, 0x23, 0x92, 0x01, 0x10, 0x10, 0x21,
0x21, 0x12, 0x12, 0x01, 0x12, 0x14, 0x03, 0xa3, 0x02, 0x03, 0x03, 0x13, 0x02, 0x12, 0x13, 0x10,
0x10, 0xa1, 0x01, 0x23, 0x03, 0x21, 0x02, 0x11, 0x11, 0x22, 0x22, 0x13, 0x13, 0x82, 0x00, 0x22,
0x22, 0x04, 0x04, 0x80, 0x20, 0x02, 0x02, 0x24, 0x24, 0xa0, 0x01, 0x10, 0x10, 0x21, 0x10, 0x14,
0x14, 0x03, 0x14, 0xa3, 0x00, 0x03, 0x04, 0x04, 0x20, 0x23, 0x24, 0xa4, 0x00, 0x20, 0x00, 0x02,
0x02, 0x22, 0x10, 0x14, 0x20, 0xa4, 0x01, 0x21, 0x21, 0x23, 0x23, 0x03, 0x03, 0x01, 0x20, 0x10,
0x10, 0x14, 0x14, 0x84, 0x03, 0x23, 0x23, 0x24, 0x24, 0x04, 0x04, 0x83, 0x01, 0x10, 0x10, 0x21,
0x10, 0x14, 0x14, 0x03, 0x14, 0x23, 0x04, 0xa4, 0x01, 0x10, 0x21, 0x10, 0x10, 0x94, 0x03, 0x14,
0x23, 0x14, 0x10, 0x94, 0x02, 0x22, 0x22, 0x11, 0x22, 0x93, 0x02, 0x22, 0x02, 0x11, 0x02, 0x93,
0x01, 0x02, 0x02, 0xa2, 0x02, 0x22, 0x22, 0x11, 0x11, 0x02, 0x02, 0x13, 0x13, 0xa2, 0x11, 0x22,
0x22, 0x02, 0x02, 0x91, 0x02, 0x13, 0x13, 0x22, 0x22, 0x82, 0x10, 0x13, 0x14, 0x94, 0x10, 0x01,
0x20, 0x91, 0x10, 0x14, 0x20, 0x24, 0x01, 0x21, 0x03, 0xa3, 0x21, 0x10, 0x10, 0x01, 0x01, 0x23,
0x23, 0x14, 0x14, 0x03, 0x10, 0x94, 0x00, 0x01, 0x23, 0x24, 0x04, 0x03, 0x03, 0x21, 0x21, 0xa0,
0x21, 0x10, 0x10, 0x01, 0x01, 0x12, 0x12, 0x03, 0x03, 0x14, 0x14, 0x23, 0x02, 0xa4, 0x10, 0x91,
0x10, 0x01, 0x01, 0x03, 0x03, 0x94, 0x10, 0x21, 0x21, 0x23, 0x23, 0x94, 0x01, 0x23, 0x11, 0x13,
0x21, 0x03, 0x02, 0xa2, 0x02, 0x22, 0x11, 0x93, 0x31, 0xc0, 0x03, 0xa1, 0x00, 0x20, 0x20, 0x24,
0x24, 0x04, 0x04, 0x00, 0x12, 0x92, 0x01, 0x10, 0x10, 0x14, 0x04, 0xa4, 0x01, 0x10, 0x10, 0x21,
0x21, 0x22, 0x22, 0x04, 0x04, 0xa4, 0x00, 0x20, 0x20, 0x24, 0x24, 0x04, 0x12, 0xa2, 0x00, 0x02,
0x02, 0x22, 0x20, 0xa4, 0x20, 0x00, 0x00, 0x02, 0x02, 0x22, 0x22, 0x24, 0x24, 0x84, 0x20, 0x02,
0x02, 0x22, 0x22, 0x24, 0x24, 0x04, 0x04, 0x82, 0x00, 0x20, 0x20, 0x21, 0x21, 0x12, 0x12, 0x94,
0x00, 0x04, 0x00, 0x20, 0x20, 0x24, 0x04, 0x24, 0x02, 0xa2, 0x00, 0x02, 0x02, 0x22, 0x22, 0x20,
0x20, 0x00, 0x22, 0x84, 0x11, 0x11, 0x13, 0x93, 0x11, 0x11, 0x13, 0x84, 0x20, 0x02, 0x02, 0xa4,
0x00, 0x22, 0x22, 0x84, 0x01, 0x10, 0x10, 0x21, 0x21, 0x12, 0x12, 0x13, 0x14, 0x94, 0x21, 0x01,
0x01, 0x04, 0x04, 0x24, 0x24, 0x22, 0x22, 0x12, 0x12, 0x13, 0x13, 0xa3, 0x04, 0x01, 0x01, 0x10,
0x10, 0x21, 0x21, 0x24, 0x02, 0xa2, 0x00, 0x04, 0x04, 0x14, 0x14, 0x23, 0x23, 0x12, 0x12, 0x02,
0x12, 0x21, 0x21, 0x10, 0x10, 0x80, 0x23, 0x14, 0x14, 0x03, 0x03, 0x01, 0x01, 0x10, 0x10, 0xa1,
0x00, 0x10, 0x10, 0x21, 0x21, 0x23, 0x23, 0x14, 0x14, 0x04, 0x04, 0x80, 0x00, 0x04, 0x04, 0x24,
0x00, 0x20, 0x02, 0x92, 0x00, 0x04, 0x00, 0x20, 0x02, 0x92, 0x21, 0x10, 0x10, 0x01, 0x01, 0x03,
0x03, 0x14, 0x14, 0x23, 0x23, 0x22, 0x22, 0x92, 0x00, 0x04, 0x20, 0x24, 0x02, 0xa2, 0x00, 0x20,
0x10, 0x14, 0x04, 0xa4, 0x00, 0x20, 0x20, 0x23, 0x23, 0x14, 0x14, 0x83, 0x00, 0x04, 0x02, 0x12,
0x12, 0x21, 0x21, 0x20, 0x12, 0x23, 0x23, 0xa4, 0x00, 0x04, 0x04, 0xa4, 0x04, 0x00, 0x00, 0x11,
0x11, 0x20, 0x20, 0xa4, 0x04, 0x00, 0x00, 0x22, 0x20, 0xa4, 0x01, 0x10, 0x10, 0x21, 0x21, 0x23,
0x23, 0x14, 0x14, 0x03, 0x03, 0x81, 0x00, 0x04, 0x00, 0x10, 0x10, 0x21, 0x21, 0x12, 0x12, 0x82,
0x01, 0x10, 0x10, 0x21, 0x21, 0x23, 0x23, 0x14, 0x14, 0x03, 0x03, 0x01, 0x04, 0x93, 0x00, 0x04,
0x00, 0x10, 0x10, 0x21, 0x21, 0x12, 0x12, 0x02, 0x02, 0xa4, 0x21, 0x10, 0x10, 0x01, 0x01, 0x23,
0x23, 0x14, 0x14, 0x83, 0x00, 0x20, 0x10, 0x94, 0x00, 0x04, 0x04, 0x24, 0x24, 0xa0, 0x00, 0x03,
0x03, 0x14, 0x14, 0x23, 0x23, 0xa0, 0x00, 0x04, 0x04, 0x24, 0x14, 0x13, 0x24, 0xa0, 0x00, 0x01,
0x01, 0x23, 0x23, 0x24, 0x04, 0x03, 0x03, 0x21, 0x21, 0xa0, 0x00, 0x01, 0x01, 0x12, 0x12, 0x14,
0x12, 0x21, 0x21, 0xa0, 0x00, 0x20, 0x20, 0x02, 0x02, 0x04, 0x04, 0xa4, 0x10, 0x00, 0x00, 0x04,
0x04, 0x94, 0x01, 0xa3, 0x10, 0x20, 0x20, 0x24, 0x24, 0x94, 0x00, 0x91, 0x02, 0x04, 0x04, 0x24,
0x24, 0x22, 0x23, 0x12, 0x12, 0x82, 0x00, 0x04, 0x04, 0x24, 0x24, 0x22, 0x22, 0x82, 0x24, 0x04,
0x04, 0x03, 0x03, 0x12, 0x12, 0xa2, 0x20, 0x24, 0x24, 0x04, 0x04, 0x02, 0x02, 0xa2, 0x24, 0x04,
0x04, 0x02, 0x02, 0x22, 0x22, 0x23, 0x23, 0x93, 0x04, 0x01, 0x02, 0x12, 0x01, 0x10, 0x10, 0xa1,
0x23, 0x12, 0x12, 0x03, 0x03, 0x14, 0x14, 0x23, 0x23, 0x24, 0x24, 0x15, 0x15, 0x84, 0x00, 0x04,
0x03, 0x12, 0x12, 0x23, 0x23, 0xa4, 0x11, 0x11, 0x12, 0x94, 0x22, 0x22, 0x23, 0x24, 0x24, 0x15,
0x15, 0x84, 0x00, 0x04, 0x03, 0x13, 0x13, 0x22, 0x13, 0xa4, 0x02, 0x04, 0x02, 0x13, 0x12, 0x14,
0x12, 0x23, 0x23, 0xa4, 0x02, 0x04, 0x03, 0x12, 0x12, 0x23, 0x23, 0xa4, 0x02, 0x05, 0x04, 0x24,
0x24, 0x22, 0x22, 0x82, 0x02, 0x04, 0x04, 0x24, 0x25, 0x22, 0x22, 0x82, 0x02, 0x04, 0x03, 0x12,
0x12, 0xa2, 0x22, 0x02, 0x02, 0x03, 0x03, 0x23, 0x23, 0x24, 0x24, 0x84, 0x11, 0x14, 0x02, 0xa2,
0x02, 0x04, 0x04, 0x14, 0x14, 0x23, 0x24, 0xa2, 0x02, 0x03, 0x03, 0x14, 0x14, 0x23, 0x23, 0xa2,
0x02, 0x03, 0x03, 0x14, 0x14, 0x12, 0x13, 0x24, 0x24, 0xa2, 0x02, 0x24, 0x04, 0xa2, 0x02, 0x03,
0x03, 0x14, 0x22, 0x23, 0x23, 0x85, 0x02, 0x22, 0x22, 0x04, 0x04, 0xa4, 0x20, 0x10, 0x10, 0x14,
0x14, 0x24, 0x12, 0x82, 0x10, 0x11, 0x13, 0x94, 0x00, 0x10, 0x10, 0x14, 0x14, 0x04, 0x12, 0xa2,
0x01, 0x10, 0x10, 0x11, 0x11, 0xa0, 0x03, 0x04, 0x04, 0x24, 0x24, 0x23, 0x23, 0x12, 0x12, 0x83,
0x10, 0x10, 0x11, 0x94, 0x21, 0x10, 0x10, 0x01, 0x01, 0x02, 0x02, 0x13, 0x13, 0x22, 0x10, 0x93,
0x11, 0x00, 0x00, 0x04, 0x04, 0x24, 0x24, 0x23, 0x02, 0x92, 0x01, 0x02, 0x11, 0x21, 0x22, 0x23,
0x03, 0x13, 0x02, 0x11, 0x11, 0x22, 0x22, 0x13, 0x13, 0x82, 0x00, 0x11, 0x11, 0x20, 0x11, 0x14,
0x02, 0x22, 0x03, 0xa3, 0x10, 0x12, 0x13, 0x95, 0x20, 0x00, 0x00, 0x02, 0x02, 0x11, 0x11, 0x22,
0x02, 0x13, 0x13, 0x22, 0x22, 0x24, 0x24, 0x84, 0x00, 0x00, 0x20, 0xa0, 0x20, 0x10, 0x10, 0x11,
0x11, 0xa1, 0x10, 0x21, 0x20, 0x21, 0x21, 0x11, 0x11, 0x90, 0x11, 0x02, 0x02, 0x13, 0x21, 0x12,
0x12, 0xa3, 0x01, 0x21, 0x21, 0xa2, 0x10, 0x20, 0x20, 0x21, 0x21, 0x11, 0x12, 0x10, 0x11, 0xa2,
0x00, 0xa0, 0x01, 0x10, 0x10, 0x21, 0x21, 0x12, 0x12, 0x81, 0x02, 0x22, 0x11, 0x13, 0x03, 0xa3,
0x01, 0x10, 0x10, 0x21, 0x21, 0x03, 0x03, 0xa3, 0x01, 0x10, 0x10, 0x21, 0x21, 0x12, 0x12, 0x02,
0x12, 0x23, 0x23, 0x83, 0x02, 0x05, 0x04, 0x14, 0x14, 0x23, 0x22, 0xa4, 0x14, 0x10, 0x10, 0x01,
0x01, 0x12, 0x10, 0x20, 0x20, 0xa4, 0x14, 0x15, 0x15, 0x85, 0x20, 0xa1, 0x10, 0x20, 0x20, 0x21,
0x21, 0x11, 0x11, 0x90, 0x01, 0x12, 0x12, 0x03, 0x11, 0x22, 0x22, 0x93, 0x00, 0x01, 0x02, 0x20,
0x12, 0x13, 0x13, 0x23, 0x22, 0xa4, 0x00, 0x01, 0x02, 0x20, 0x12, 0x22, 0x22, 0x13, 0x13, 0x14,
0x14, 0xa4, 0x00, 0x10, 0x10, 0x11, 0x11, 0x01, 0x11, 0x02, 0x02, 0x20, 0x12, 0x13, 0x13, 0x23,
0x22, 0xa4, 0x10, 0x10, 0x11, 0x12, 0x12, 0x03, 0x03, 0x14, 0x14, 0xa3, 0x04, 0x02, 0x02, 0x11,
0x11, 0x22, 0x22, 0x24, 0x03, 0x23, 0x10, 0xa1, 0x04, 0x02, 0x02, 0x11, 0x11, 0x22, 0x22, 0x24,
0x03, 0x23, 0x01, 0x90, 0x04, 0x02, 0x02, 0x11, 0x11, 0x22, 0x22, 0x24, 0x03, 0x23, 0x01, 0x10,
0x10, 0xa1, 0x04, 0x02, 0x02, 0x11, 0x11, 0x22, 0x22, 0x24, 0x03, 0x23, 0x01, 0x10, 0x10, 0x11,
0x11, 0xa0, 0x04, 0x02, 0x02, 0x11, 0x11, 0x22, 0x22, 0x24, 0x03, 0x23, 0x00, 0x00, 0x20, 0xa0,
0x04, 0x02, 0x02, 0x11, 0x11, 0x22, 0x22, 0x24, 0x03, 0x23, 0x00, 0x20, 0x20, 0x11, 0x11, 0x80,
0x00, 0x04, 0x02, 0x22, 0x00, 0x11, 0x10, 0x14, 0x10, 0x20, 0x14, 0xa4, 0x23, 0x14, 0x14, 0x03,
0x03, 0x01, 0x01, 0x10, 0x10, 0x21, 0x14, 0x15, 0x15, 0x85, 0x02, 0x22, 0x02, 0x04, 0x04, 0x24,
0x03, 0x13, 0x00, 0x91, 0x02, 0x22, 0x02, 0x04, 0x04, 0x24, 0x03, 0x13, 0x11, 0xa0, 0x02, 0x22,
0x02, 0x04, 0x04, 0x24, 0x03, 0x13, 0x01, 0x10, 0x10, 0xa1, 0x02, 0x22, 0x02, 0x04, 0x04, 0x24,
0x03, 0x13, 0x00, 0x00, 0x20, 0xa0, 0x02, 0x22, 0x12, 0x14, 0x04, 0x24, 0x00, 0x91, 0x02, 0x22,
0x12, 0x14, 0x04, 0x24, 0x11, 0xa0, 0x02, 0x22, 0x12, 0x14, 0x04, 0x24, 0x01, 0x10, 0x10, 0xa1,
0x02, 0x22, 0x12, 0x14, 0x04, 0x24, 0x20, 0x20, 0x00, 0x80, 0x00, 0x10, 0x10, 0x21, 0x21, 0x23,
0x23, 0x14, 0x14, 0x04, 0x04, 0x00, 0x02, 0x92, 0x04, 0x02, 0x02, 0x24, 0x24, 0x22, 0x01, 0x10,
0x10, 0x11, 0x11, 0xa0, 0x02, 0x22, 0x22, 0x24, 0x24, 0x04, 0x04, 0x02, 0x00, 0x91, 0x02, 0x22,
0x22, 0x24, 0x24, 0x04, 0x04, 0x02, 0x11, 0xa0, 0x02, 0x22, 0x22, 0x24, 0x24, 0x04, 0x04, 0x02,
0x11, 0x20, 0x00, 0x91, 0x02, 0x22, 0x22, 0x24, 0x24, 0x04, 0x04, 0x02, 0x01, 0x10, 0x10, 0x11,
0x11, 0xa0, 0x01, 0x21, 0x21, 0x24, 0x24, 0x04, 0x04, 0x01, 0x00, 0x00, 0x20, 0xa0, 0x01, 0x23,
0x03, 0xa1, 0x01, 0x10, 0x10, 0x21, 0x21, 0x23, 0x23, 0x14, 0x14, 0x03, 0x03, 0x01, 0x03, 0xa1,
0x01, 0x04, 0x04, 0x24, 0x24, 0x21, 0x11, 0xa0, 0x01, 0x04, 0x04, 0x24, 0x24, 0x21, 0x00, 0x91,
0x02, 0x04, 0x04, 0x24, 0x24, 0x22, 0x01, 0x10, 0x10, 0xa1, 0x01, 0x04, 0x04, 0x24, 0x24, 0x21,
0x00, 0x00, 0x20, 0xa0, 0x01, 0x02, 0x02, 0x13, 0x13, 0x14, 0x13, 0x22, 0x22, 0x21, 0x11, 0xa0,
0x00, 0x04, 0x01, 0x11, 0x11, 0x22, 0x22, 0x13, 0x13, 0x83, 0x00, 0x05, 0x00, 0x10, 0x10, 0x21,
0x21, 0x12, 0x02, 0x22, 0x22, 0x24, 0x24, 0x84, 0x02, 0x04, 0x04, 0x24, 0x24, 0x22, 0x23, 0x12,
0x12, 0x02, 0x00, 0x91, 0x02, 0x04, 0x04, 0x24, 0x24, 0x22, 0x23, 0x12, 0x12, 0x02, 0x11, 0xa0,
0x02, 0x04, 0x04, 0x24, 0x24, 0x22, 0x23, 0x12, 0x12, 0x02, 0x01, 0x10, 0x10, 0xa1, 0x02, 0x04,
0x04, 0x24, 0x24, 0x22, 0x23, 0x12, 0x12, 0x02, 0x01, 0x10, 0x10, 0x11, 0x11, 0xa0, 0x02, 0x04,
0x04, 0x24, 0x24, 0x22, 0x23, 0x12, 0x12, 0x02, 0x01, 0x01, 0x21, 0xa1, 0x02, 0x04, 0x04, 0x24,
0x24, 0x22, 0x23, 0x12, 0x12, 0x02, 0x01, 0x10, 0x10, 0x21, 0x21, 0x81, 0x02, 0x13, 0x02, 0x04,
0x04, 0x24, 0x12, 0x14, 0x12, 0x22, 0x13, 0x23, 0x22, 0xa3, 0x03, 0x04, 0x04, 0x24, 0x03, 0x12,
0x12, 0x22, 0x14, 0x15, 0x15, 0x85, 0x24, 0x04, 0x04, 0x02, 0x02, 0x22, 0x22, 0x23, 0x23, 0x13,
0x00, 0x91, 0x24, 0x04, 0x04, 0x02, 0x02, 0x22, 0x22, 0x23, 0x23, 0x13, 0x11, 0xa0, 0x24, 0x04,
0x04, 0x02, 0x02, 0x22, 0x22, 0x23, 0x23, 0x13, 0x01, 0x10, 0x10, 0xa1, 0x24, 0x04, 0x04, 0x02,
0x02, 0x22, 0x22, 0x23, 0x23, 0x13, 0x01, 0x01, 0x21, 0xa1, 0x12, 0x14, 0x00, 0x91, 0x12, 0x14,
0x11, 0xa0, 0x12, 0x14, 0x01, 0x10, 0x10, 0xa1, 0x12, 0x14, 0x01, 0x01, 0x21, 0xa1, 0x00, 0x22,
0x11, 0x20, 0x02, 0x22, 0x22, 0x24, 0x24, 0x04, 0x04, 0x82, 0x02, 0x04, 0x03, 0x12, 0x12, 0x23,
0x23, 0x24, 0x01, 0x10, 0x10, 0x11, 0x11, 0xa0, 0x02, 0x04, 0x04, 0x24, 0x24, 0x22, 0x22, 0x02,
0x00, 0x91, 0x02, 0x04, 0x04, 0x24, 0x24, 0x22, 0x22, 0x02, 0x11, 0xa0, 0x02, 0x04, 0x04, 0x24,
0x24, 0x22, 0x22, 0x02, 0x01, 0x10, 0x10, 0xa1, 0x02, 0x04, 0x04, 0x24, 0x24, 0x22, 0x22, 0x02,
0x01, 0x10, 0x10, 0x11, 0x11, 0xa0, 0x02, 0x04, 0x04, 0x24, 0x24, 0x22, 0x22, 0x02, 0x01, 0x01,
0x21, 0xa1, 0x11, 0x11, 0x02, 0x22, 0x13, 0x93, 0x02, 0x04, 0x04, 0x24, 0x24, 0x22, 0x22, 0x02,
0x04, 0xa2, 0x02, 0x04, 0x04, 0x14, 0x14, 0x23, 0x24, 0x22, 0x00, 0x91, 0x02, 0x04, 0x04, 0x14,
0x14, 0x23, 0x24, 0x22, 0x11, 0xa0, 0x02, 0x04, 0x04, 0x14, 0x14, 0x23, 0x24, 0x22, 0x01, 0x10,
0x10, 0xa1, 0x02, 0x04, 0x04, 0x14, 0x14, 0x23, 0x24, 0x22, 0x01, 0x01, 0x21, 0xa1, 0x02, 0x03,
0x03, 0x14, 0x22, 0x23, 0x23, 0x05, 0x11, 0xa0, 0x00, 0x04, 0x02, 0x11, 0x11, 0x22, 0x22, 0x13,
0x13, 0x82, 0x02, 0x03, 0x03, 0x14, 0x22, 0x23, 0x23, 0x05, 0x01, 0x01, 0x21, 0xa1, };
void CNFGDrawText( const char * text, int scale )
{
const unsigned char * lmap;
float iox = (float)CNFGPenX;
float ioy = (float)CNFGPenY;
int place = 0;
unsigned short index;
int bQuit = 0;
while( text[place] )
{
unsigned char c = text[place];
switch( c )
{
case 9:
iox += 12 * scale;
break;
case 10:
iox = (float)CNFGPenX;
ioy += 6 * scale;
break;
default:
index = FontCharMap[c];
if( index == 65535 )
{
iox += 3 * scale;
break;
}
lmap = &FontCharData[index];
do
{
int x1 = (int)((((*lmap) & 0x70)>>4)*scale + iox);
int y1 = (int)(((*lmap) & 0x0f)*scale + ioy);
int x2 = (int)((((*(lmap+1)) & 0x70)>>4)*scale + iox);
int y2 = (int)(((*(lmap+1)) & 0x0f)*scale + ioy);
lmap++;
CNFGTackSegment( x1, y1, x2, y2 );
bQuit = *lmap & 0x80;
lmap++;
} while( !bQuit );
iox += 3 * scale;
}
place++;
}
}
void CNFGDrawBox( int x1, int y1, int x2, int y2 )
{
unsigned lc = CNFGLastColor;
CNFGColor( CNFGDialogColor );
CNFGTackRectangle( x1, y1, x2, y2 );
CNFGColor( lc );
CNFGTackSegment( x1, y1, x2, y1 );
CNFGTackSegment( x2, y1, x2, y2 );
CNFGTackSegment( x2, y2, x1, y2 );
CNFGTackSegment( x1, y2, x1, y1 );
}
void CNFGGetTextExtents( const char * text, int * w, int * h, int textsize )
{
int charsx = 0;
int charsy = 1;
int charsline = 0;
const char * s;
for( s = text; *s; s++ )
{
if( *s == '\n' )
{
charsline = 0;
if( *(s+1) )
charsy++;
}
else
{
charsline++;
if( charsline > charsx )
charsx = charsline;
}
}
*w = charsx * textsize * 3 + textsize;
*h = charsy * textsize * 6;
}
void CNFGDrawTextbox( int x, int y, const char * text, int textsize )
{
int w;
int h;
CNFGGetTextExtents( text, &w, &h, textsize );
CNFGDrawBox( x, y, x + w, y + h );
CNFGPenX = x + textsize;
CNFGPenY = y + textsize;
CNFGDrawText( text, textsize );
}

View file

@ -1,54 +0,0 @@
//Copyright (c) 2011 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose.
#ifndef _DRAWFUCNTIONS_H
#define _DRAWFUCNTIONS_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
typedef struct {
short x, y;
} RDPoint;
extern int CNFGPenX, CNFGPenY;
extern uint32_t CNFGBGColor;
extern uint32_t CNFGLastColor;
extern uint32_t CNFGDialogColor; //background for boxes
void CNFGDrawText( const char * text, int scale );
void CNFGDrawBox( int x1, int y1, int x2, int y2 );
void CNFGGetTextExtents( const char * text, int * w, int * h, int textsize );
void CNFGDrawTextbox( int x, int y, const char * text, int textsize ); //ignores pen.
//To be provided by driver.
uint32_t CNFGColor( uint32_t RGB );
void CNFGUpdateScreenWithBitmap( unsigned long * data, int w, int h );
void CNFGTackPixel( short x1, short y1 );
void CNFGTackSegment( short x1, short y1, short x2, short y2 );
void CNFGTackRectangle( short x1, short y1, short x2, short y2 );
void CNFGTackPoly( RDPoint * points, int verts );
void CNFGClearFrame();
void CNFGSwapBuffers();
void CNFGGetDimensions( short * x, short * y );
void CNFGSetup( const char * WindowName, int w, int h );
void CNFGSetupFullscreen( const char * WindowName, int screen_number );
void CNFGHandleInput();
//You must provide:
void HandleKey( int keycode, int bDown );
void HandleButton( int x, int y, int button, int bDown );
void HandleMotion( int x, int y, int mask );
#ifdef __cplusplus
};
#endif
#endif

View file

@ -1,29 +1,42 @@
all : colorchord
all : colorchord colorchord-opengl
RAWDRAW:=DrawFunctions.o XDriver.o
SOUND:=sound.o sound_alsa.o sound_pulse.o sound_null.o
OUTS := OutputVoronoi.o DisplayArray.o OutputLinear.o DisplayPie.o DisplayNetwork.o DisplayUSB2812.o DisplayUSBIsochronous.o DisplayDMX.o OutputProminent.o RecorderPlugin.o DisplayHIDAPI.o hidapi.o OutputCells.o DisplaySHM.o DisplayFileWrite.o DisplayRadialPoles.o
OUTS := OutputVoronoi.o DisplayArray.o OutputLinear.o DisplayPie.o DisplayNetwork.o DisplayUSB2812.o DisplayDMX.o OutputProminent.o RecorderPlugin.o DisplayHIDAPI.o hidapi.o OutputCells.o DisplaySHM.o DisplayFileWrite.o
WINGCC?=i686-w64-mingw32-gcc
WINGCC:= i686-w64-mingw32-gcc
WINGCCFLAGS:= -g -DICACHE_FLASH_ATTR= -I../embeddedcommon -Icnfa -Irawdraw -I. -O1 #-O2 -Wl,--relax -Wl,--gc-sections -ffunction-sections -fdata-sections
WINLDFLAGS:=-lwinmm -lgdi32 -lws2_32 -lsetupapi -lole32 -lavrt
WINGCCFLAGS:= -g -DICACHE_FLASH_ATTR= -I../embeddedcommon -I. -O1 #-O2 -Wl,--relax -Wl,--gc-sections -ffunction-sections -fdata-sections
WINLDFLAGS:=-lwinmm -lgdi32 -lws2_32 -lsetupapi
#CFLAGS:=-g -O1 -flto -Wall -ffast-math -I../embeddedcommon -I. -Icnfa -Irawdraw -DICACHE_FLASH_ATTR=
CFLAGS:=-g -O2 -Wall -ffast-math -I../embeddedcommon -I. -Icnfa -Irawdraw -DICACHE_FLASH_ATTR=
RAWDRAWLIBS:=-lX11 -lm -lpthread -lXinerama -lXext
LDLIBS:=-lpthread -lasound -lm -lpulse-simple -lpulse -ludev -lrt
ifneq ($(OS),Windows_NT)
ifneq ($(shell uname),Darwin)
RAWDRAWCFLAGS:=$(shell pkg-config --cflags x11 xinerama xext libusb-1.0)
RAWDRAWLIBS:=-lm -lrt -pthread $(shell pkg-config --libs x11 xinerama xext libusb-1.0)
CFLAGS+=$(RAWDRAWCFLAGS)
endif
endif
ifeq ($(shell uname),Linux)
EXTRALIBS+=-ludev
EXTRALIBS+=$(shell pkg-config --libs alsa)
EXTRALIBS+=$(shell pkg-config --libs libpulse)
EXTRALIBS+=$(shell pkg-config --libs libpulse-simple)
OPENGLLIBS=$(shell pkg-config --cflags --libs glut)
endif
CFLAGS:=-g -O0 -flto -Wall -ffast-math -I../embeddedcommon -I. -DICACHE_FLASH_ATTR=
EXTRALIBS:=-lusb-1.0
OBJS:=main.o dft.o decompose.o filter.o color.o notefinder.o util.o outdrivers.o $(OUTS) parameters.o chash.o hook.o ../embeddedcommon/DFT32.o configs.o
colorchord : os_generic.o main.o dft.o decompose.o filter.o color.o notefinder.o util.o outdrivers.o $(RAWDRAW) $(SOUND) $(OUTS) parameters.o chash.o hook.o ../embeddedcommon/DFT32.o configs.o
gcc -o $@ $^ $(CFLAGS) $(LDLIBS) $(EXTRALIBS) $(RAWDRAWLIBS)
colorchord : $(OBJS)
$(CC) -o $@ $^ $(EXTRALIBS) $(RAWDRAWLIBS)
colorchord-opengl : $(OBJS)
$(CC) -o $@ $^ -DCNFGOGL $(EXTRALIBS) $(OPENGLLIBS) $(RAWDRAWLIBS)
colorchord.exe : os_generic.c main.c dft.c decompose.c filter.c color.c notefinder.c util.c outdrivers.c DrawFunctions.c parameters.c chash.c WinDriver.c sound.c sound_null.c sound_win.c OutputVoronoi.c OutputProminent.c DisplayArray.c OutputLinear.c DisplayPie.c DisplayNetwork.c hook.c RecorderPlugin.c ../embeddedcommon/DFT32.c OutputCells.c configs.c hidapi.c DisplayHIDAPI.c
$(WINGCC) $(WINGCCFLAGS) -o $@ $^ $(WINLDFLAGS)
colorchord.exe : main.c dft.c decompose.c filter.c color.c notefinder.c util.c outdrivers.c parameters.c chash.c OutputVoronoi.c OutputProminent.c DisplayArray.c OutputLinear.c DisplayPie.c DisplayNetwork.c hook.c RecorderPlugin.c ../embeddedcommon/DFT32.c OutputCells.c configs.c hidapi.c DisplayHIDAPI.c
$(WINGCC) $(WINGCCFLAGS) -o $@ $^ $(o) $(WINLDFLAGS)
clean :
rm -rf *.o *~ colorchord colorchord.exe embeddedcc
rm -rf *.o *~ colorchord colorchord-opengl colorchord.exe embeddedcc $(OBJS)

View file

@ -30,6 +30,7 @@ struct CellsOutDriver
float light_siding;
float satamp;
float qtyamp;
float outgamma;
int steady_bright;
int timebased; //Useful for pies, turn off for linear systems.
int snakey; //Advance head for where to get LEDs around.
@ -184,11 +185,11 @@ static void LEDUpdate(void * id, struct NoteFinder*nf)
if( satQ > 1 ) satQ = 1;
float sendsat = (led->steady_bright?sat:satQ);
if( sendsat > 1 ) sendsat = 1;
int r = CCtoHEX( binpos[ia], 1.0, sendsat );
int r = CCtoHEX( binpos[ia], 1.0, pow( sendsat, led->outgamma ) );
OutLEDs[i*3+0] = r & 0xff;
OutLEDs[i*3+1] = (r>>8) & 0xff;
OutLEDs[i*3+2] = (r>>16) & 0xff;
OutLEDs[i*3+0] = (r>>24) & 0xff;
OutLEDs[i*3+1] = (r>>16) & 0xff;
OutLEDs[i*3+2] = (r>>8) & 0xff;
}
}
@ -199,11 +200,12 @@ static void LEDParams(void * id )
led->satamp = 2; RegisterValue( "satamp", PAFLOAT, &led->satamp, sizeof( led->satamp ) );
led->total_leds = 300; RegisterValue( "leds", PAINT, &led->total_leds, sizeof( led->total_leds ) );
led->steady_bright = 1; RegisterValue( "seady_bright", PAINT, &led->steady_bright, sizeof( led->steady_bright ) );
led->steady_bright = 1; RegisterValue( "steady_bright", PAINT, &led->steady_bright, sizeof( led->steady_bright ) );
led->led_floor = .1; RegisterValue( "led_floor", PAFLOAT, &led->led_floor, sizeof( led->led_floor ) );
led->light_siding = 1.9;RegisterValue( "light_siding", PAFLOAT, &led->light_siding, sizeof( led->light_siding ) );
led->qtyamp = 20; RegisterValue( "qtyamp", PAFLOAT, &led->qtyamp, sizeof( led->qtyamp ) );
led->timebased = 1; RegisterValue( "timebased", PAINT, &led->timebased, sizeof( led->timebased ) );
led->outgamma = 1.0; RegisterValue( "outgamma", PAFLOAT, &led->outgamma, sizeof( led->outgamma ) );
led->snakey = 0; RegisterValue( "snakey", PAINT, &led->snakey, sizeof( led->snakey ) );

View file

@ -24,6 +24,7 @@ struct LEDOutDriver
float last_led_amp[MAX_LEDS];
int steady_bright;
float led_floor;
float outgamma;
float led_limit; //Maximum brightness
float satamp;
int lastadvance;
@ -177,14 +178,13 @@ static void LEDUpdate(void * id, struct NoteFinder*nf)
if( sendsat > led->led_limit ) sendsat = led->led_limit;
int r = CCtoHEX( led->last_led_pos[i], 1.0, sendsat );
int r = CCtoHEX( led->last_led_pos[i], 1.0, pow( sendsat, led->outgamma ) );
OutLEDs[i*3+0] = r & 0xff;
OutLEDs[i*3+1] = (r>>8) & 0xff;
OutLEDs[i*3+2] = (r>>16) & 0xff;
OutLEDs[i*3+0] = (r>>24) & 0xff;
OutLEDs[i*3+1] = (r>>16) & 0xff;
OutLEDs[i*3+2] = (r>>8) & 0xff;
}
if( led->is_loop )
{
for( i = 0; i < led->total_leds; i++ )
@ -206,6 +206,7 @@ static void LEDParams(void * id )
led->is_loop = 0; RegisterValue( "is_loop", PAINT, &led->is_loop, sizeof( led->is_loop ) );
led->steady_bright = 1; RegisterValue( "steady_bright", PAINT, &led->steady_bright, sizeof( led->steady_bright ) );
led->led_limit = 1; RegisterValue( "led_limit", PAFLOAT, &led->led_limit, sizeof( led->led_limit ) );
led->outgamma = 1.0; RegisterValue( "outgamma", PAFLOAT, &led->outgamma, sizeof( led->outgamma ) );
printf( "Found LEDs for output. leds=%d\n", led->total_leds );

View file

@ -43,18 +43,16 @@ static void LEDUpdate(void * id, struct NoteFinder*nf)
}
}
float sendsat = selected_amp;
if( sendsat > 1 ) sendsat = 1;
int r = CCtoHEX( selected_note, 1.0, sendsat );
//Advance the LEDs to this position when outputting the values.
for( i = 0; i < led->total_leds; i++ )
{
float sendsat = selected_amp;
if( sendsat > 1 ) sendsat = 1;
int r = CCtoHEX( selected_note, 1.0, sendsat );
OutLEDs[i*3+0] = r & 0xff;
OutLEDs[i*3+1] = (r>>8) & 0xff;
OutLEDs[i*3+2] = (r>>16) & 0xff;
OutLEDs[i*3+0] = (r>>24) & 0xff;
OutLEDs[i*3+1] = (r>>16) & 0xff;
OutLEDs[i*3+2] = (r>>8) & 0xff;
}
}

View file

@ -25,6 +25,7 @@ struct DPODriver
{
int xn;
int yn;
float outgamma;
float cutoff;
float satamp;
float amppow; //For amplitudes
@ -126,12 +127,13 @@ static void DPOUpdate(void * id, struct NoteFinder*nf)
{
float sat = nf->note_amplitudes_out[bestmatch] * d->satamp;
if( sat > 1.0 ) sat = 1.0;
color = CCtoHEX( nf->note_positions[bestmatch] / nf->freqbins, 1.0, sat );
float note_color = nf->note_positions[bestmatch] / nf->freqbins;
color = CCtoHEX( note_color, 1.0, pow( sat, d->outgamma ) );
}
OutLEDs[led*3+0] = color & 0xff;
OutLEDs[led*3+1] = ( color >> 8 ) & 0xff;
OutLEDs[led*3+2] = ( color >> 16 ) & 0xff;
OutLEDs[led*3+0] = ( color >> 24 ) & 0xff;
OutLEDs[led*3+1] = ( color >> 16 ) & 0xff;
OutLEDs[led*3+2] = ( color >> 8 ) & 0xff;
led++;
}
}
@ -145,7 +147,7 @@ static void DPOParams(void * id )
d->yn = 90; RegisterValue( "lighty", PAINT, &d->yn, sizeof( d->yn ) );
d->cutoff = .01; RegisterValue( "Voronoi_cutoff", PAFLOAT, &d->cutoff, sizeof( d->cutoff ) );
d->satamp = 5; RegisterValue( "satamp", PAFLOAT, &d->satamp, sizeof( d->satamp ) );
d->outgamma = 1.0; RegisterValue( "outgamma", PAFLOAT, &d->outgamma, sizeof( d->outgamma ) );
d->amppow = 2.51; RegisterValue( "amppow", PAFLOAT, &d->amppow, sizeof( d->amppow ) );
d->distpow = 1.5; RegisterValue( "distpow", PAFLOAT, &d->distpow, sizeof( d->distpow ) );

View file

@ -107,7 +107,7 @@ void StartRecording( struct RecorderPlugin * rp )
}
static void RecordEvent(void * v, int samples, float * samps, int channel_ct)
static void RecordEvent(void * v, int samples, short * samps, int channel_ct)
{
struct RecorderPlugin * rp = (struct RecorderPlugin*)v;
@ -115,7 +115,7 @@ static void RecordEvent(void * v, int samples, float * samps, int channel_ct)
if( rp->DunBoop || !rp->fPlay )
{
int r = fwrite( samps, channel_ct * sizeof( float ), samples, rp->fRec );
int r = fwrite( samps, channel_ct * sizeof( short ), samples, rp->fRec );
if( r != samples )
{
StopRecording( rp );
@ -123,12 +123,12 @@ static void RecordEvent(void * v, int samples, float * samps, int channel_ct)
}
}
static void PlaybackEvent(void * v, int samples, float * samps, int channel_ct)
static void PlaybackEvent(void * v, int samples, short * samps, int channel_ct)
{
struct RecorderPlugin * rp = (struct RecorderPlugin*)v;
if( !rp->fPlay ) return;
int r = fread( samps, channel_ct * sizeof( float ), samples, rp->fPlay );
int r = fread( samps, channel_ct * sizeof( short ), samples, rp->fPlay );
if( r != samples )
{
StopRecording( rp );
@ -143,7 +143,7 @@ static void PlaybackEvent(void * v, int samples, float * samps, int channel_ct)
else
force_white = 0;
int r = fwrite( samps, channel_ct * sizeof( float ), samples, rp->fRec );
int r = fwrite( samps, channel_ct * sizeof( short ), samples, rp->fRec );
if( r != samples )
{
StopRecording( rp );

View file

@ -1,236 +0,0 @@
//Copyright (c) 2011 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose.
//Portion from: http://en.wikibooks.org/wiki/Windows_Programming/Window_Creation
#include "DrawFunctions.h"
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h> //for alloca
static HINSTANCE lhInstance;
static HWND lsHWND;
static HDC lsHDC;
static HBITMAP lsBackBitmap;
static HDC lsWindowHDC;
static HBRUSH lsHBR;
static HPEN lsHPEN;
static HBRUSH lsClearBrush;
static unsigned int lsLastWidth;
static unsigned int lsLastHeight;
static void InternalHandleResize()
{
DeleteObject( lsBackBitmap );
lsBackBitmap = CreateCompatibleBitmap( lsHDC, lsLastWidth, lsLastHeight );
SelectObject( lsHDC, lsBackBitmap );
}
uint32_t CNFGColor( uint32_t RGB )
{
CNFGLastColor = RGB;
DeleteObject( lsHBR );
lsHBR = CreateSolidBrush( RGB );
SelectObject( lsHDC, lsHBR );
DeleteObject( lsHPEN );
lsHPEN = CreatePen( PS_SOLID, 0, RGB );
SelectObject( lsHDC, lsHPEN );
return RGB;
}
void CNFGTackSegment( short x1, short y1, short x2, short y2 )
{
POINT pt[2] = { {x1, y1}, {x2, y2} };
Polyline( lsHDC, pt, 2 );
SetPixel( lsHDC, x1, y1, CNFGLastColor );
SetPixel( lsHDC, x2, y2, CNFGLastColor );
}
void CNFGTackRectangle( short x1, short y1, short x2, short y2 )
{
RECT r;
if( x1 < x2 ) { r.left = x1; r.right = x2; }
else { r.left = x2; r.right = x1; }
if( y1 < y2 ) { r.top = y1; r.bottom = y2; }
else { r.top = y2; r.bottom = y1; }
FillRect( lsHDC, &r, lsHBR );
}
void CNFGClearFrame()
{
RECT r = { 0, 0, lsLastWidth, lsLastHeight };
DeleteObject( lsClearBrush );
lsClearBrush = CreateSolidBrush( CNFGBGColor );
SelectObject( lsHDC, lsClearBrush );
FillRect( lsHDC, &r, lsClearBrush );
}
void CNFGSwapBuffers()
{
int thisw, thish;
RECT r;
BitBlt( lsWindowHDC, 0, 0, lsLastWidth, lsLastHeight, lsHDC, 0, 0, SRCCOPY );
UpdateWindow( lsHWND );
//Check to see if the window is closed.
if( !IsWindow( lsHWND ) )
{
exit( 0 );
}
GetClientRect( lsHWND, &r );
thisw = r.right - r.left;
thish = r.bottom - r.top;
if( thisw != lsLastWidth || thish != lsLastHeight )
{
lsLastWidth = thisw;
lsLastHeight = thish;
InternalHandleResize();
}
}
void CNFGTackPoly( RDPoint * points, int verts )
{
int i;
POINT * t = (POINT*)alloca( sizeof( POINT ) * verts );
for( i = 0; i < verts; i++ )
{
t[i].x = points[i].x;
t[i].y = points[i].y;
}
Polygon( lsHDC, t, verts );
}
void CNFGTackPixel( short x1, short y1 )
{
SetPixel( lsHDC, x1, y1, CNFGLastColor );
}
void CNFGGetDimensions( short * x, short * y )
{
*x = lsLastWidth;
*y = lsLastHeight;
}
//This was from the article
LRESULT CALLBACK MyWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
//This was from the article, too... well, mostly.
void CNFGSetup( const char * name_of_window, int width, int height )
{
static LPSTR szClassName = "MyClass";
RECT client, window;
WNDCLASS wnd;
int w, h, wd, hd;
HINSTANCE hInstance = GetModuleHandle(NULL);
lsLastWidth = width;
lsLastHeight = height;
wnd.style = CS_HREDRAW | CS_VREDRAW; //we will explain this later
wnd.lpfnWndProc = MyWndProc;
wnd.cbClsExtra = 0;
wnd.cbWndExtra = 0;
wnd.hInstance = hInstance;
wnd.hIcon = LoadIcon(NULL, IDI_APPLICATION); //default icon
wnd.hCursor = LoadCursor(NULL, IDC_ARROW); //default arrow mouse cursor
wnd.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
wnd.lpszMenuName = NULL; //no menu
wnd.lpszClassName = szClassName;
if(!RegisterClass(&wnd)) //register the WNDCLASS
{
MessageBox(NULL, "This Program Requires Windows NT", "Error", MB_OK);
}
lsHWND = CreateWindow(szClassName,
name_of_window, //name_of_window,
WS_OVERLAPPEDWINDOW, //basic window style
CW_USEDEFAULT,
CW_USEDEFAULT, //set starting point to default value
lsLastWidth,
lsLastHeight, //set all the dimensions to default value
NULL, //no parent window
NULL, //no menu
hInstance,
NULL); //no parameters to pass
lsWindowHDC = GetDC( lsHWND );
lsHDC = CreateCompatibleDC( lsWindowHDC );
lsBackBitmap = CreateCompatibleBitmap( lsWindowHDC, lsLastWidth, lsLastHeight );
SelectObject( lsHDC, lsBackBitmap );
lsClearBrush = CreateSolidBrush( CNFGBGColor );
lsHBR = CreateSolidBrush( 0xFFFFFF );
lsHPEN = CreatePen( PS_SOLID, 0, 0xFFFFFF );
ShowWindow(lsHWND, 1); //display the window on the screen
//Once set up... we have to change the window's borders so we get the client size right.
GetClientRect( lsHWND, &client );
GetWindowRect( lsHWND, &window );
w = ( window.right - window.left);
h = ( window.bottom - window.top);
wd = w - client.right;
hd = h - client.bottom;
MoveWindow( lsHWND, window.left, window.top, lsLastWidth + wd, lsLastHeight + hd, 1 );
InternalHandleResize();
}
void WindowsTerm();
void CNFGHandleInput()
{
int ldown = 0;
MSG msg;
while( PeekMessage( &msg, lsHWND, 0, 0xFFFF, 1 ) )
{
TranslateMessage(&msg);
switch( msg.message )
{
case WM_QUIT:
case WM_DESTROY:
case WM_CLOSE:
printf( "Close\n" );
WindowsTerm();
TerminateProcess( 0, 0 );
break;
case WM_MOUSEMOVE:
HandleMotion( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, ( (msg.wParam & 0x01)?1:0) | ((msg.wParam & 0x02)?2:0) | ((msg.wParam & 0x10)?4:0) );
break;
case WM_LBUTTONDOWN: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 1, 1 ); break;
case WM_RBUTTONDOWN: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 2, 1 ); break;
case WM_MBUTTONDOWN: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 3, 1 ); break;
case WM_LBUTTONUP: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 1, 0 ); break;
case WM_RBUTTONUP: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 2, 0 ); break;
case WM_MBUTTONUP: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 3, 0 ); break;
case WM_KEYDOWN:
case WM_KEYUP:
HandleKey( tolower( msg.wParam ), (msg.message==WM_KEYDOWN) );
break;
default:
DispatchMessage(&msg);
break;
}
}
}

View file

@ -1,258 +0,0 @@
//Copyright (c) 2011 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose.
//portions from
//http://www.xmission.com/~georgeps/documentation/tutorials/Xlib_Beginner.html
#define HAS_XINERAMA
#include "DrawFunctions.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
#ifdef HAS_XINERAMA
#include <X11/extensions/shape.h>
#include <X11/extensions/Xinerama.h>
#endif
#include <stdio.h>
#include <stdlib.h>
XWindowAttributes CNFGWinAtt;
Display *CNFGDisplay;
Window CNFGWindow;
Pixmap CNFGPixmap;
GC CNFGGC;
GC CNFGWindowGC;
int FullScreen = 0;
void CNFGGetDimensions( short * x, short * y )
{
*x = CNFGWinAtt.width;
*y = CNFGWinAtt.height;
}
static void InternalLinkScreenAndGo( const char * WindowName )
{
XGetWindowAttributes( CNFGDisplay, CNFGWindow, &CNFGWinAtt );
XSelectInput (CNFGDisplay, CNFGWindow, KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | ExposureMask | PointerMotionMask );
XSetStandardProperties( CNFGDisplay, CNFGWindow, WindowName, WindowName, None, NULL, 0, NULL );
CNFGWindowGC = XCreateGC(CNFGDisplay, CNFGWindow, 0, 0);
CNFGPixmap = XCreatePixmap( CNFGDisplay, CNFGWindow, CNFGWinAtt.width, CNFGWinAtt.height, CNFGWinAtt.depth );
CNFGGC = XCreateGC(CNFGDisplay, CNFGPixmap, 0, 0);
}
void CNFGSetupFullscreen( const char * WindowName, int screen_no )
{
#ifdef HAS_XINERAMA
XineramaScreenInfo *screeninfo = NULL;
int screens;
int event_basep, error_basep, a, b;
CNFGDisplay = XOpenDisplay(NULL);
int screen = XDefaultScreen(CNFGDisplay);
int xpos, ypos;
if (!XShapeQueryExtension(CNFGDisplay, &event_basep, &error_basep))
{
fprintf( stderr, "X-Server does not support shape extension" );
exit( 1 );
}
Visual * visual = DefaultVisual(CNFGDisplay, screen);
CNFGWinAtt.depth = DefaultDepth(CNFGDisplay, screen);
if (XineramaQueryExtension(CNFGDisplay, &a, &b ) &&
(screeninfo = XineramaQueryScreens(CNFGDisplay, &screens)) &&
XineramaIsActive(CNFGDisplay) && screen_no >= 0 &&
screen_no < screens ) {
CNFGWinAtt.width = screeninfo[screen_no].width;
CNFGWinAtt.height = screeninfo[screen_no].height;
xpos = screeninfo[screen_no].x_org;
ypos = screeninfo[screen_no].y_org;
} else
{
CNFGWinAtt.width = XDisplayWidth(CNFGDisplay, screen);
CNFGWinAtt.height = XDisplayHeight(CNFGDisplay, screen);
xpos = 0;
ypos = 0;
}
if (screeninfo)
XFree(screeninfo);
XSetWindowAttributes setwinattr;
setwinattr.override_redirect = 1;
setwinattr.save_under = 1;
setwinattr.event_mask = StructureNotifyMask | SubstructureNotifyMask | ExposureMask | ButtonPressMask | ButtonReleaseMask | ButtonPressMask | PointerMotionMask | ButtonMotionMask | EnterWindowMask | LeaveWindowMask |KeyPressMask |KeyReleaseMask | SubstructureNotifyMask | FocusChangeMask;
setwinattr.border_pixel = 0;
CNFGWindow = XCreateWindow(CNFGDisplay, XRootWindow(CNFGDisplay, screen),
xpos, ypos, CNFGWinAtt.width, CNFGWinAtt.height,
0, CNFGWinAtt.depth, InputOutput, visual, CWBorderPixel | CWEventMask | CWOverrideRedirect | CWSaveUnder, &setwinattr);
XMapWindow(CNFGDisplay, CNFGWindow);
XSetInputFocus( CNFGDisplay, CNFGWindow, RevertToParent, CurrentTime );
XFlush(CNFGDisplay);
FullScreen = 1;
//printf( "%d %d %d %d\n", xpos, ypos, CNFGWinAtt.width, CNFGWinAtt.height );
InternalLinkScreenAndGo( WindowName );
/*
setwinattr.override_redirect = 1;
XChangeWindowAttributes(
CNFGDisplay, CNFGWindow,
CWBorderPixel | CWEventMask | CWOverrideRedirect, &setwinattr);
*/
#else
CNFGSetup( WindowName, 640, 480 );
#endif
}
void CNFGSetup( const char * WindowName, int w, int h )
{
CNFGDisplay = XOpenDisplay(NULL);
XGetWindowAttributes( CNFGDisplay, RootWindow(CNFGDisplay, 0), &CNFGWinAtt );
int depth = CNFGWinAtt.depth;
CNFGWindow = XCreateWindow(CNFGDisplay, RootWindow(CNFGDisplay, 0), 1, 1, w, h, 0, depth, InputOutput, CopyFromParent, 0, 0 );
XMapWindow(CNFGDisplay, CNFGWindow);
XFlush(CNFGDisplay);
InternalLinkScreenAndGo( WindowName );
}
void CNFGHandleInput()
{
static int ButtonsDown;
XEvent report;
int bKeyDirection = 1;
int r;
while( (r=XCheckMaskEvent( CNFGDisplay, KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | ExposureMask | PointerMotionMask , &report )) )
{
// XEvent nev;
// XPeekEvent(CNFGDisplay, &nev);
//printf( "EVENT %d\n", report.type );
//XMaskEvent(CNFGDisplay, KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | ExposureMask, &report);
bKeyDirection = 1;
switch (report.type)
{
case NoExpose:
break;
case Expose:
XGetWindowAttributes( CNFGDisplay, CNFGWindow, &CNFGWinAtt );
if( CNFGPixmap ) XFreePixmap( CNFGDisplay, CNFGPixmap );
CNFGPixmap = XCreatePixmap( CNFGDisplay, CNFGWindow, CNFGWinAtt.width, CNFGWinAtt.height, CNFGWinAtt.depth );
if( CNFGGC ) XFreeGC( CNFGDisplay, CNFGGC );
CNFGGC = XCreateGC(CNFGDisplay, CNFGPixmap, 0, 0);
break;
case KeyRelease:
bKeyDirection = 0;
case KeyPress:
HandleKey( XLookupKeysym(&report.xkey, 0), bKeyDirection );
break;
case ButtonRelease:
bKeyDirection = 0;
case ButtonPress:
HandleButton( report.xbutton.x, report.xbutton.y, report.xbutton.button, bKeyDirection );
ButtonsDown = (ButtonsDown & (~(1<<report.xbutton.button))) | ( bKeyDirection << report.xbutton.button );
//Intentionall fall through -- we want to send a motion in event of a button as well.
case MotionNotify:
HandleMotion( report.xmotion.x, report.xmotion.y, ButtonsDown>>1 );
break;
default:
printf( "Event: %d\n", report.type );
}
}
}
void CNFGUpdateScreenWithBitmap( unsigned long * data, int w, int h )
{
static XImage *xi;
static int depth;
static int lw, lh;
if( !xi )
{
int screen = DefaultScreen(CNFGDisplay);
// Visual * visual = DefaultVisual(CNFGDisplay, screen);
depth = DefaultDepth(CNFGDisplay, screen)/8;
// xi = XCreateImage(CNFGDisplay, DefaultVisual( CNFGDisplay, DefaultScreen(CNFGDisplay) ), depth*8, ZPixmap, 0, (char*)data, w, h, 32, w*4 );
// lw = w;
// lh = h;
}
if( lw != w || lh != h )
{
if( xi ) free( xi );
xi = XCreateImage(CNFGDisplay, DefaultVisual( CNFGDisplay, DefaultScreen(CNFGDisplay) ), depth*8, ZPixmap, 0, (char*)data, w, h, 32, w*4 );
lw = w;
lh = h;
}
// ls = lw * lh;
XPutImage(CNFGDisplay, CNFGWindow, CNFGWindowGC, xi, 0, 0, 0, 0, w, h );
}
#ifndef RASTERIZER
uint32_t CNFGColor( uint32_t RGB )
{
unsigned char red = RGB & 0xFF;
unsigned char grn = ( RGB >> 8 ) & 0xFF;
unsigned char blu = ( RGB >> 16 ) & 0xFF;
CNFGLastColor = RGB;
unsigned long color = (red<<16)|(grn<<8)|(blu);
XSetForeground(CNFGDisplay, CNFGGC, color);
return color;
}
void CNFGClearFrame()
{
XGetWindowAttributes( CNFGDisplay, CNFGWindow, &CNFGWinAtt );
XSetForeground(CNFGDisplay, CNFGGC, CNFGColor(CNFGBGColor) );
XFillRectangle(CNFGDisplay, CNFGPixmap, CNFGGC, 0, 0, CNFGWinAtt.width, CNFGWinAtt.height );
}
void CNFGSwapBuffers()
{
XCopyArea(CNFGDisplay, CNFGPixmap, CNFGWindow, CNFGWindowGC, 0,0,CNFGWinAtt.width,CNFGWinAtt.height,0,0);
XFlush(CNFGDisplay);
if( FullScreen )
XSetInputFocus( CNFGDisplay, CNFGWindow, RevertToParent, CurrentTime );
}
void CNFGTackSegment( short x1, short y1, short x2, short y2 )
{
XDrawLine( CNFGDisplay, CNFGPixmap, CNFGGC, x1, y1, x2, y2 );
XDrawPoint( CNFGDisplay, CNFGPixmap, CNFGGC, x2, y2 );
}
void CNFGTackPixel( short x1, short y1 )
{
XDrawPoint( CNFGDisplay, CNFGPixmap, CNFGGC, x1, y1 );
}
void CNFGTackRectangle( short x1, short y1, short x2, short y2 )
{
XFillRectangle(CNFGDisplay, CNFGPixmap, CNFGGC, x1, y1, x2-x1, y2-y1 );
}
void CNFGTackPoly( RDPoint * points, int verts )
{
XFillPolygon(CNFGDisplay, CNFGPixmap, CNFGGC, (XPoint *)points, verts, Convex, CoordModeOrigin );
}
#endif

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<manifest xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android" package="org.cnlohr.colorchord"
android:versionCode="8">
<uses-sdk android:minSdkVersion="22"
android:targetSdkVersion="28" />
<uses-permission android:name="android.permission.SET_RELEASE_APP"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<application android:debuggable="false" android:hasCode="false" android:label="colorchord" tools:replace="android:icon,android:theme,android:allowBackup,label" android:icon="@mipmap/icon" > <!--android:requestLegacyExternalStorage="true" Not needed til Android 29 -->
<activity android:configChanges="keyboardHidden|orientation" android:label="colorchord" android:name="android.app.NativeActivity">
<!-- This device filter seems to do nothing at all! If you figure out how to use it or what it does, let me know!! -->
<intent-filter>
<action android:name="android.hardware.usb.action.ACTION_USB_DEVICE_ATTACHED" />
</intent-filter>
<meta-data android:name="android.hardware.usb.action.ACTION_USB_DEVICE_ATTACHED" android:resource="@xml/device_filter" />
<meta-data android:name="android.app.lib_name" android:value="colorchord"/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>

View file

@ -0,0 +1,129 @@
#include "outdrivers.h"
#include "notefinder.h"
#include <stdio.h>
#include "parameters.h"
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include "color.h"
#include "CNFG.h"
#include "rawdrawandroid/android_usb_devices.h"
#include <sys/ioctl.h>
#include <linux/usbdevice_fs.h>
#include <asm/byteorder.h>
extern int is_suspended;
#define MAX_LEDS_PER_NOTE 512
extern short screenx, screeny;
struct DTADriver
{
int leds;
};
char TensigralDebugStatus[8192];
int RequestPermissionOrGetConnectionFD( char * debug_status, uint16_t vid, uint16_t pid );
void DisconnectUSB(); //Disconnect from USB
extern jobject deviceConnection;
extern int deviceConnectionFD;
static void DTAUpdate(void * id, struct NoteFinder*nf)
{
struct DTADriver * d = (struct DTADriver*)id;
int i;
if( deviceConnectionFD == 0 )
{
RequestPermissionOrGetConnectionFD( TensigralDebugStatus, 0xabcd, 0xf410 );
}
if( !is_suspended )
{
CNFGPenX = 800;
CNFGPenY = 800;
CNFGColor( 0xffffff );
CNFGDrawText( TensigralDebugStatus, 2 );
}
if( !deviceConnectionFD ) return;
uint8_t packet[64];
if( deviceConnectionFD )
{
//This section does the crazy wacky stuff to actually split the LEDs into HID Packets and get them out the door... Carefully.
int byrem = d->leds*4; //OutLEDs[i*3+1]
int offset = 0;
int marker = 0;
for( i = 0; i < 2; i++ )
{
uint8_t sendbuf[64];
sendbuf[0] = (byrem > 60)?15:(byrem/4);
sendbuf[1] = offset;
// memcpy( sendbuf + 2, Colorbuf + offset*4, sendbuf[0]*4 );
int j;
for( j = 0; j < sendbuf[0]; j++ )
{
sendbuf[j*4+3] = OutLEDs[marker++];
sendbuf[j*4+2] = OutLEDs[marker++];
sendbuf[j*4+4] = OutLEDs[marker++];
sendbuf[j*4+5] = 0;
}
offset += sendbuf[0];
byrem -= sendbuf[0]*4;
if( byrem == 0 ) sendbuf[0] |= 0x80;
int tsend = 65; //Size of payload (must be 64+1 always)
//Ok this looks weird, because Android has a bulkTransfer function, but that has a TON of layers of misdirection before it just calls the ioctl.
struct usbdevfs_bulktransfer ctrl;
memset(&ctrl, 0, sizeof(ctrl));
ctrl.ep = 0x02; //Endpoint 0x02 is output endpoint.
ctrl.len = 64;
ctrl.data = sendbuf;
ctrl.timeout = 100;
int lastFDWrite = ioctl(deviceConnectionFD, USBDEVFS_BULK, &ctrl);
if( lastFDWrite < 0 )
{
DisconnectUSB();
break;
}
usleep(1000);
}
}
CNFGColor( 0xffffff );
}
static void DTAParams(void * id )
{
struct DTADriver * d = (struct DTADriver*)id;
d->leds = 9; RegisterValue( "leds", PAINT, &d->leds, sizeof( d->leds ) );
}
static struct DriverInstances * DisplayTensigralAndroid(const char * parameters)
{
struct DriverInstances * ret = malloc( sizeof( struct DriverInstances ) );
struct DTADriver * d = ret->id = malloc( sizeof( struct DTADriver ) );
memset( d, 0, sizeof( struct DTADriver ) );
ret->Func = DTAUpdate;
ret->Params = DTAParams;
DTAParams( d );
return ret;
}
REGISTER_OUT_DRIVER(DisplayTensigralAndroid);

View file

@ -0,0 +1,26 @@
APPNAME=colorchord
RAWDRAWANDROID=rawdrawandroid
PACKAGENAME?=org.cnlohr.$(APPNAME)
CFLAGS:=-I. -I.. -Irawdrawandroid/rawdraw -I../cnfa -I../../embeddedcommon \
-ffunction-sections -Os -s -DPRINTF_NO_OVERRIDDE -fvisibility=hidden \
-DRDALOGFNCB=example_log_function
ANDROIDVERSION=22
ANDROIDTARGET=28
LDFLAGS:=-s -lOpenSLES
CC_C:= ../main.c ../dft.c ../decompose.c ../filter.c ../color.c ../notefinder.c ../util.c ../hook.c ../outdrivers.c ../parameters.c ../chash.c ../OutputVoronoi.c ../OutputProminent.c ../DisplayArray.c ../OutputLinear.c ../DisplayPie.c ../DisplayNetwork.c ../../embeddedcommon/DFT32.c ../OutputCells.c ../configs.c
SRC:=rawdrawandroid/android_usb_devices.c DisplayTensigralAndroid.c $(CC_C)
#Uncomment to make all targets.
TARGETS:=makecapk/lib/armeabi-v7a/lib$(APPNAME).so #makecapk/lib/arm64-v8a/lib$(APPNAME).so makecapk/lib/x86/lib$(APPNAME).so makecapk/lib/x86_64/lib$(APPNAME).so
include rawdrawandroid/Makefile

View file

@ -0,0 +1,6 @@
This part of the project is baed on https://github.com/cnlohr/androidusbtest
You should be able to build this. If you give your app full permissions, and you
copy a copy of `colorchord-android.conf` into the external storage folder on your
Android device at the root level, it should pick it up and switch over to a pie
style display and output to the tensigral lamp.

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This file seems to do NOTHING AT ALL! If you have any idea how to use the device filters file, please let me know! -->
<resources>
<!-- <usb-device vendor-id="abcd" product-id="f410" /> --> <!-- class="3" subclass="255" protocol="255" -->
<usb-device vendor-id="43981" product-id="62480" />
</resources>

View file

@ -0,0 +1,14 @@
# Put configs here. Note when you reload the file, a lot of variables can be updated dynamically, but some like drivers cannot be changed at the time.
#Play with OutputCells and OutputLinear
amplify=4.0
outdrivers = DisplayPie,DisplayTensigralAndroid,OutputCells
leds = 20
light_siding = 2.2 #Turn this to ~1.9 for more uniformity, ~1.0 for less. (Linear driver looks good at 1, cells look good at 1.9)
timebased = 1
snakey = 1

@ -0,0 +1 @@
Subproject commit c9700f455e17ff3db9bb509c3f4cb54edfff81b0

View file

@ -10,6 +10,13 @@
#include <stdio.h>
#include <malloc.h>
#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) \
|| defined(_WIN32) || defined(_WIN64)
#ifndef strdup
#define strdup _strdup
#endif
#endif
#define I_AM_LITTLE (((union { unsigned x; unsigned char c; }){1}).c)
static unsigned long GetStrHash( const char * c )

1
colorchord2/cnfa Submodule

@ -0,0 +1 @@
Subproject commit d271e0196d81412032eeffa634a94a1aaf0060a7

View file

@ -9,23 +9,23 @@ uint32_t CCtoHEX( float note, float sat, float value )
{
float hue = 0.0;
note = fmodf( note, 1.0 );
note *= 12;
if( note < 4 )
note *= 12.0;
if( note < 4.0 )
{
//Needs to be YELLOW->RED
hue = (4 - note) / 24.0;
hue = (4.0 - note) / 24.0;
}
else if( note < 8 )
else if( note < 8.0 )
{
// [4] [8]
//Needs to be RED->BLUE
hue = ( 4 - note ) / 12.0;
hue = ( 4.0 - note ) / 12.0;
}
else
{
// [8] [12]
//Needs to be BLUE->YELLOW
hue = ( 12 - note ) / 8.0 + 1./6.;
hue = ( 12.0 - note ) / 8.0 + 1.0/6.0;
}
return HSVtoHEX( hue, sat, value );
}
@ -41,44 +41,44 @@ uint32_t CCtoHEX( float note, float sat, float value )
uint32_t HSVtoHEX( float hue, float sat, float value )
{
float pr = 0;
float pg = 0;
float pb = 0;
float pr = 0.0;
float pg = 0.0;
float pb = 0.0;
short ora = 0;
short og = 0;
short ob = 0;
short ora = 0.0;
short og = 0.0;
short ob = 0.0;
float ro = fmod( hue * 6, 6. );
float ro = fmod( hue * 6.0, 6.0 );
float avg = 0;
float avg = 0.0;
ro = fmod( ro + 6 + 1, 6 ); //Hue was 60* off...
ro = fmod( ro + 6.0 + 1.0, 6.0 ); //Hue was 60* off...
if( ro < 1 ) //yellow->red
if( ro < 1.0 ) //yellow->red
{
pr = 1;
pg = 1. - ro;
} else if( ro < 2 )
pr = 1.0;
pg = 1.0 - ro;
} else if( ro < 2.0 )
{
pr = 1;
pb = ro - 1.;
} else if( ro < 3 )
pr = 1.0;
pb = ro - 1.0;
} else if( ro < 3.0 )
{
pr = 3. - ro;
pb = 1;
} else if( ro < 4 )
pr = 3.0 - ro;
pb = 1.0;
} else if( ro < 4.0 )
{
pb = 1;
pg = ro - 3;
} else if( ro < 5 )
pb = 1.0;
pg = ro - 3.0;
} else if( ro < 5.0 )
{
pb = 5 - ro;
pg = 1;
pb = 5.0 - ro;
pg = 1.0;
} else
{
pg = 1;
pr = ro - 5;
pg = 1.0;
pr = ro - 5.0;
}
//Actually, above math is backwards, oops!
@ -90,9 +90,9 @@ uint32_t HSVtoHEX( float hue, float sat, float value )
avg += pg;
avg += pb;
pr = pr * sat + avg * (1.-sat);
pg = pg * sat + avg * (1.-sat);
pb = pb * sat + avg * (1.-sat);
pr = pr * sat + avg * (1.0-sat);
pg = pg * sat + avg * (1.0-sat);
pb = pb * sat + avg * (1.0-sat);
ora = pr * 255;
og = pb * 255;
@ -105,5 +105,6 @@ uint32_t HSVtoHEX( float hue, float sat, float value )
if( ob < 0 ) ob = 0;
if( ob > 255 ) ob = 255;
return (ob<<16) | (og<<8) | ora;
// Pack bits in RGBA format
return (ora << 24) | (og << 16) | (ob << 8) | 0xFF;
}

Binary file not shown.

View file

@ -19,6 +19,7 @@ void LoadFile( const char * filename )
char * buffer;
int r;
printf( "Loading file: %s\n", filename );
FILE * f = fopen( filename, "rb" );
if( !f )
{
@ -30,12 +31,12 @@ void LoadFile( const char * filename )
int size = ftell( f );
fseek( f, 0, SEEK_SET );
buffer = malloc( size + 1 );
r = fread( buffer, size, 1, f );
r = fread( buffer, 1, size, f);
fclose( f );
buffer[size] = 0;
if( r != 1 )
if( r != size )
{
fprintf( stderr, "Warning: %d bytes read. Expected: %d from file %s\n", r, size, filename );
fprintf( stderr, "Warning: %d bytes read. Expected: %d from file %s\n", r, size, filename );
}
else
{
@ -47,25 +48,76 @@ void LoadFile( const char * filename )
void SetEnvValues( int force )
{
int i;
static int ifcheck;
int hits = 0;
for( i = 0; i < InitialFileCount; i++ )
if( InitialFileCount )
{
double ft = OGGetFileTime( InitialFile[i] );
if( FileTimes[i] != ft )
//Only check one location per frame.
double ft = OGGetFileTime( InitialFile[ifcheck] );
if( FileTimes[ifcheck] != ft )
{
FileTimes[i] = ft;
FileTimes[ifcheck] = ft;
hits++;
}
ifcheck = ( ifcheck + 1 ) % InitialFileCount;
}
if( !hits && !force ) return;
//Otherwise, something changed.
#ifdef ANDROID
SetParametersFromString( "cpu_autolimit=1" );
SetParametersFromString( "set_screenx=720" );
SetParametersFromString( "set_screeny=480" );
SetParametersFromString( "buffer=384" );
SetParametersFromString( "play=0" );
SetParametersFromString( "rec=1" );
SetParametersFromString( "channels=2" );
SetParametersFromString( "samplerate=44100" );
SetParametersFromString( "sourcename=default" );
SetParametersFromString( "amplify=2.0" );
SetParametersFromString( "base_hz=55" );
LoadFile( InitialFile[0] );
SetParametersFromString( "dft_iir=0.6" );
SetParametersFromString( "dft_q=20.0000" );
SetParametersFromString( "dft_speedup=1000.0000" );
SetParametersFromString( "octaves=5" );
for( i = 1; i < gargc; i++ )
SetParametersFromString( "do_progressive_dft=4" );
SetParametersFromString( "filter_iter=2" );
SetParametersFromString( "filter_strength=.5" );
SetParametersFromString( "freqbins = 24" );
SetParametersFromString( "do_progressive_dft=4" );
SetParametersFromString( "note_attach_amp_iir=0.3500" );
SetParametersFromString( "note_attach_amp_iir2=0.250" );
SetParametersFromString( "note_combine_distance=0.5000" );
SetParametersFromString( "note_jumpability=1.8000" );
SetParametersFromString( "note_minimum_new_distribution_value=0.0200" );
SetParametersFromString( "note_out_chop=0.05000" );
SetParametersFromString( "outdrivers=OutputVoronoi,DisplayArray" );
SetParametersFromString( "note_attach_amp_iir2=0.250" );
SetParametersFromString( "lightx=32" );
SetParametersFromString( "lighty=60" );
SetParametersFromString( "fromsides=1" );
SetParametersFromString( "shape_cutoff=0.03" );
SetParametersFromString( "satamp=5.000" );
SetParametersFromString( "amppow=2.510" );
SetParametersFromString( "distpow=1.500" );
printf( "On Android, looking for configuration file in: %s\n", InitialFile[0] );
#endif
int i;
for( i = 0; i < InitialFileCount; i++ )
{
LoadFile( InitialFile[i] );
}
for( ; i < gargc; i++ )
{
if( strchr( gargv[i], '=' ) != 0 )
{
@ -101,9 +153,15 @@ void ProcessArgs()
void SetupConfigs()
{
#ifdef ANDROID
InitialFile[0] = "/sdcard/colorchord-android.txt";
InitialFile[1] = "/storage/emulated/0/colorchord-android.txt";
InitialFile[2] = "/sdcard/colorchord-android-overlay.txt";
InitialFile[3] = "/storage/emulated/0/colorchord-android-overlay.txt";
InitialFileCount = 4;
#else
InitialFile[0] = "default.conf";
#endif
ProcessArgs();
}

View file

@ -24,7 +24,7 @@ int DecomposeHistogram( float * histogram, int bins, struct NoteDists * out_dist
float this = histogram[i];
float next = histogram[(i+1)%bins];
if( prev > this || next > this ) continue;
if( prev >= this || next > this ) continue;
if( prev == this && next == this ) continue;
//i is at a peak...

View file

@ -8,65 +8,79 @@
cpu_autolimit = 1
#General GUI properties.
title = PA Test
title = ColorChord Default Configuration (Vornoi)
set_screenx = 720
set_screeny = 480
#Sound properties.
#######################################
# ColorChord audio driver properties. #
#######################################
# Colorchord now automatically picks most of the important audio driver settings automatically
# and will default to PULSE with ALSA as a fallback on Linux,
# On Windows it will default to WASAPI with winmm (WIN) as a fallback
# To force a specific driver use the "sound_source" property, the following values are valid
# sound_source:
# Linux: PULSE, ALSA
# Windows: WASAPI, WIN
# Android: ANDROID
# sound_source =
# The "devplay" property sets the playback device for CNFA (what speakers to go to) most uses for
# colorchord don't use audio output so this can be almost universally ignored
# devplay =
# The "devrecord" Sets the device to get audio from. This will default to monitoring your speaker
# output. For WASAPI, "default" searches for a mic, and "defaultRender" searches for your loopback.
# For PulseAudio @DEFAULT_SOURCE@ selects the default input (typically a microphone) and
# @DEFAULT_MONITOR@ selects the default loopback device.
# devrecord =
# For Linux you can use the following command to find valid devices to read from:
# pactl list | grep pci- | grep monitor
#-1 indicates left and right, 0 left, 1 right.
sample_channel = -1
# Other properties
buffer = 384
play = 0
rec = 1
channels = 2
samplerate = 44100
wininput = -1
#Compiled version will default this.
#sound_source = ALSA
#-1 indicates left and right, 0 left, 1 right.
sample_channel = -1
sourcename = default
#alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
#default
# alsa_output.pci-0000_00_1b.0.analog-stereo.monitor
#alsa_output.pci-0000_00_1f.3.analog-stereo.monitor << New laptop
#use pactl list | grep pci- | grep monitor
##################################
# General ColorChord properties. #
##################################
# How much to amplify the incoming signal.
amplify = 2.0
amplify = 10
# What is the base note? I.e. the lowest note.
# Note that it won't have very much impact until an octave up though!
base_hz = 55
base_hz = 55
# This is only used when dealing with the slow decompose (now defunct)
# decompose_iterations = 1000
# default_sigma = 1.4000
# DFT properties for the DFT up top.
# DFT properties for the DFT when Extra Debug is being shown.
dft_iir = 0.6
dft_q = 20.0000
dft_q = 60.0000
dft_speedup = 1000.0000
octaves = 5
octaves = 6
# Should we use a progressive DFT?
# What DFT method should we use?
# 0 = DFT Quick
# 1 = DFT Progressive
# 2 = DFT Progressive Integer
# 3 = DFT Progressive Integer Skippy
# 4 = Integer, 32-Bit, Progressive, Skippy.
do_progressive_dft = 4
filter_iter = 2
filter_strength = .5
do_progressive_dft = 0
# How many bins per octave to use?
freqbins = 24
freqbins = 64
# DFT Output IIR filter (Probably don't change these)
filter_iter = 2
filter_strength = .5
# For the final note information... How much to slack everything?
note_attach_amp_iir = 0.3500
@ -80,23 +94,16 @@ note_jumpability = 1.8000
note_minimum_new_distribution_value = 0.0200
note_out_chop = 0.05000
#compress_coefficient = 4.0
#compress_exponent = .5
##########################################
# Colorchord Display and Output Settings #
##########################################
#=======================================================================
#Outputs
This is a vornoi thing:
# This is a vornoi thing:
outdrivers = OutputVoronoi, DisplayArray
lightx = 64
lighty = 32
lightx = 128
lighty = 64
fromsides = 1
shape_cutoff = 0.03
satamp = 5.000
amppow = 2.510
distpow = 1.500

View file

@ -10,24 +10,25 @@
#ifndef CCEMBEDDED
void DoDFT( float * outbins, float * frequencies, int bins, float * databuffer, int place_in_data_buffer, int size_of_data_buffer, float q )
void DoDFT( float *outbins, float *frequencies, int bins, float *databuffer,
int place_in_data_buffer, int size_of_data_buffer, float q )
{
int i, j;
for( i = 0; i < bins; i++ )
for ( i = 0; i < bins; i++ )
{
float freq = frequencies[i];
float freq = frequencies[ i ];
float phi = 0;
int sampleplace = place_in_data_buffer;
float advance = 3.14159*2.0/freq;
float advance = 3.14159 * 2.0 / freq;
float binqtys = 0;
float binqtyc = 0;
for( j = 0; j <= freq * q; j++ )
for ( j = 0; j <= freq * q; j++ )
{
float sample = databuffer[sampleplace];
sampleplace = (sampleplace-1+size_of_data_buffer)%size_of_data_buffer;
//printf( "%d\n", sampleplace );
float sample = databuffer[ sampleplace ];
sampleplace = ( sampleplace - 1 + size_of_data_buffer ) % size_of_data_buffer;
// printf( "%d\n", sampleplace );
float sv = sin( phi ) * sample;
float cv = cos( phi ) * sample;
@ -38,36 +39,37 @@ void DoDFT( float * outbins, float * frequencies, int bins, float * databuffer,
}
float amp = sqrtf( binqtys * binqtys + binqtyc * binqtyc );
outbins[i] = amp / freq / q;
outbins[ i ] = amp / freq / q;
}
}
void DoDFTQuick( float * outbins, float * frequencies, int bins, const float * databuffer, int place_in_data_buffer, int size_of_data_buffer, float q, float speedup )
void DoDFTQuick( float *outbins, float *frequencies, int bins, const float *databuffer,
int place_in_data_buffer, int size_of_data_buffer, float q, float speedup )
{
int i, j;
for( i = 0; i < bins; i++ )
for ( i = 0; i < bins; i++ )
{
int flirts = 0;
float freq = frequencies[i];
float freq = frequencies[ i ];
float phi = 0;
int ftq = freq * q;
int ftq = freq * q;
int sampleplace = place_in_data_buffer;
float advance = 3.14159*2.0/freq;
float advance = 3.14159 * 2.0 / freq;
float binqtys = 0;
float binqtyc = 0;
int skip = floor( ftq / speedup );
if( skip == 0 ) skip = 1;
if ( skip == 0 ) skip = 1;
advance *= skip;
for( j = 0; j <= ftq; j += skip )
for ( j = 0; j <= ftq; j += skip )
{
float sample = databuffer[sampleplace];
sampleplace = (sampleplace-skip+size_of_data_buffer)%size_of_data_buffer;
//printf( "%d\n", sampleplace );
float sample = databuffer[ sampleplace ];
sampleplace = ( sampleplace - skip + size_of_data_buffer ) % size_of_data_buffer;
// printf( "%d\n", sampleplace );
float sv = sinf( phi ) * sample;
float cv = cosf( phi ) * sample;
@ -79,25 +81,24 @@ void DoDFTQuick( float * outbins, float * frequencies, int bins, const float * d
}
float amp = sqrtf( binqtys * binqtys + binqtyc * binqtyc );
outbins[i] = amp / freq / q * skip;
outbins[ i ] = amp / freq / q * skip;
}
}
////////////////////////////DFT Progressive is for embedded systems, primarily.
static float * gbinqtys;
static float * gbinqtyc;
static float * phis;
static float * gfrequencies;
static float * lastbins;
static float * advances;
static float * goutbins;
static int gbins;
static float gq;
static float gspeedup;
static float *gbinqtys;
static float *gbinqtyc;
static float *phis;
static float *gfrequencies;
static float *lastbins;
static float *advances;
static float *goutbins;
static int gbins;
static float gq;
static float gspeedup;
#define PROGIIR .005
@ -105,53 +106,53 @@ void HandleProgressive( float sample )
{
int i;
for( i = 0; i < gbins; i++ )
for ( i = 0; i < gbins; i++ )
{
float thiss = sinf( phis[i] ) * sample;
float thisc = cosf( phis[i] ) * sample;
float thiss = sinf( phis[ i ] ) * sample;
float thisc = cosf( phis[ i ] ) * sample;
float s = gbinqtys[i] = gbinqtys[i] * (1.-PROGIIR) + thiss * PROGIIR;
float c = gbinqtyc[i] = gbinqtyc[i] * (1.-PROGIIR) + thisc * PROGIIR;
float s = gbinqtys[ i ] = gbinqtys[ i ] * ( 1. - PROGIIR ) + thiss * PROGIIR;
float c = gbinqtyc[ i ] = gbinqtyc[ i ] * ( 1. - PROGIIR ) + thisc * PROGIIR;
phis[i] += advances[i];
if( phis[i] > 6.283 ) phis[i]-=6.283;
phis[ i ] += advances[ i ];
if ( phis[ i ] > 6.283 ) phis[ i ] -= 6.283;
goutbins[i] = sqrtf( s * s + c * c );
goutbins[ i ] = sqrtf( s * s + c * c );
}
}
void DoDFTProgressive( float * outbins, float * frequencies, int bins, const float * databuffer, int place_in_data_buffer, int size_of_data_buffer, float q, float speedup )
void DoDFTProgressive( float *outbins, float *frequencies, int bins, const float *databuffer,
int place_in_data_buffer, int size_of_data_buffer, float q, float speedup )
{
int i;
static int last_place;
if( gbins != bins )
if ( gbins != bins )
{
if( gbinqtys ) free( gbinqtys );
if( gbinqtyc ) free( gbinqtyc );
if( phis ) free( phis );
if( lastbins ) free( lastbins );
if( advances ) free( advances );
if ( gbinqtys ) free( gbinqtys );
if ( gbinqtyc ) free( gbinqtyc );
if ( phis ) free( phis );
if ( lastbins ) free( lastbins );
if ( advances ) free( advances );
gbinqtys = malloc( sizeof(float)*bins );
gbinqtyc = malloc( sizeof(float)*bins );
phis = malloc( sizeof(float)*bins );
lastbins = malloc( sizeof(float)*bins );
advances = malloc( sizeof(float)*bins );
memset( gbinqtys, 0, sizeof(float)*bins );
memset( gbinqtyc, 0, sizeof(float)*bins );
memset( phis, 0, sizeof(float)*bins );
memset( lastbins, 0, sizeof(float)*bins );
gbinqtys = malloc( sizeof( float ) * bins );
gbinqtyc = malloc( sizeof( float ) * bins );
phis = malloc( sizeof( float ) * bins );
lastbins = malloc( sizeof( float ) * bins );
advances = malloc( sizeof( float ) * bins );
memset( gbinqtys, 0, sizeof( float ) * bins );
memset( gbinqtyc, 0, sizeof( float ) * bins );
memset( phis, 0, sizeof( float ) * bins );
memset( lastbins, 0, sizeof( float ) * bins );
}
memcpy( outbins, lastbins, sizeof(float)*bins );
memcpy( outbins, lastbins, sizeof( float ) * bins );
for( i = 0; i < bins; i++ )
for ( i = 0; i < bins; i++ )
{
float freq = frequencies[i];
advances[i] = 3.14159*2.0/freq;
float freq = frequencies[ i ];
advances[ i ] = 3.14159 * 2.0 / freq;
}
gbins = bins;
@ -160,36 +161,28 @@ void DoDFTProgressive( float * outbins, float * frequencies, int bins, const flo
gspeedup = speedup;
gq = q;
place_in_data_buffer = (place_in_data_buffer+1)%size_of_data_buffer;
place_in_data_buffer = ( place_in_data_buffer + 1 ) % size_of_data_buffer;
int didrun = 0;
for( i = last_place; i != place_in_data_buffer; i = (i+1)%size_of_data_buffer )
for ( i = last_place; i != place_in_data_buffer; i = ( i + 1 ) % size_of_data_buffer )
{
float fin = ((float)((int)(databuffer[i] * 127))) / 127.0; //simulate 8-bit input (it looks FINE!)
float fin = ( (float)( (int)( databuffer[ i ] * 127 ) ) ) /
127.0; // simulate 8-bit input (it looks FINE!)
HandleProgressive( fin );
didrun = 1;
}
last_place = place_in_data_buffer;
if( didrun )
{
memcpy( lastbins, outbins, sizeof(float)*bins );
}
/* for( i = 0; i < bins; i++ )
{
printf( "%0.2f ", outbins[i]*100 );
}
printf( "\n" );*/
if ( didrun ) { memcpy( lastbins, outbins, sizeof( float ) * bins ); }
/* for( i = 0; i < bins; i++ )
{
printf( "%0.2f ", outbins[i]*100 );
}
printf( "\n" );*/
}
/////////////////////////////INTEGER DFT
@ -223,111 +216,111 @@ void HandleProgressiveInt( int8_t sample1, int8_t sample2 )
//Estimated 78 minimum instructions... So for two pairs each... just over 4ksps, theoretical.
//Running overall at ~2kHz. With GCC: YUCK! 102 cycles!!!
for( i = 0; i < gbins; i++ ) //Loop, fixed size = 3 + 2 cycles N/A
for( i = 0; i < gbins; i++ ) //Loop, fixed size = 3 + 2 cycles N/A
{
//12 cycles MIN
adv = *(ds++); //Read, indirect from RAM (and increment) 2+2 cycles 4
ipl = *(ds++); //Read, indirect from RAM (and increment) 2+2 cycles 4
adv = *(ds++); //Read, indirect from RAM (and increment) 2+2 cycles 4
ipl = *(ds++); //Read, indirect from RAM (and increment) 2+2 cycles 4
//13 cycles MIN
ipl += adv; //Advance, 16bit += 16bit, 1 + 1 cycles 2
localipl = (ipl>>8)<<1; //Select upper 8 bits 1 cycles 1 *** AS/IS: 4
ipl += adv; //Advance, 16bit += 16bit, 1 + 1 cycles 2
localipl = (ipl>>8)<<1; //Select upper 8 bits, 1 cycle 1 *** AS/IS: 4
st = &sintable[localipl];
s1 = *(st++); //Read s1 component out of table. 2+2 cycles 2
c1 = *st; //Read c1 component out of table. 2 cycles 2 *** AS/IS: 4
s1 = *(st++); //Read s1 component out of table. 2+2 cycles 2
c1 = *st; //Read c1 component out of table. 2 cycles 2 *** AS/IS: 4
ts = (s1 * sample1); // 8 x 8 multiply signed + copy R1 out. zero MSB ts 2 ->Deferred
tc = (c1 * sample1); // 8 x 8 multiply signed + copy R1 out. zero MSB tc 2 ->Deferred
ts = (s1 * sample1); // 8 x 8 multiply signed + copy R1 out. zero MSB ts 2 ->Deferred
tc = (c1 * sample1); // 8 x 8 multiply signed + copy R1 out. zero MSB tc 2 ->Deferred
//15 cycles MIN
ipl += adv; //Advance, 16bit += 16bit, 1 + 1 cycles 2
localipl = (ipl>>8)<<1; //Select upper 8 bits 1 cycles 1 *** AS/IS: 4
ipl += adv; //Advance, 16bit += 16bit, 1 + 1 cycles 2
localipl = (ipl>>8)<<1; //Select upper 8 bits 1 cycles 1 *** AS/IS: 4
// need to load Z with 'sintable' and add localipl 2
// need to load Z with 'sintable' and add localipl 2
st = &sintable[localipl];
s1 = *(st++); //Read s1 component out of table. 2 cycles 2
c1 = *st; //Read c1 component out of table. 2 cycles 2 *** AS/IS: 4
s1 = *(st++); //Read s1 component out of table. 2 cycles 2
c1 = *st; //Read c1 component out of table. 2 cycles 2 *** AS/IS: 4
ts += (s1 * sample2); // 8 x 8 multiply signed + add R1 out. 3 ->Deferred
tc += (c1 * sample2); // 8 x 8 multiply signed + add R1 out. 3 ->Deferred
ts += ( s1 * sample2 ); // 8 x 8 multiply signed + add R1 out. 3 ->Deferred
tc += ( c1 * sample2 ); // 8 x 8 multiply signed + add R1 out. 3 ->Deferred
//Add TS and TC to the datspace stuff. (24 instructions)
tmp1 = (*ds); //Read out, sin component. 4 Accurate.
tmp1 -= tmp1>>7; //Subtract from the MSB (with carry) 2 -> 6 AS/IS: 7+7 = 14
tmp1 += ts>>7; //Add MSBs with carry 2 -> 6 AS/IS: 6
// Add TS and TC to the datspace stuff. (24 instructions)
tmp1 = ( *ds ); // Read out, sin component. 4 Accurate
tmp1 -= tmp1 >> 7; // Subtract from the MSB (with carry) 2 -> 6 AS/IS: 7+7 = 14
tmp1 += ts >> 7; // Add MSBs with carry 2 -> 6 AS/IS: 6
*(ds++) = tmp1; //Store values back 4
*( ds++ ) = tmp1; // Store values back 4
tmp1 = *ds; //Read out, sin component. 4
tmp1 -= tmp1>>7; //Subtract from the MSB (with carry) 2 -> 6 AS/IS: 7+7 = 14
tmp1 += tc>>7; //Add MSBs with carry 2 -> 6 AS/IS: 6
tmp1 = *ds; // Read out, sin component. 4
tmp1 -= tmp1 >> 7; // Subtract from the MSB (with carry) 2 -> 6 AS/IS: 7+7 = 14
tmp1 += tc >> 7; // Add MSBs with carry 2 -> 6 AS/IS: 6
*ds++ = tmp1; //Store values back 4
*ds++ = tmp1; // Store values back 4
*(ds-3) = ipl; //Store values back 4 AS/IS: 6
*( ds - 3 ) = ipl; // Store values back 4 AS/IS: 6
//AS-IS: 8 loop overhead.
}
}
void DoDFTProgressiveInteger( float * outbins, float * frequencies, int bins, const float * databuffer, int place_in_data_buffer, int size_of_data_buffer, float q, float speedup )
void DoDFTProgressiveInteger( float *outbins, float *frequencies, int bins, const float *databuffer,
int place_in_data_buffer, int size_of_data_buffer, float q, float speedup )
{
int i;
static int last_place;
if( !donefirstrun )
if ( !donefirstrun )
{
donefirstrun = 1;
for( i = 0; i < 256; i++ )
for ( i = 0; i < 256; i++ )
{
sintable[i*2+0] = (int8_t)((sinf( i / 256.0 * 6.283 ) * 127.0));
sintable[i*2+1] = (int8_t)((cosf( i / 256.0 * 6.283 ) * 127.0));
sintable[ i * 2 + 0 ] = ( int8_t )( ( sinf( i / 256.0 * 6.283 ) * 127.0 ) );
sintable[ i * 2 + 1 ] = ( int8_t )( ( cosf( i / 256.0 * 6.283 ) * 127.0 ) );
}
}
if( gbins != bins )
if ( gbins != bins )
{
gbins = bins;
if( datspace ) free( datspace );
if ( datspace ) free( datspace );
datspace = malloc( bins * 2 * 4 );
}
for( i = 0; i < bins; i++ )
for ( i = 0; i < bins; i++ )
{
float freq = frequencies[i];
datspace[i*4] = 65536.0/freq;
float freq = frequencies[ i ];
datspace[ i * 4 ] = 65536.0 / freq;
}
for( i = last_place; i != ( place_in_data_buffer&0xffffe ); i = (i+2)%size_of_data_buffer )
for ( i = last_place; i != ( place_in_data_buffer & 0xffffe );
i = ( i + 2 ) % size_of_data_buffer )
{
int8_t ifr1 = (int8_t)( ((databuffer[i+0]) ) * 127 );
int8_t ifr2 = (int8_t)( ((databuffer[i+1]) ) * 127 );
// printf( "%d %d\n", i, place_in_data_buffer&0xffffe );
int8_t ifr1 = ( int8_t )( ( ( databuffer[ i + 0 ] ) ) * 127 );
int8_t ifr2 = ( int8_t )( ( ( databuffer[ i + 1 ] ) ) * 127 );
// printf( "%d %d\n", i, place_in_data_buffer&0xffffe );
HandleProgressiveInt( ifr1, ifr2 );
}
last_place = place_in_data_buffer&0xfffe;
last_place = place_in_data_buffer & 0xfffe;
//Extract bins.
for( i = 0; i < bins; i++ )
// Extract bins.
for ( i = 0; i < bins; i++ )
{
int16_t isps = datspace[i*4+2];
int16_t ispc = datspace[i*4+3];
int16_t mux = ( (isps/256) * (isps/256)) + ((ispc/256) * (ispc/256));
// printf( "%d (%d %d)\n", mux, isps, ispc );
outbins[i] = sqrt( mux )/100.0;
int16_t isps = datspace[ i * 4 + 2 ];
int16_t ispc = datspace[ i * 4 + 3 ];
int16_t mux = ( ( isps / 256 ) * ( isps / 256 ) ) + ( ( ispc / 256 ) * ( ispc / 256 ) );
// printf( "%d (%d %d)\n", mux, isps, ispc );
outbins[ i ] = sqrt( mux ) / 100.0;
}
// printf( "\n");
// printf( "\n");
}
#endif
@ -420,9 +413,12 @@ uint16_t Sdatspace[FIXBINS*4]; //(advances,places,isses,icses)
static uint8_t Sdo_this_octave[BINCYCLE];
static int16_t Saccum_octavebins[OCTAVES];
static uint8_t Swhichoctaveplace;
#ifndef INCLUDING_EMBEDDED
uint16_t embeddedbins[FIXBINS]; //This is updated every time the DFT hits the octavecount, or 1/32 updates.
#endif
//multiple definition of `embeddedbins'; dft.o (symbol from plugin):(.text+0x0): first defined here
//Had this issue when trying to build, commenting this lines out made the build successful
//#ifndef INCLUDING_EMBEDDED
//uint16_t embeddedbins[FIXBINS]; //This is updated every time the DFT hits the octavecount, or 1/32 updates.
//#endif
//From: http://stackoverflow.com/questions/1100090/looking-for-an-efficient-integer-square-root-algorithm-for-arm-thumb2
/**

View file

@ -21,8 +21,6 @@ amplify=.4
compress_coefficient = 4.0
compress_exponent = .5
sourcename = alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
fliprg = 1
skittlequantity = 24
timebased = 1

View file

@ -8,13 +8,7 @@ led_floor = .1 #Turn to .25 for more uniformity, .1 for less.
steady_bright = 0
rgby = 1
sourcename = alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
#sourcename = default
#alsa_output.pci-0000_01_00.1.hdmi-stereo.monitor
# alsa_output.pci-0000_01_00.1.hdmi-stereo-extra1.monitor
#alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
skipfirst = 0
firstval = 0
port = 7777

View file

@ -21,8 +21,6 @@ amplify=.5
lightx = 20
lighty = 20
sourcename = #alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
fliprg = 1
skittlequantity = 24

View file

@ -33,12 +33,6 @@ amplify=.35
lightx = 20
lighty = 20
sourcename =
# alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
#alsa_input.pci-0000_00_1f.3.analog-stereo
#alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
fliprg = 1
skittlequantity = 24

View file

@ -0,0 +1,32 @@
outdrivers = DisplayArray,DisplayNetwork,OutputCells
#OutputVoronoi
leds = 199
light_siding = 1.0 #Turn this to ~1.9 for more uniformity, ~1.0 for less.
satamp = 1.600
is_loop=0
led_floor = .1 #Turn to .25 for more uniformity, .1 for less.
#note_attach_amp_iir = .3 #.3000
#note_attach_amp_iir2 = .15 #.1500
#note_attach_freq_iir = .3 #0.3000
steady_bright = 0
#dft_iir = 0.0
#dft_q = 20.0000
#dft_speedup = 1000.0000
skipfirst = 0
firstval = 0
port = 7000
address = 192.168.43.179
#address = 192.168.0.245
slope=.10
amplify=.9
headless = 00
zigzag = 0
lightx = 13
lighty = 19
qtyamp = 50
timebased = 1
snakey = 0

View file

@ -16,9 +16,6 @@ lighty=1
ledoutamp = 1
sourcename = alsa_output.pci-0000_01_00.1.hdmi-stereo.monitor
bank1_size = 40
bank1_id = 8
bank2_size = 27

View file

@ -15,9 +15,6 @@ light_siding = 1.9
samplerate = 11025
buffer = 64
#sourcename = default
sourcename = alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
#default
do_write_method = 2
amplify = 2.5

View file

@ -0,0 +1,4 @@
do_progressive_dft = 3
samplerate = 8000
buffer = 64

View file

@ -15,19 +15,6 @@ set_screeny = 480
in_amplitude = 1.0
sample_channel = -1
sourcename = alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
#bluez_sink.40_EF_4C_CA_A4_5D.monitor
#alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
## alsa_output.pci-0000_00_1b.0.analog-stereo.monitor
# alsa_output.pci-0000_01_00.1.hdmi-stereo-extra2.monitor (On desktop)
#alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
#default
# alsa_output.pci-0000_00_1b.0.analog-stereo.monitor
#alsa_output.pci-0000_00_1f.3.analog-stereo.monitor << New laptop
#use pactl list | grep pci- | grep monitor
#How many bins a note can jump from frame to frame to be considered a slide.
#this is used to prevent notes from popping in and out a lot.

View file

@ -0,0 +1,45 @@
#For use with https://github.com/cnlohr/esp8266_dmx_explorer
outdrivers = DisplayArray,DisplayNetwork, OutputVoronoi
#OutputLinear
#OutputCells
#OutputLinear
#vornooi
lightx = 24
lighty = 7
fromsides = 1
outgamma=2.0
#cells
qtyamp=50
timebased=1
snakey = 1
steady_bright = 0
amppow = 1.00 #Higher willcull off not as bight things.
leds = 168
light_siding = 1.0 #Turn this to ~1.9 for more uniformity, ~1.0 for less.
satamp = 1.600
is_loop=1
led_floor = .1 #Turn to .25 for more uniformity, .1 for less.
note_attach_amp_iir = .2 #.3000
note_attach_amp_iir2 = .1 #.1500
note_attach_freq_iir = .3 #0.3000
steady_bright = 0
#dft_iir = 0.0
#dft_q = 20.0000
#dft_speedup = 1000.0000
skipfirst = 3
firstval = 0
flipgb = 1
port = 7777
#address = 192.168.4.1
address = esp8266dmx.local
slope=.10
amplify=.1
led_floor=.1

View file

@ -0,0 +1,43 @@
#For use with https://github.com/cnlohr/esp8266_dmx_explorer
outdrivers = DisplayPie,DisplayNetwork, OutputCells
#OutputLinear
#OutputCells
#OutputLinear
#cells
qtyamp=50
timebased=1
snakey = 1
steady_bright = 0
outgamma=3
leds = 168
light_siding = 1.0 #Turn this to ~1.9 for more uniformity, ~1.0 for less.
satamp = 1.600
is_loop=1
led_floor = .1 #Turn to .25 for more uniformity, .1 for less.
#note_attach_amp_iir = .3 #.3000
#note_attach_amp_iir2 = .15 #.1500
#note_attach_freq_iir = .3 #0.3000
steady_bright = 0
#dft_iir = 0.0
#dft_q = 20.0000
#dft_speedup = 1000.0000
skipfirst = 3
firstval = 0
flipgb = 1
port = 7777
#address = 192.168.4.1
address = esp8266dmx.local
slope=.10
amplify=.1
led_floor=.1
lightx = 20
lighty = 20

View file

@ -13,9 +13,6 @@ steady_bright = 0
#dft_q = 20.0000
#dft_speedup = 1000.0000
sourcename =
#alsa_output.pci-0000_00_1b.0.analog-stereo.monitor
# home: alsa_output.pci-0000_01_00.1.hdmi-stereo.monitor
skipfirst = 1
firstval = 0
port = 7777

View file

@ -12,9 +12,6 @@ steady_bright = 0
#dft_q = 20.0000
#dft_speedup = 1000.0000
sourcename = alsa_output.pci-0000_01_00.1.hdmi-stereo.monitor
# alsa_output.pci-0000_01_00.1.hdmi-stereo-extra1.monitor
#alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
skipfirst = 1
firstval = 0
port = 7777

View file

@ -12,11 +12,6 @@ steady_bright = 0
#dft_q = 20.0000
#dft_speedup = 1000.0000
sourcename = alsa_output.pci-0000_01_00.1.hdmi-stereo-extra1.monitor
#sourcename = alsa_output.pci-0000_00_1b.0.analog-stereo.monitor
#alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
skipfirst = 1
firstval = 1
port = 7777

View file

@ -7,7 +7,6 @@ lightx = 2
lighty = 1
light_siding = 1.6
note_attach_amp_iir = .3000
note_attach_amp_iir2 = .1500
note_attach_freq_iir = 0.3000

View file

@ -1,10 +1,10 @@
This is a vornoi thing:
outdrivers = DisplayArray, OutputProminent
lightx = 2
lighty = 2
leds = 4
fromsides = 1
outdrivers = DisplayArray, OutputCells
lightx = 3
lighty = 3
leds = 9
fromsides = 0
shape_cutoff = 0.03
satamp = 5.000
amppow = 2.510
@ -13,8 +13,6 @@ distpow = 1.500
samplerate = 11025
buffer = 64
sourcename = default
amplify = 2.5
note_attach_amp_iir = 0.9000
note_attach_amp_iir2 = 0.550

View file

@ -0,0 +1,100 @@
# This is the configuration file for colorchord.
# Most values are already defaulted in the software.
# This file is constantly checked for new versions.
# \r, and ; are used as terminators, so you can put
# multiple entries on the same line.
#Whether to limit the control loop to ~60ish FPS.
cpu_autolimit = 1
#General GUI properties.
title = ColorChord RadialPole Output
set_screenx = 480
set_screeny = 480
#Sound properties.
buffer = 1024
play = 0
rec = 1
channels = 2
samplerate = 44100
#######################################
# ColorChord audio driver properties. #
#######################################
# Colorchord now automatically picks most of the important audio driver settings automatically
# and will default to PULSE with ALSA as a fallback on Linux,
# On Windows it will default to WASAPI with winmm (WIN) as a fallback
# To force a specific driver use the "sound_source" property, the following values are valid
# sound_source:
# Linux: PULSE, ALSA
# Windows: WASAPI, WIN
# Android: ANDROID
# sound_source =
# The "devplay" property sets the playback device for CNFA (what speakers to go to) most uses for
# colorchord don't use audio output so this can be almost universally ignored
# devplay =
# The "devrecord" Sets the device to get audio from. This will default to monitoring your speaker
# output. For WASAPI, "default" searches for a mic, and "defaultRender" searches for your loopback.
# For PulseAudio @DEFAULT_SOURCE@ selects the default input (typically a microphone) and
# @DEFAULT_MONITOR@ selects the default loopback device.
# devrecord =
#-1 indicates left and right, 0 left, 1 right.
sample_channel = -1
##################################
# General ColorChord properties. #
##################################
# How much to amplify the incoming signal.
amplify = 2
#amplify = 10
# What is the base note? I.e. the lowest note.
# Note that it won't have very much impact until an octave up though!
base_hz = 55
# DFT properties for the DFT up top.
dft_iir = 0.6
dft_q = 60.0000
dft_speedup = 1000.0000
octaves = 6
# Should we use a progressive DFT?
# 0 = DFT Quick
# 1 = DFT Progressive
# 2 = DFT Progressive Integer
# 3 = DFT Progressive Integer Skippy
# 4 = Integer, 32-Bit, Progressive, Skippy.
do_progressive_dft = 0
filter_iter = 2
filter_strength = .5
# How many bins per octave to use?
freqbins = 64
# For the final note information... How much to slack everything?
note_attach_amp_iir = 0.3500
note_attach_amp_iir2 = 0.250
note_attach_freq_iir = 0.3000
#How many bins a note can jump from frame to frame to be considered a slide.
#this is used to prevent notes from popping in and out a lot.
note_combine_distance = 0.5000
note_jumpability = 1.8000
note_minimum_new_distribution_value = 0.0200
note_out_chop = 0.05000
##########################################
# Colorchord Display and Output Settings #
##########################################
# RadialPole Output driver
outdrivers = DisplayRadialPoles
radialscale = 2000
radialmode = 1

View file

@ -8,19 +8,6 @@ set_screeny = 480
in_amplitude = 3.0
sample_channel = -1
sourcename = alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
#sourcename = bluez_sink.40_EF_4C_CA_A4_5D.monitor
#alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
## alsa_output.pci-0000_00_1b.0.analog-stereo.monitor
# alsa_output.pci-0000_01_00.1.hdmi-stereo-extra2.monitor (On desktop)
#alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
#default
# alsa_output.pci-0000_00_1b.0.analog-stereo.monitor
#alsa_output.pci-0000_00_1f.3.analog-stereo.monitor << New laptop
#use pactl list | grep pci- | grep monitor
#How many bins a note can jump from frame to frame to be considered a slide.
#this is used to prevent notes from popping in and out a lot.

View file

@ -9,9 +9,6 @@ amppow = 3.5
distpow = 1.500
ledoutamp = 1
sourcename = alsa_output.pci-0000_01_00.1.hdmi-stereo-extra1.monitor
#alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
note_attach_amp_iir = .3000
note_attach_amp_iir2 = .1500
note_attach_freq_iir = 0.3000

View file

@ -0,0 +1,22 @@
# Normal Colorchord stuff
outdrivers = DisplayNetwork, OutputLinear
headless = 1
leds = 30
light_siding = 1.5 #Turn this to ~1.9 for more uniformity, ~1.0 for less.
cpu_autolimit_interval=.014
cpu_autolimit = 1
satamp = 1.600
is_loop=0
led_floor = .18
steady_bright = 0
fliprg=0
slope=.5
amplify=1
base_hz = 51.5000
# WLED Integration stuff
wled_realtime=1 #Enable WLED Interfacing params
port = 19446 #Default port for UDP Realtime
address = 192.168.0.24 #WLED Node IP
wled_timeout=2

File diff suppressed because it is too large Load diff

View file

@ -54,14 +54,14 @@ void UnhookKeyEvent( void (*KeyE)( void * v, int key, int down ), void * v )
struct SoundEvent
{
void (*SoundE)( void * v, int samples, float * samps, int channel_ct );
void (*SoundE)( void * v, int samples, short * samps, int channel_ct );
void * v;
};
struct SoundEvent SoundEvents[2][MAX_SOUND_EVENTS];
void SoundEventHappened( int samples, float * samps, int is_out, int channel_ct )
void SoundEventHappened( int samples, short * samps, int is_out, int channel_ct )
{
int i;
for( i = 0; i < MAX_SOUND_EVENTS; i++ )
@ -73,7 +73,7 @@ void SoundEventHappened( int samples, float * samps, int is_out, int channel_ct
}
}
void HookSoundInEvent( void (*SoundE)( void * v, int samples, float * samps, int channel_ct ), void * v, int is_out )
void HookSoundInEvent( void (*SoundE)( void * v, int samples, short * samps, int channel_ct ), void * v, int is_out )
{
int i;
for( i = 0; i < MAX_SOUND_EVENTS; i++ )
@ -87,7 +87,7 @@ void HookSoundInEvent( void (*SoundE)( void * v, int samples, float * samps, int
}
}
void UnhookSoundInEvent( void (*SoundE)( void * v, int samples, float * samps, int channel_ct ), void * v, int is_out )
void UnhookSoundInEvent( void (*SoundE)( void * v, int samples, short * samps, int channel_ct ), void * v, int is_out )
{
int i;
for( i = 0; i < MAX_SOUND_EVENTS; i++ )

View file

@ -11,9 +11,9 @@ void HookKeyEvent( void (*KeyEvent)( void * v, int key, int down ), void * v );
void UnhookKeyEvent( void (*KeyEvent)( void * v, int key, int down ), void * v );
void SoundEventHappened( int samples, float * samps, int channel_ct, int is_out );
void HookSoundInEvent( void (*SoundE)( void * v, int samples, float * samps, int channel_ct ), void * v, int is_out );
void UnhookSoundInEvent( void (*SoundE)( void * v, int samples, float * samps, int channel_ct ), void * v, int is_out );
void SoundEventHappened( int samples, short * samps, int channel_ct, int is_out );
void HookSoundInEvent( void (*SoundE)( void * v, int samples, short * samps, int channel_ct ), void * v, int is_out );
void UnhookSoundInEvent( void (*SoundE)( void * v, int samples, short * samps, int channel_ct ), void * v, int is_out );
#endif

View file

@ -1,5 +0,0 @@
do_progressive_dft = 3
samplerate = 8000
buffer = 64
sourcename = alsa_output.pci-0000_00_1b.0.analog-stereo.monitor

108
colorchord2/iso.conf Normal file
View file

@ -0,0 +1,108 @@
# This is the configuration file for colorchord.
# Most values are already defaulted in the software.
# This file is constantly checked for new versions.
# \r, and ; are used as terminators, so you can put
# multiple entries on the same line.
#Whether to limit the control loop to ~60ish FPS.
cpu_autolimit = 1
#General GUI properties.
title = PA Test
#set_screenx = 720
#set_screeny = 480
#Sound properties.
buffer = 384
play = 0
rec = 1
channels = 2
samplerate = 44100
wininput = 1
#Compiled version will default this.
#sound_source = ALSA
#-1 indicates left and right, 0 left, 1 right.
sample_channel = -1
sourcename = alsa_output.pci-0000_00_1b.0.analog-stereo.monitor
#alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
#default
# alsa_output.pci-0000_00_1b.0.analog-stereo.monitor
#alsa_output.pci-0000_00_1f.3.analog-stereo.monitor << New laptop
#use pactl list | grep pci- | grep monitor
##################################
# General ColorChord properties. #
##################################
# How much to amplify the incoming signal.
amplify = 5
# What is the base note? I.e. the lowest note.
# Note that it won't have very much impact until an octave up though!
base_hz = 55
# This is only used when dealing with the slow decompose (now defunct)
# decompose_iterations = 1000
# default_sigma = 1.4000
# DFT properties for the DFT up top.
dft_iir = 0.6
dft_q = 20.0000
dft_speedup = 1000.0000
octaves = 5
# Should we use a progressive DFT?
# 0 = DFT Quick
# 1 = DFT Progressive
# 2 = DFT Progressive Integer
# 3 = DFT Progressive Integer Skippy
# 4 = Integer, 32-Bit, Progressive, Skippy.
do_progressive_dft = 4fa
filter_iter = 2
filter_strength = .5
# How many bins per octave to use?
freqbins = 24
# For the final note information... How much to slack everything?
note_attach_amp_iir = 0.3500
note_attach_amp_iir2 = 0.250
note_attach_freq_iir = 0.3000
#How many bins a note can jump from frame to frame to be considered a slide.
#this is used to prevent notes from popping in and out a lot.
note_combine_distance = 0.5000
note_jumpability = 1.8000
note_minimum_new_distribution_value = 0.0200
note_out_chop = 0.05000
#compress_coefficient = 4.0
#compress_exponent = .5
#=======================================================================
#Outputs
outdrivers = DisplayUSBIsochronous, OutputVoronoi, DisplayArray
leds = 484
lightx = 11
lighty = 44
fromsides = 1
shape_cutoff = 0.03
satamp = 2.000
amppow = 2.5
distpow = 1.500
zigzag = 1
rot90 = 1
ledoutamp = .01
note_attach_amp_iir = .3000
note_attach_amp_iir2 = .1500
note_attach_freq_iir = 0.3000
steady_bright = 0

View file

@ -1,188 +1,362 @@
//Copyright 2015 <>< Charles Lohr under the ColorChord License.
// Copyright 2015-2020 <>< Charles Lohr under the ColorChord License.
#if defined( WINDOWS ) || defined( USE_WINDOWS ) || defined( WIN32 ) || defined( WIN64 ) || \
defined( _WIN32 ) || defined( _WIN64 )
#ifdef TCC
#include <winsock2.h>
#endif
#ifndef strdup
#define strdup _strdup
#endif
// define convenient macro to detect windows
#define IS_WINDOWS 1
#else
// this isn't windows
#define IS_WINDOWS 0
#endif
#include <ctype.h>
#include "color.h"
#include <math.h>
#include <stdio.h>
#include "sound.h"
#include "os_generic.h"
#include "DrawFunctions.h"
#include "configs.h"
#include "decompose.h"
#include "dft.h"
#include "filter.h"
#include "decompose.h"
#include <stdlib.h>
#include <string.h>
#include "hook.h"
#include "notefinder.h"
#include "os_generic.h"
#include "outdrivers.h"
#include "parameters.h"
#include "hook.h"
#include "configs.h"
#include <ctype.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct SoundDriver * sd;
#if defined(WIN32) || defined(USE_WINDOWS)
#include <winsock2.h>
#include <windows.h>
#define CNFG_IMPLEMENTATION
#include "CNFG.h"
#define ESCAPE_KEY 0x1B
#define CNFA_IMPLEMENTATION
#include "CNFA.h"
void WindowsTerm()
// Sound driver.
struct CNFADriver *sd;
int bQuitColorChord = 0;
#ifdef ANDROID
#include <android/log.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
void HandleDestroy()
{
CloseSound( sd );
bQuitColorChord = 1;
CNFAClose( sd );
}
#else
#endif
#define GENLINEWIDTH 89
#define GENLINES 67
char genlog[ ( GENLINEWIDTH + 1 ) * ( GENLINES + 1 ) + 2 ] = "log";
int genloglen;
int genloglines;
int genlinelen = 0;
int firstnewline = -1;
// Define application colors RGBA format
#define BACKGROUND_COLOR 0x000000ff
#define LINE_COLOR 0xffffffff
#define TEXT_COLOR 0xffffffff
// Text colors for the debug options at the bottom of the screen
#define ENABLED_COLOR 0xffffffff
#define DISABLED_COLOR 0x800000ff
void example_log_function( int readSize, char *buf )
{
static og_mutex_t *mt;
if ( !mt ) mt = OGCreateMutex();
OGLockMutex( mt );
for ( int i = 0; readSize && i <= readSize && buf[ i ]; i++ )
{
char c = buf[ i ];
if ( c == '\0' ) c = '\n';
if ( ( c != '\n' && genlinelen >= GENLINEWIDTH ) || c == '\n' )
{
genloglines++;
if ( genloglines >= GENLINES )
{
genloglen -= firstnewline + 1;
int offset = firstnewline;
firstnewline = -1;
int k;
for ( k = 0; k < genloglen; k++ )
{
if ( ( genlog[ k ] = genlog[ k + offset + 1 ] ) == '\n' && firstnewline < 0 )
firstnewline = k;
}
genlog[ k ] = 0;
genloglines--;
}
genlinelen = 0;
if ( c != '\n' )
{
genlog[ genloglen + 1 ] = 0;
genlog[ genloglen++ ] = '\n';
}
if ( firstnewline < 0 ) firstnewline = genloglen;
}
genlog[ genloglen + 1 ] = 0;
genlog[ genloglen++ ] = c;
if ( c != '\n' ) genlinelen++;
}
OGUnlockMutex( mt );
}
#if defined( WIN32 ) || defined( USE_WINDOWS )
#define ESCAPE_KEY 0x1B
void HandleDestroy()
{
CNFAClose( sd );
}
#else
#define ESCAPE_KEY 65307
// Stub function for Linux
void HandleDestroy()
{
}
#endif
float DeltaFrameTime = 0;
double Now = 0;
double Now = 0;
int is_suspended = 0;
int lastfps;
short screenx, screeny;
struct DriverInstances * outdriver[MAX_OUT_DRIVERS];
struct DriverInstances *outdriver[ MAX_OUT_DRIVERS ];
int headless = 0; REGISTER_PARAM( headless, PAINT );
int set_screenx = 640; REGISTER_PARAM( set_screenx, PAINT );
int set_screeny = 480; REGISTER_PARAM( set_screeny, PAINT );
char sound_source[ 16 ]; REGISTER_PARAM( sound_source, PABUFFER );
int cpu_autolimit = 1; REGISTER_PARAM( cpu_autolimit, PAINT );
float cpu_autolimit_interval = 0.016; REGISTER_PARAM( cpu_autolimit_interval, PAFLOAT );
int sample_channel = -1; REGISTER_PARAM( sample_channel, PAINT );
int showfps = 1; REGISTER_PARAM( showfps, PAINT );
int headless = 0; REGISTER_PARAM( headless, PAINT );
int set_screenx = 640; REGISTER_PARAM( set_screenx, PAINT );
int set_screeny = 480; REGISTER_PARAM( set_screeny, PAINT );
char sound_source[16]; REGISTER_PARAM( sound_source, PABUFFER );
int cpu_autolimit = 1; REGISTER_PARAM( cpu_autolimit, PAINT );
float cpu_autolimit_interval = 0.016; REGISTER_PARAM( cpu_autolimit_interval, PAFLOAT );
int sample_channel = -1;REGISTER_PARAM( sample_channel, PAINT );
int showfps = 0; REGISTER_PARAM( showfps, PAINT );
float in_amplitude = 1; REGISTER_PARAM( in_amplitude, PAFLOAT );
#if defined( ANDROID ) || defined( __android__ )
float in_amplitude = 2;
#else
float in_amplitude = 1;
#endif
REGISTER_PARAM( in_amplitude, PAFLOAT );
struct NoteFinder * nf;
struct NoteFinder *nf;
//Sound circular buffer
#define SOUNDCBSIZE 8096
// Sound circular buffer
#define SOUNDCBSIZE 8096
#define MAX_CHANNELS 2
double VisTimeEnd, VisTimeStart;
int soundhead = 0;
float sound[SOUNDCBSIZE];
int show_debug = 0;
int show_debug_basic = 1;
float sound[ SOUNDCBSIZE ];
int soundhead = 0;
int show_debug = 0;
int show_debug_basic = 0;
int gKey = 0;
extern int force_white;
void RecalcBaseHz()
{
nf->base_hz = 55 * pow( 2, gKey / 12.0 );
ChangeNFParameters( nf );
}
void HandleKey( int keycode, int bDown )
{
char c = toupper( keycode );
if( c == 'D' && bDown ) show_debug = !show_debug;
if( c == 'W' ) force_white = bDown;
if( c == '9' && bDown ) { gKey--; nf->base_hz = 55 * pow( 2, gKey / 12.0 ); ChangeNFParameters( nf ); }
if( c == '-' && bDown ) { gKey++; nf->base_hz = 55 * pow( 2, gKey / 12.0 ); ChangeNFParameters( nf ); }
if( c == '0' && bDown ) { gKey = 0; nf->base_hz = 55 * pow( 2, gKey / 12.0 ); ChangeNFParameters( nf ); }
if( c == 'E' && bDown ) show_debug_basic = !show_debug_basic;
if( c == 'K' && bDown ) DumpParameters();
#ifdef ANDROID
if ( keycode == 4 && bDown )
{
// Back button.
printf( "Back button pressed\n" );
AndroidSendToBack( 0 );
return;
}
#endif
if( keycode == ESCAPE_KEY ) exit( 0 );
if( c == 'W' ) force_white = bDown;
if( c == 'D' && bDown ) show_debug = !show_debug;
if( c == '9' && bDown ) { gKey--; RecalcBaseHz(); }
if( c == '-' && bDown ) { gKey++; RecalcBaseHz(); }
if( c == '0' && bDown ) { gKey = 0; RecalcBaseHz(); }
if( c == 'E' && bDown ) show_debug_basic = !show_debug_basic;
if( c == 'K' && bDown ) DumpParameters();
printf( "Key: %d -> %d\n", keycode, bDown );
KeyHappened( keycode, bDown );
}
// On Android we want a really basic GUI
void HandleButton( int x, int y, int button, int bDown )
{
printf( "Button: %d,%d (%d) -> %d\n", x, y, button, bDown );
if ( bDown )
{
if ( y < 800 )
{
if ( x < screenx / 3 )
gKey--;
else if ( x < ( screenx * 2 / 3 ) )
gKey = 0;
else
gKey++;
printf( "KEY: %d\n", gKey );
RecalcBaseHz();
}
}
}
void HandleMotion( int x, int y, int mask )
{
}
void SoundCB( float * out, float * in, int samplesr, int * samplesp, struct SoundDriver * sd )
void SoundCB( struct CNFADriver *sd, short *out, short *in, int framesp, int framesr )
{
int channelin = sd->channelsRec;
// int channelout = sd->channelsPlay;
//*samplesp = 0;
// int process_channels = (MAX_CHANNELS < channelin)?MAX_CHANNELS:channelin;
int channelout = sd->channelsPlay;
//Load the samples into a ring buffer. Split the channels from interleved to one per buffer.
int i;
int j;
for( i = 0; i < samplesr; i++ )
// Load the samples into a ring buffer. Split the channels from interleved to one per buffer.
if ( in )
{
if( out )
for ( int i = 0; i < framesr; i++ )
{
for( j = 0; j < channelin; j++ )
if ( sample_channel < 0 )
{
out[i*channelin+j] = 0;
float fo = 0;
for ( int j = 0; j < channelin; j++ )
{
float f = in[ i * channelin + j ] / 32767.;
if ( f >= -1 && f <= 1 )
fo += f;
else
fo += ( f > 0 ) ? 1 : -1;
}
fo /= channelin;
sound[ soundhead ] = fo * in_amplitude;
soundhead = ( soundhead + 1 ) % SOUNDCBSIZE;
}
}
if( sample_channel < 0 )
{
float fo = 0;
for( j = 0; j < channelin; j++ )
else
{
float f = in[i*channelin+j];
if( f >= -1 && f <= 1 )
{
fo += f;
}
else
{
fo += (f>0)?1:-1;
// printf( "Sound fault A %d/%d %d/%d %f\n", j, channelin, i, samplesr, f );
}
float f = in[ i * channelin + sample_channel ] / 32767.;
if ( f > 1 || f < -1 ) f = ( f > 0 ) ? 1 : -1;
sound[ soundhead ] = f * in_amplitude;
soundhead = ( soundhead + 1 ) % SOUNDCBSIZE;
}
fo /= channelin;
sound[soundhead] = fo*in_amplitude;
soundhead = (soundhead+1)%SOUNDCBSIZE;
}
else
{
float f = in[i*channelin+sample_channel];
if( f > 1 || f < -1 )
{
f = (f>0)?1:-1;
}
//printf( "Sound fault B %d/%d\n", i, samplesr );
sound[soundhead] = f*in_amplitude;
soundhead = (soundhead+1)%SOUNDCBSIZE;
}
SoundEventHappened( framesr, in, 0, channelin );
}
SoundEventHappened( samplesr, in, 0, channelin );
if( out )
if ( out )
{
SoundEventHappened( samplesr, out, 1, sd->channelsPlay );
memset( out, 0, framesp * channelout );
SoundEventHappened( framesp, out, 1, channelout );
}
*samplesp = samplesr;
}
int main(int argc, char ** argv)
#ifdef ANDROID
void HandleSuspend()
{
int i;
#ifdef TCC
void ManuallyRegisterDevices();
ManuallyRegisterDevices();
is_suspended = 1;
}
void HandleResume()
{
is_suspended = 0;
}
#endif
// function for calling initilization functions if we are using TCC
#ifdef TCC
void RegisterConstructorFunctions()
{
// Basic Window stuff
REGISTERheadless();
REGISTERset_screenx();
REGISTERset_screeny();
REGISTERsound_source();
REGISTERcpu_autolimit();
REGISTERcpu_autolimit_interval();
REGISTERsample_channel();
REGISTERshowfps();
REGISTERin_amplitude();
// Audio stuff
REGISTERNullCNFA();
REGISTERWinCNFA();
REGISTERcnfa_wasapi();
// Video Stuff
REGISTERnull();
REGISTERDisplayArray();
REGISTERDisplayHIDAPI();
REGISTERDisplayNetwork();
REGISTERDisplayOutDriver();
REGISTERDisplayPie();
REGISTERDisplayRadialPoles();
// block trying to load linux specific displays
#if not IS_WINDOWS
REGISTERDisplayDMX();
REGISTERDisplayFileWrite();
REGISTERDisplaySHM();
REGISTERDisplayUSB2812();
#endif
// Output stuff
REGISTEROutputCells();
REGISTEROutputLinear();
REGISTEROutputProminent();
REGISTEROutputVoronoi();
}
#endif
int main( int argc, char **argv )
{
#ifdef TCC
RegisterConstructorFunctions();
#endif
printf( "Output Drivers:\n" );
for( i = 0; i < MAX_OUT_DRIVERS; i++ )
for ( int i = 0; i < MAX_OUT_DRIVERS; i++ )
{
if( ODList[i].Name ) printf( "\t%s\n", ODList[i].Name );
if ( ODList[ i ].Name ) printf( "\t%s\n", ODList[ i ].Name );
}
#if defined(WIN32) || defined(USE_WINDOWS)
WSADATA wsaData;
WSAStartup(0x202, &wsaData);
strcpy( sound_source, "WIN" );
#if defined( WIN32 ) || defined( USE_WINDOWS )
// In case something needs network access.
WSADATA wsaData;
WSAStartup( 0x202, &wsaData );
#elif defined( ANDROID )
int hasperm = AndroidHasPermissions( "READ_EXTERNAL_STORAGE" );
int haspermInternet = AndroidHasPermissions( "INTERNET" );
if ( !hasperm ) AndroidRequestAppPermissions( "READ_EXTERNAL_STORAGE" );
if ( !haspermInternet ) AndroidRequestAppPermissions( "INTERNET" );
#else
strcpy( sound_source, "PULSE" );
// Linux
#endif
gargc = argc;
@ -190,262 +364,283 @@ int main(int argc, char ** argv)
SetupConfigs();
//Initialize Rawdraw
// Initialize Rawdraw
int frames = 0;
double ThisTime;
double SecToWait;
double LastFPSTime = OGGetAbsoluteTime();
double LastFrameTime = OGGetAbsoluteTime();
double SecToWait;
CNFGBGColor = 0x800000;
CNFGDialogColor = 0x444444;
CNFGBGColor = BACKGROUND_COLOR;
char title[1024];
char * tp = title;
memcpy( tp, "ColorChord ", strlen( "ColorChord " ) );
tp += strlen( "ColorChord " );
for( i = 1; i < argc; i++ )
// Generate the window title
char title[ 1024 ];
strcpy( title, "Colorchord " );
for ( int i = 1; i < argc; i++ )
{
memcpy( tp, argv[i], strlen( argv[i] ) );
tp += strlen( argv[i] );
*tp = ' ';
tp++;
strcat( title, argv[ i ] );
strcat( title, " " );
}
*tp = 0;
if( !headless )
CNFGSetup( title, set_screenx, set_screeny );
if ( !headless ) CNFGSetup( title, set_screenx, set_screeny );
char * OutDriverNames = strdup( GetParameterS( "outdrivers", "null" ) );
char * ThisDriver = OutDriverNames;
char * TDStart;
for( i = 0; i < MAX_OUT_DRIVERS; i++ )
char *OutDriverNames = strdup( GetParameterS( "outdrivers", "null" ) );
char *ThisDriver = OutDriverNames;
char *TDStart;
for ( int i = 0; i < MAX_OUT_DRIVERS; i++ )
{
while( *ThisDriver == ' ' || *ThisDriver == '\t' ) ThisDriver++;
if( !*ThisDriver ) break;
while ( *ThisDriver == ' ' || *ThisDriver == '\t' ) ThisDriver++;
if ( !*ThisDriver ) break;
TDStart = ThisDriver;
while( *ThisDriver != 0 && *ThisDriver != ',' )
while ( *ThisDriver != 0 && *ThisDriver != ',' )
{
if( *ThisDriver == '\t' || *ThisDriver == ' ' ) *ThisDriver = 0;
if ( *ThisDriver == '\t' || *ThisDriver == ' ' ) *ThisDriver = 0;
ThisDriver++;
}
if( *ThisDriver )
if ( *ThisDriver )
{
*ThisDriver = 0;
ThisDriver++;
}
printf( "Loading: %s\n", TDStart );
outdriver[i] = SetupOutDriver( TDStart );
outdriver[ i ] = SetupOutDriver( TDStart );
}
free(OutDriverNames);
free( OutDriverNames );
//Initialize Sound
sd = InitSound( sound_source, &SoundCB );
do {
#if IS_WINDOWS
const char *record_dev_name = "defaultRender";
#else
const char *record_dev_name = "@DEFAULT_MONITOR@";
#endif
// Initialize Sound
sd = CNFAInit( sound_source, "colorchord", &SoundCB, GetParameterI( "samplerate", 44100 ),
GetParameterI( "samplerate", 44100 ), GetParameterI( "channels", 2 ),
GetParameterI( "channels", 2 ), GetParameterI( "buffer", 1024 ),
GetParameterS( "devplay", "default" ), GetParameterS( "devrecord", record_dev_name ),
NULL );
if( !sd )
{
fprintf( stderr, "ERROR: Failed to initialize sound output device\n" );
return -1;
}
if ( sd ) break;
CNFGColor( LINE_COLOR );
CNFGPenX = 10;
CNFGPenY = 100;
CNFGHandleInput();
CNFGClearFrame();
CNFGDrawText( "Colorchord must be used with sound. Sound not available.", 10 );
CNFGSwapBuffers();
OGSleep( 1 );
} while ( 1 );
nf = CreateNoteFinder( sd->spsRec );
//Once everything was reinitialized, re-read the ini files.
// Once everything was reinitialized, re-read the ini files.
SetEnvValues( 1 );
printf( "================================================= Set Up\n" );
Now = OGGetAbsoluteTime();
double Last = Now;
while(1)
while ( !bQuitColorChord )
{
char stt[1024];
//Handle Rawdraw frame swappign
char stt[ 1024 ];
// Handle Rawdraw frame swappign
Now = OGGetAbsoluteTime();
DeltaFrameTime = Now - Last;
if( !headless )
if ( !headless )
{
CNFGHandleInput();
CNFGClearFrame();
CNFGColor( 0xFFFFFF );
CNFGColor( LINE_COLOR );
CNFGGetDimensions( &screenx, &screeny );
}
RunNoteFinder( nf, sound, (soundhead-1+SOUNDCBSIZE)%SOUNDCBSIZE, SOUNDCBSIZE );
//Done all ColorChord work.
RunNoteFinder( nf, sound, ( soundhead - 1 + SOUNDCBSIZE ) % SOUNDCBSIZE, SOUNDCBSIZE );
// Done all ColorChord work.
VisTimeStart = OGGetAbsoluteTime();
for( i = 0; i < MAX_OUT_DRIVERS; i++ )
// call the output drivers with the updated note finder data
for ( int i = 0; i < MAX_OUT_DRIVERS; i++ )
{
if( force_white )
{
memset( OutLEDs, 0x7f, MAX_LEDS*3 );
}
if( outdriver[i] )
outdriver[i]->Func( outdriver[i]->id, nf );
if ( force_white ) memset( OutLEDs, 0x7f, MAX_LEDS * 3 );
if ( outdriver[ i ] ) outdriver[ i ]->Func( outdriver[ i ]->id, nf );
}
VisTimeEnd = OGGetAbsoluteTime();
if( !headless )
if ( !headless )
{
//Handle outputs.
// Handle outputs.
int freqbins = nf->freqbins;
int note_peaks = freqbins/2;
int note_peaks = freqbins / 2;
int freqs = freqbins * nf->octaves;
//int maxdists = freqbins/2;
//Do a bunch of debugging.
if( show_debug_basic )
// Do a bunch of debugging.
if ( show_debug_basic && !is_suspended )
{
//char sttdebug[1024];
//char * sttend = sttdebug;
for( i = 0; i < nf->dists_count; i++ )
CNFGColor( TEXT_COLOR );
for ( int i = 0; i < nf->dists_count; i++ )
{
CNFGPenX = (nf->dists[i].mean + 0.5) / freqbins * screenx; //Move over 0.5 for visual purposes. The means is correct.
CNFGPenY = 400-nf->dists[i].amp * 150.0 / nf->dists[i].sigma;
//printf( "%f %f\n", dists[i].mean, dists[i].amp );
sprintf( stt, "%f\n%f\n", nf->dists[i].mean, nf->dists[i].amp );
// sttend += sprintf( sttend, "%f/%f ",nf->dists[i].mean, nf->dists[i].amp );
// Move over 0.5 for visual purposes. The means is correct.
CNFGPenX = ( nf->dists[ i ].mean + 0.5 ) / freqbins * screenx;
CNFGPenY = 400 - nf->dists[ i ].amp * 150.0 / nf->dists[ i ].sigma;
sprintf( stt, "%f\n%f\n", nf->dists[ i ].mean, nf->dists[ i ].amp );
CNFGDrawText( stt, 2 );
}
CNFGColor( 0xffffff );
//Draw the folded bins
for( i = 0; i < freqbins; i++ )
CNFGColor( LINE_COLOR );
// Draw the folded bins
for ( int bin = 0; bin < freqbins; bin++ )
{
float x0 = i / (float)freqbins * (float)screenx;
float x1 = (i+1) / (float)freqbins * (float)screenx;
float amp = nf->folded_bins[i] * 250.0;
CNFGDialogColor = CCtoHEX( ((float)(i+0.5) / freqbins), 1.0, 1.0 );
CNFGDrawBox( x0, 400-amp, x1, 400 );
const float x0 = bin / (float)freqbins * (float)screenx;
const float x1 = ( bin + 1 ) / (float)freqbins * (float)screenx;
const float amp = nf->folded_bins[ bin ] * 250.0;
const float note = (float)( bin + 0.5 ) / freqbins;
CNFGDialogColor = CCtoHEX( note, 1.0, 1.0 );
CNFGDrawBox( x0, 400 - amp, x1, 400 );
}
CNFGDialogColor = 0xf0f000;
for( i = 0; i < note_peaks; i++ )
// Draw the note peaks
for ( int peak = 0; peak < note_peaks; peak++ )
{
//printf( "%f %f /", note_positions[i], note_amplitudes[i] );
if( nf->note_amplitudes_out[i] < 0 ) continue;
CNFGDialogColor = CCtoHEX( (nf->note_positions[i] / freqbins), 1.0, 1.0 );
CNFGDrawBox( ((float)i / note_peaks) * screenx, 480 - nf->note_amplitudes_out[i] * 100, ((float)(i+1) / note_peaks) * screenx, 480 );
CNFGPenX = ((float)(i+.4) / note_peaks) * screenx;
if ( nf->note_amplitudes_out[ peak ] < 0 ) continue;
float note = (float)nf->note_positions[ peak ] / freqbins;
CNFGDialogColor = CCtoHEX( note, 1.0, 1.0 );
const int x1 = ( (float)peak / note_peaks ) * screenx;
const int x2 = ( (float)( peak + 1 ) / note_peaks ) * screenx;
const int y1 = 480 - nf->note_amplitudes_out[ peak ] * 100;
const int y2 = 480;
CNFGColor( LINE_COLOR );
CNFGDrawBox( x1, y1, x2, y2 );
CNFGPenX = ( (float)( peak + .4 ) / note_peaks ) * screenx;
CNFGPenY = screeny - 30;
sprintf( stt, "%d\n%0.0f", nf->enduring_note_id[i], nf->note_amplitudes2[i]*1000.0 );
//sttend += sprintf( sttend, "%5d/%5.0f ", nf->enduring_note_id[i], nf->note_amplitudes2[i]*1000.0 );
sprintf( stt, "%d\n%0.0f", nf->enduring_note_id[ peak ],
nf->note_amplitudes2[ peak ] * 1000.0 );
CNFGColor( TEXT_COLOR );
CNFGDrawText( stt, 2 );
}
//Let's draw the o-scope.
int thissoundhead = soundhead;
thissoundhead = (thissoundhead-1+SOUNDCBSIZE)%SOUNDCBSIZE;
int lasty = sound[thissoundhead] * 128 + 128; thissoundhead = (thissoundhead-1+SOUNDCBSIZE)%SOUNDCBSIZE;
int thisy = sound[thissoundhead] * 128 + 128; thissoundhead = (thissoundhead-1+SOUNDCBSIZE)%SOUNDCBSIZE;
for( i = 0; i < screenx; i++ )
CNFGColor( LINE_COLOR );
// Let's draw the o-scope.
int lasty;
int thissoundhead = ( soundhead - 1 + SOUNDCBSIZE ) % SOUNDCBSIZE;
int thisy = sound[ thissoundhead ] * -128 + 128;
for ( int i = screenx - 1; i > 0; i-- )
{
CNFGTackSegment( i, lasty, i+1, thisy );
lasty = thisy;
thisy = sound[thissoundhead] * 128 + 128; thissoundhead = (thissoundhead-1+SOUNDCBSIZE)%SOUNDCBSIZE;
thissoundhead = ( thissoundhead - 1 + SOUNDCBSIZE ) % SOUNDCBSIZE;
thisy = sound[ thissoundhead ] * -128 + 128;
CNFGTackSegment( i, lasty, i - 1, thisy );
}
//puts( sttdebug );
}
//Extra debugging?
if( show_debug )
// Extra debugging?
if ( show_debug && !is_suspended )
{
//Draw the histogram
// Draw the histogram
float lasthistval;
CNFGColor( 0xffffff );
for( i = -1; i < screenx; i++ )
CNFGColor( LINE_COLOR );
for ( int x_val = -1; x_val < screenx; x_val++ )
{
float thishistval = CalcHistAt( (float)i/(float)screenx*freqbins-0.5, nf->freqbins, nf->dists, nf->dists_count );
if( i >= 0 )
CNFGTackSegment( i, 400-lasthistval * 250.0, i+1, 400-thishistval * 250.0 );
// Calculate the value of the histogram at the current screen position
float hist_point = (float)x_val / (float)screenx * freqbins - 0.5;
float thishistval =
CalcHistAt( hist_point, nf->freqbins, nf->dists, nf->dists_count );
// Display the value on the screen
const short y = 400 - lasthistval * 250.0;
if ( x_val >= 0 ) CNFGTackSegment( x_val, y, x_val + 1, y );
lasthistval = thishistval;
}
CNFGColor( 0xffffff );
//Draw the bins
for( i = 0; i < freqs; i++ )
CNFGColor( LINE_COLOR );
// Draw the bins
for ( int bin = 0; bin < freqs; bin++ )
{
float x0 = i / (float)freqs * (float)screenx;
float x1 = (i+1) / (float)freqs * (float)screenx;
float amp = nf->outbins[i] * 250.0;
CNFGDialogColor = CCtoHEX( ((float)i / freqbins), 1.0, 1.0 );
float x0 = bin / (float)freqs * (float)screenx;
float x1 = ( bin + 1 ) / (float)freqs * (float)screenx;
float amp = nf->outbins[ bin ] * 250.0;
float note = (float)bin / freqbins;
CNFGDialogColor = CCtoHEX( note, 1.0, 1.0 );
CNFGDrawBox( x0, 0, x1, amp );
}
CNFGDialogColor = 0x0f0f0f;
char stdebug[1024];
CNFGColor( TEXT_COLOR );
char stdebug[ 1024 ];
sprintf( stdebug, "DFT:%8.2fms\nFLT:%8.2f\nDEC:%8.2f\nFNL:%8.2f\nDPY:%8.2f",
(nf->DFTTime - nf->StartTime)*1000,
(nf->FilterTime - nf->DFTTime)*1000,
(nf->DecomposeTime - nf->FilterTime)*1000,
(nf->FinalizeTime - nf->DecomposeTime)*1000,
(VisTimeEnd - VisTimeStart)*1000 );
( nf->DFTTime - nf->StartTime ) * 1000, ( nf->FilterTime - nf->DFTTime ) * 1000,
( nf->DecomposeTime - nf->FilterTime ) * 1000,
( nf->FinalizeTime - nf->DecomposeTime ) * 1000,
( VisTimeEnd - VisTimeStart ) * 1000 );
CNFGPenX = 50;
CNFGPenY = 50;
CNFGDrawText( stdebug, 2 );
}
CNFGColor( show_debug?0xffffff:0x000000 );
CNFGPenX = 0; CNFGPenY = screeny-10;
CNFGDrawText( "Extra Debug (D)", 2 );
if ( !is_suspended )
{
CNFGColor( show_debug ? ENABLED_COLOR : DISABLED_COLOR );
CNFGPenX = 0;
CNFGPenY = screeny - 10;
CNFGDrawText( "Extra Debug (D)", 2 );
CNFGColor( show_debug_basic?0xffffff:0x000000 );
CNFGPenX = 120; CNFGPenY = screeny-10;
CNFGDrawText( "Basic Debug (E)", 2 );
CNFGColor( show_debug_basic ? ENABLED_COLOR : DISABLED_COLOR );
CNFGPenX = 120;
CNFGPenY = screeny - 10;
CNFGDrawText( "Basic Debug (E)", 2 );
CNFGColor( show_debug_basic?0xffffff:0x000000 );
CNFGPenX = 240; CNFGPenY = screeny-10;
sprintf( stt, "[9] Key: %d [0] (%3.1f) [-]", gKey, nf->base_hz );
CNFGDrawText( stt, 2 );
CNFGColor( show_debug_basic ? ENABLED_COLOR : DISABLED_COLOR );
CNFGPenX = 240;
CNFGPenY = screeny - 10;
sprintf( stt, "[9] Key: %d [0] (%3.1f) [-]", gKey, nf->base_hz );
CNFGDrawText( stt, 2 );
CNFGColor( 0xffffff );
CNFGPenX = 440; CNFGPenY = screeny-10;
sprintf( stt, "FPS: %d", lastfps );
CNFGDrawText( stt, 2 );
CNFGSwapBuffers();
CNFGColor( TEXT_COLOR );
CNFGPenX = 440;
CNFGPenY = screeny - 10;
sprintf( stt, "FPS: %d", lastfps );
CNFGDrawText( stt, 2 );
#ifdef ANDROID
CNFGColor( TEXT_COLOR );
CNFGPenX = 10;
CNFGPenY = 600;
CNFGDrawText( genlog, 3 );
#endif
CNFGSwapBuffers();
}
}
//Finish Rawdraw with FPS counter, and a nice delay loop.
// Finish Rawdraw with FPS counter, and a nice delay loop.
frames++;
ThisTime = OGGetAbsoluteTime();
if( ThisTime > LastFPSTime + 1 && showfps )
if ( ThisTime > LastFPSTime + 1 && showfps )
{
printf( "FPS: %d\n", frames );
lastfps = frames;
frames = 0;
LastFPSTime+=1;
LastFPSTime += 1;
}
if( cpu_autolimit )
if ( cpu_autolimit )
{
SecToWait = cpu_autolimit_interval - ( ThisTime - LastFrameTime );
LastFrameTime += cpu_autolimit_interval;
if( SecToWait < -.1 ) LastFrameTime = ThisTime - .1;
if( SecToWait > 0 )
OGUSleep( (int)( SecToWait * 1000000 ) );
if ( SecToWait < -.1 ) LastFrameTime = ThisTime - .1;
if ( SecToWait > 0 ) OGUSleep( (int)( SecToWait * 1000000 ) );
}
SetEnvValues( 0 );
if ( !is_suspended ) SetEnvValues( 0 );
Last = Now;
}
}

View file

@ -1,338 +0,0 @@
//Copyright 2015 <>< Charles Lohr under the NewBSD OR MIT/x11 License.
#include "os_generic.h"
#ifdef USE_WINDOWS
#include <windows.h>
void OGSleep( int is )
{
Sleep( is*1000 );
}
void OGUSleep( int ius )
{
Sleep( ius/1000 );
}
double OGGetAbsoluteTime()
{
static LARGE_INTEGER lpf;
LARGE_INTEGER li;
if( !lpf.QuadPart )
{
QueryPerformanceFrequency( &lpf );
}
QueryPerformanceCounter( &li );
return (double)li.QuadPart / (double)lpf.QuadPart;
}
double OGGetFileTime( const char * file )
{
FILETIME ft;
HANDLE h = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if( h==INVALID_HANDLE_VALUE )
return -1;
GetFileTime( h, 0, 0, &ft );
CloseHandle( h );
return ft.dwHighDateTime + ft.dwLowDateTime;
}
og_thread_t OGCreateThread( void * (routine)( void * ), void * parameter )
{
return (og_thread_t)CreateThread( 0, 0, (LPTHREAD_START_ROUTINE)routine, parameter, 0, 0 );
}
void * OGJoinThread( og_thread_t ot )
{
WaitForSingleObject( ot, INFINITE );
CloseHandle( ot );
return 0;
}
void OGCancelThread( og_thread_t ot )
{
CloseHandle( ot );
}
og_mutex_t OGCreateMutex()
{
return CreateMutex( 0, 0, 0 );
}
void OGLockMutex( og_mutex_t om )
{
WaitForSingleObject(om, INFINITE);
}
void OGUnlockMutex( og_mutex_t om )
{
ReleaseMutex(om);
}
void OGDeleteMutex( og_mutex_t om )
{
CloseHandle( om );
}
og_sema_t OGCreateSema()
{
HANDLE sem = CreateSemaphore( 0, 0, 32767, 0 );
return (og_sema_t)sem;
}
int OGGetSema( og_sema_t os )
{
typedef LONG NTSTATUS;
HANDLE sem = (HANDLE)os;
typedef NTSTATUS (NTAPI *_NtQuerySemaphore)(
HANDLE SemaphoreHandle,
DWORD SemaphoreInformationClass, /* Would be SEMAPHORE_INFORMATION_CLASS */
PVOID SemaphoreInformation, /* but this is to much to dump here */
ULONG SemaphoreInformationLength,
PULONG ReturnLength OPTIONAL
);
typedef struct _SEMAPHORE_BASIC_INFORMATION {
ULONG CurrentCount;
ULONG MaximumCount;
} SEMAPHORE_BASIC_INFORMATION;
static _NtQuerySemaphore NtQuerySemaphore;
SEMAPHORE_BASIC_INFORMATION BasicInfo;
NTSTATUS Status;
if( !NtQuerySemaphore )
{
NtQuerySemaphore = (_NtQuerySemaphore)GetProcAddress (GetModuleHandle ("ntdll.dll"), "NtQuerySemaphore");
if( !NtQuerySemaphore )
{
return -1;
}
}
Status = NtQuerySemaphore (sem, 0 /*SemaphoreBasicInformation*/,
&BasicInfo, sizeof (SEMAPHORE_BASIC_INFORMATION), NULL);
if (Status == ERROR_SUCCESS)
{
return BasicInfo.CurrentCount;
}
return -2;
}
void OGLockSema( og_sema_t os )
{
WaitForSingleObject( (HANDLE)os, INFINITE );
}
void OGUnlockSema( og_sema_t os )
{
ReleaseSemaphore( (HANDLE)os, 1, 0 );
}
void OGDeleteSema( og_sema_t os )
{
CloseHandle( os );
}
#else
#define _GNU_SOURCE
#include <sys/stat.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/time.h>
#include <semaphore.h>
#include <unistd.h>
pthread_mutex_t g_RawMutexStart = PTHREAD_MUTEX_INITIALIZER;
void OGSleep( int is )
{
sleep( is );
}
void OGUSleep( int ius )
{
usleep( ius );
}
double OGGetAbsoluteTime()
{
struct timeval tv;
gettimeofday( &tv, 0 );
return ((double)tv.tv_usec)/1000000. + (tv.tv_sec);
}
double OGGetFileTime( const char * file )
{
struct stat buff;
int r = stat( file, &buff );
if( r < 0 )
{
return -1;
}
return buff.st_mtime;
}
og_thread_t OGCreateThread( void * (routine)( void * ), void * parameter )
{
pthread_t * ret = malloc( sizeof( pthread_t ) );
int r = pthread_create( ret, 0, routine, parameter );
if( r )
{
free( ret );
return 0;
}
return (og_thread_t)ret;
}
void * OGJoinThread( og_thread_t ot )
{
void * retval;
if( !ot )
{
return 0;
}
pthread_join( *(pthread_t*)ot, &retval );
free( ot );
return retval;
}
void OGCancelThread( og_thread_t ot )
{
if( !ot )
{
return;
}
pthread_cancel( *(pthread_t*)ot );
free( ot );
}
og_mutex_t OGCreateMutex()
{
pthread_mutexattr_t mta;
og_mutex_t r = malloc( sizeof( pthread_mutex_t ) );
pthread_mutexattr_init(&mta);
pthread_mutexattr_settype(&mta, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init( (pthread_mutex_t *)r, &mta );
return r;
}
void OGLockMutex( og_mutex_t om )
{
if( !om )
{
return;
}
pthread_mutex_lock( (pthread_mutex_t*)om );
}
void OGUnlockMutex( og_mutex_t om )
{
if( !om )
{
return;
}
pthread_mutex_unlock( (pthread_mutex_t*)om );
}
void OGDeleteMutex( og_mutex_t om )
{
if( !om )
{
return;
}
pthread_mutex_destroy( (pthread_mutex_t*)om );
free( om );
}
og_sema_t OGCreateSema()
{
sem_t * sem = malloc( sizeof( sem_t ) );
sem_init( sem, 0, 0 );
return (og_sema_t)sem;
}
int OGGetSema( og_sema_t os )
{
int valp;
sem_getvalue( os, &valp );
return valp;
}
void OGLockSema( og_sema_t os )
{
sem_wait( os );
}
void OGUnlockSema( og_sema_t os )
{
sem_post( os );
}
void OGDeleteSema( og_sema_t os )
{
sem_destroy( os );
free(os);
}
#endif
//Date Stamp: 2012-02-15
/*
Copyright (c) 2011-2012 <>< Charles Lohr
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of this file.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
*/

View file

@ -1,78 +0,0 @@
//Copyright 2015 <>< Charles Lohr under the NewBSD or MIT/x11 License.
#ifndef _OS_GENERIC_H
#define _OS_GENERIC_H
#if defined( WIN32 ) || defined (WINDOWS) || defined( _WIN32)
#define USE_WINDOWS
#endif
#ifdef __cplusplus
extern "C" {
#endif
//Things that shouldn't be macro'd
double OGGetAbsoluteTime();
void OGSleep( int is );
void OGUSleep( int ius );
double OGGetFileTime( const char * file );
//Threads and Mutices
typedef void* og_thread_t;
typedef void* og_mutex_t;
typedef void* og_sema_t;
og_thread_t OGCreateThread( void * (routine)( void * ), void * parameter );
void * OGJoinThread( og_thread_t ot );
void OGCancelThread( og_thread_t ot );
//Always a recrusive mutex.
og_mutex_t OGCreateMutex();
void OGLockMutex( og_mutex_t om );
void OGUnlockMutex( og_mutex_t om );
void OGDeleteMutex( og_mutex_t om );
//Always a semaphore
og_sema_t OGCreateSema(); //Create a semaphore, comes locked initially. NOTE: Max count is 32767
void OGLockSema( og_sema_t os );
int OGGetSema( og_sema_t os ); //if <0 there was a failure.
void OGUnlockSema( og_sema_t os );
void OGDeleteSema( og_sema_t os );
#ifdef __cplusplus
};
#endif
#endif
//Date Stamp: 2012-02-15
/*
NOTE: Portions (namely the top section) are part of headers from other
sources.
Copyright (c) 2011-2012 <>< Charles Lohr
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of this file.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
*/

View file

@ -7,6 +7,13 @@
#include <stdio.h>
#include <stdlib.h>
#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) \
|| defined(_WIN32) || defined(_WIN64)
#ifndef strdup
#define strdup _strdup
#endif
#endif
int force_white = 0;
unsigned char OutLEDs[MAX_LEDS*3];
int UsedLEDs;

View file

@ -37,7 +37,7 @@ extern struct OutDriverListElem ODList[MAX_OUT_DRIVERS];
extern const char OutDriverParameters[MAX_OUT_DRIVER_STRING];
//Pass setup "name=[driver]"
struct DriverInstances * SetupOutDriver( );
struct DriverInstances * SetupOutDriver( const char * drivername );
void RegOutDriver( const char * ron, struct DriverInstances * (*Init)( ) );
#define REGISTER_OUT_DRIVER( name ) \

View file

@ -6,6 +6,13 @@
#include <stdio.h>
#include <stdlib.h>
#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) \
|| defined(_WIN32) || defined(_WIN64)
#ifndef strdup
#define strdup _strdup
#endif
#endif
static struct chash * parameters;
//XXX TODO: Make this thread safe.

1
colorchord2/rawdraw Submodule

@ -0,0 +1 @@
Subproject commit 0f29047f6bfe287cf032e3ef845939f5d796a70e

20
colorchord2/shell.nix Normal file
View file

@ -0,0 +1,20 @@
{ pkgs ? import <nixpkgs> {} }:
let
localAdjustments = if (builtins.pathExists ./.local.shell.nix) then import ./.local.shell.nix pkgs else (x: x);
in
pkgs.mkShell (localAdjustments {
nativeBuildInputs = with pkgs; [
gnumake
gcc
glib
libpulseaudio
alsa-lib
xorg.libX11
xorg.libXext
xorg.libXinerama
libusb1
udev
freeglut
pkg-config
];
})

View file

@ -1,108 +0,0 @@
//Copyright 2015 <>< Charles Lohr under the ColorChord License.
#include "sound.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
static SoundInitFn * SoundDrivers[MAX_SOUND_DRIVERS];
static char * SoundDriverNames[MAX_SOUND_DRIVERS]; //XXX: There's a bug in my compiler, this should be 'static'
static int SoundDriverPriorities[MAX_SOUND_DRIVERS];
/*
void CleanupSound() __attribute__((destructor));
void CleanupSound()
{
int i;
for( i = 0; i < MAX_SOUND_DRIVERS; i++ )
{
if( SoundDriverNames[i] )
{
free( SoundDriverNames[i] );
}
}
}
*/
void RegSound( int priority, const char * name, SoundInitFn * fn )
{
int j;
if( priority <= 0 )
{
return;
}
for( j = MAX_SOUND_DRIVERS-1; j >= 0; j-- )
{
//Cruise along, find location to insert
if( j > 0 && ( !SoundDrivers[j-1] || SoundDriverPriorities[j-1] < priority ) )
{
SoundDrivers[j] = SoundDrivers[j-1];
SoundDriverNames[j] = SoundDriverNames[j-1];
SoundDriverPriorities[j] = SoundDriverPriorities[j-1];
}
else
{
SoundDrivers[j] = fn;
SoundDriverNames[j] = strdup( name );
SoundDriverPriorities[j] = priority;
break;
}
}
}
struct SoundDriver * InitSound( const char * driver_name, SoundCBType cb )
{
int i;
struct SoundDriver * ret = 0;
if( driver_name == 0 || strlen( driver_name ) == 0 )
{
//Search for a driver.
for( i = 0; i < MAX_SOUND_DRIVERS; i++ )
{
if( SoundDrivers[i] == 0 )
{
return 0;
}
ret = SoundDrivers[i]( cb );
if( ret )
{
return ret;
}
}
}
else
{
printf( "Initializing sound. Recommended driver: %s\n", driver_name );
for( i = 0; i < MAX_SOUND_DRIVERS; i++ )
{
if( SoundDrivers[i] == 0 )
{
return 0;
}
if( strcmp( SoundDriverNames[i], driver_name ) == 0 )
{
return SoundDrivers[i]( cb );
}
}
}
return 0;
}
int SoundState( struct SoundDriver * soundobject )
{
if( soundobject )
{
return soundobject->SoundStateFn( soundobject );
}
return -1;
}
void CloseSound( struct SoundDriver * soundobject )
{
if( soundobject )
{
soundobject->CloseFn( soundobject );
}
}

View file

@ -1,41 +0,0 @@
//Copyright 2015 <>< Charles Lohr under the ColorChord License.
#ifndef _SOUND_H
#define _SOUND_H
#define MAX_SOUND_DRIVERS 10
struct SoundDriver;
typedef void(*SoundCBType)( float * out, float * in, int samplesr, int * samplesp, struct SoundDriver * sd );
typedef void*(SoundInitFn)( SoundCBType cb );
struct SoundDriver
{
void (*CloseFn)( void * object );
int (*SoundStateFn)( struct SoundDriver * object );
SoundCBType callback;
int channelsPlay;
int spsPlay;
int channelsRec;
int spsRec;
//More fields may exist on a per-sound-driver basis
};
//Accepts:
// samplerate=44100;channels=2;devplay=default;devrecord=default;record=1;play=1;minavailcount=4096;stopthresh=1024;startthresh=4096;buffer=1024
// buffer is in samples
//If DriverName = 0 or empty, will try to find best driver.
struct SoundDriver * InitSound( const char * driver_name, SoundCBType cb );
int SoundState( struct SoundDriver * soundobject ); //returns 0 if okay, negative if faulted.
void CloseSound( struct SoundDriver * soundobject );
//Called by various sound drivers. Notice priority must be greater than 0. Priority of 0 or less will not register.
void RegSound( int priority, const char * name, SoundInitFn * fn );
#define REGISTER_SOUND( sounddriver, priority, name, function ) \
void __attribute__((constructor)) REGISTER##sounddriver() { RegSound( priority, name, function ); }
#endif

View file

@ -1,363 +0,0 @@
//Copyright 2015 <>< Charles Lohr under the ColorChord License.
#include "sound.h"
#include "os_generic.h"
#include "parameters.h"
#include <alsa/asoundlib.h>
#define BUFFERSETS 4
#define BLOCKING
struct SoundDriverAlsa
{
void (*CloseFn)( struct SoundDriverAlsa * object );
int (*SoundStateFn)( struct SoundDriverAlsa * object );
SoundCBType callback;
int channelsPlay;
int spsPlay;
int channelsRec;
int spsRec;
int alsa_fmt_s16le;
snd_pcm_uframes_t buffer;
og_thread_t thread;
snd_pcm_t *playback_handle;
snd_pcm_t *record_handle;
//More fields may exist on a per-sound-driver basis
};
static struct SoundDriverAlsa* InitASound( struct SoundDriverAlsa * r );
void CloseSoundAlsa( struct SoundDriverAlsa * r );
int SoundStateAlsa( struct SoundDriverAlsa * soundobject )
{
return ((soundobject->playback_handle)?1:0) | ((soundobject->record_handle)?2:0);
}
void CloseSoundAlsa( struct SoundDriverAlsa * r )
{
if( r )
{
if( r->playback_handle ) snd_pcm_close (r->playback_handle);
if( r->record_handle ) snd_pcm_close (r->record_handle);
#ifdef BLOCKING
OGUSleep(2000);
OGCancelThread( r->thread );
#endif
free( r );
}
}
static int SetHWParams( snd_pcm_t * handle, int * samplerate, int * channels, snd_pcm_uframes_t * buffer, struct SoundDriverAlsa * a )
{
int err;
snd_pcm_hw_params_t *hw_params;
if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) {
fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n",
snd_strerror (err));
return -1;
}
if ((err = snd_pcm_hw_params_any (handle, hw_params)) < 0) {
fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n",
snd_strerror (err));
goto fail;
}
if ((err = snd_pcm_hw_params_set_access (handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
fprintf (stderr, "cannot set access type (%s)\n",
snd_strerror (err));
goto fail;
}
if ((err = snd_pcm_hw_params_set_format (handle, hw_params, SND_PCM_FORMAT_FLOAT )) < 0) {
fprintf (stderr, "cannot set sample format (%s)\n",
snd_strerror (err));
printf( "Trying backup: S16LE.\n" );
if ((err = snd_pcm_hw_params_set_format (handle, hw_params, SND_PCM_FORMAT_S16_LE )) < 0) {
fprintf (stderr, "cannot set sample format (%s)\n",
snd_strerror (err));
goto fail;
}
a->alsa_fmt_s16le = 1;
}
if ((err = snd_pcm_hw_params_set_rate_near (handle, hw_params, (unsigned int*)samplerate, 0)) < 0) {
fprintf (stderr, "cannot set sample rate (%s)\n",
snd_strerror (err));
goto fail;
}
if ((err = snd_pcm_hw_params_set_channels (handle, hw_params, *channels)) < 0) {
fprintf (stderr, "cannot set channel count (%s)\n",
snd_strerror (err));
goto fail;
}
int dir = 0;
if( (err = snd_pcm_hw_params_set_period_size_near(handle, hw_params, buffer, &dir)) < 0 )
{
fprintf( stderr, "cannot set period size. (%s)\n",
snd_strerror(err) );
goto fail;
}
if ((err = snd_pcm_hw_params (handle, hw_params)) < 0) {
fprintf (stderr, "cannot set parameters (%s)\n",
snd_strerror (err));
goto fail;
}
snd_pcm_hw_params_free (hw_params);
return 0;
fail:
snd_pcm_hw_params_free (hw_params);
return -2;
}
static int SetSWParams( snd_pcm_t * handle, int isrec )
{
snd_pcm_sw_params_t *sw_params;
int err;
//Time for software parameters:
if( !isrec )
{
if ((err = snd_pcm_sw_params_malloc (&sw_params)) < 0) {
fprintf (stderr, "cannot allocate software parameters structure (%s)\n",
snd_strerror (err));
goto failhard;
}
if ((err = snd_pcm_sw_params_current (handle, sw_params)) < 0) {
fprintf (stderr, "cannot initialize software parameters structure (%s) (%p)\n",
snd_strerror (err), handle);
goto fail;
}
if ((err = snd_pcm_sw_params_set_avail_min (handle, sw_params, GetParameterI( "minavailcount", 2048 ) )) < 0) {
fprintf (stderr, "cannot set minimum available count (%s)\n",
snd_strerror (err));
goto fail;
}
if ((err = snd_pcm_sw_params_set_stop_threshold(handle, sw_params, GetParameterI( "stopthresh", 512 ))) < 0) {
fprintf (stderr, "cannot set minimum available count (%s)\n",
snd_strerror (err));
goto fail;
}
if ((err = snd_pcm_sw_params_set_start_threshold(handle, sw_params, GetParameterI( "startthresh", 2048 ))) < 0) {
fprintf (stderr, "cannot set minimum available count (%s)\n",
snd_strerror (err));
goto fail;
}
if ((err = snd_pcm_sw_params (handle, sw_params)) < 0) {
fprintf (stderr, "cannot set software parameters (%s)\n",
snd_strerror (err));
goto fail;
}
}
if ((err = snd_pcm_prepare (handle)) < 0) {
fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
snd_strerror (err));
goto fail;
}
return 0;
fail:
if( !isrec )
{
snd_pcm_sw_params_free (sw_params);
}
failhard:
return -1;
}
#ifdef BLOCKING
static void * SoundThread( void * v )
{
int i;
struct SoundDriverAlsa * a = (struct SoundDriverAlsa*)v;
float * bufr[BUFFERSETS];
float * bufp[BUFFERSETS];
for(i = 0; i < BUFFERSETS; i++ )
{
bufr[i] = malloc( a->buffer * sizeof(float) * a->channelsRec );
bufp[i] = malloc( a->buffer * sizeof(float) * a->channelsPlay );
}
while( a->record_handle || a->playback_handle )
{
int err;
i = (i+1)%BUFFERSETS;
if( a->record_handle )
{
if( (err = snd_pcm_readi (a->record_handle, bufr[i], a->buffer)) != a->buffer)
{
fprintf (stderr, "read from audio interface failed (%s)\n",
snd_strerror (err));
if( a->record_handle ) snd_pcm_close (a->record_handle);
a->record_handle = 0;
}
else
{
//has_rec = 1;
}
}
if( a->alsa_fmt_s16le )
{
//Hacky: Turns out data was s16le.
int16_t * dat = (int16_t*)bufr[i];
float * dot = bufr[i];
int i;
int len = a->buffer;
for( i = len-1; i >= 0; i-- )
{
dot[i] = dat[i]/32768.0;
}
}
//Do our callback.
int playbacksamples = 0;
a->callback( bufp[i], bufr[i], a->buffer, &playbacksamples, (struct SoundDriver*)a );
//playbacksamples *= sizeof(float) * a->channelsPlay;
if( a->playback_handle )
{
if ((err = snd_pcm_writei (a->playback_handle, bufp[i], playbacksamples)) != playbacksamples)
{
fprintf (stderr, "write to audio interface failed (%s)\n",
snd_strerror (err));
if( a->playback_handle ) snd_pcm_close (a->playback_handle);
a->playback_handle = 0;
}
}
}
//Fault happened, re-initialize?
InitASound( a );
return 0;
}
#else
//Handle callback
static struct SoundDriverAlsa * reccb;
static int record_callback (snd_pcm_sframes_t nframes)
{
int err;
// printf ("playback callback called with %u frames\n", nframes);
/* ... fill buf with data ... */
if ((err = snd_pcm_writei (playback_handle, buf, nframes)) < 0) {
fprintf (stderr, "write failed (%s)\n", snd_strerror (err));
}
return err;
}
#endif
static struct SoundDriverAlsa * InitASound( struct SoundDriverAlsa * r )
{
int err;
if( GetParameterI( "play", 0 ) )
{
if ((err = snd_pcm_open (&r->playback_handle, GetParameterS( "devplay", "default" ), SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
fprintf (stderr, "cannot open output audio device (%s)\n",
snd_strerror (err));
goto fail;
}
}
if( GetParameterI( "record", 1 ) )
{
if ((err = snd_pcm_open (&r->record_handle, GetParameterS( "devrecord", "default" ), SND_PCM_STREAM_CAPTURE, 0)) < 0) {
fprintf (stderr, "cannot open input audio device (%s)\n",
snd_strerror (err));
goto fail;
}
}
if( r->playback_handle )
{
if( SetHWParams( r->playback_handle, &r->spsPlay, &r->channelsPlay, &r->buffer, r ) < 0 )
goto fail;
if( SetSWParams( r->playback_handle, 0 ) < 0 )
goto fail;
}
if( r->record_handle )
{
if( SetHWParams( r->record_handle, &r->spsRec, &r->channelsRec, &r->buffer, r ) < 0 )
goto fail;
if( SetSWParams( r->record_handle, 1 ) < 0 )
goto fail;
}
if( r->playback_handle && r->record_handle )
{
snd_pcm_link ( r->playback_handle, r->record_handle );
}
#ifdef BLOCKING
r->thread = OGCreateThread( SoundThread, r );
#else
reccb = r;
//handle interrupt
#endif
return r;
fail:
if( r )
{
if( r->playback_handle ) snd_pcm_close (r->playback_handle);
if( r->record_handle ) snd_pcm_close (r->record_handle);
free( r );
}
return 0;
}
void * InitSoundAlsa( SoundCBType cb )
{
struct SoundDriverAlsa * r = malloc( sizeof( struct SoundDriverAlsa ) );
r->CloseFn = CloseSoundAlsa;
r->SoundStateFn = SoundStateAlsa;
r->callback = cb;
r->spsPlay = GetParameterI( "samplerate", 44100 );
r->channelsPlay = GetParameterI( "channels", 2 );
r->spsRec = r->spsPlay;
r->channelsRec = r->channelsPlay;
r->playback_handle = 0;
r->record_handle = 0;
r->buffer = GetParameterI( "buffer", 1024 );
r->alsa_fmt_s16le = 0;
return InitASound(r);
}
REGISTER_SOUND( AlsaSound, 10, "ALSA", InitSoundAlsa );

View file

@ -1,45 +0,0 @@
//Copyright 2015 <>< Charles Lohr under the ColorChord License.
#include "sound.h"
#include "os_generic.h"
#include <stdlib.h>
#include "parameters.h"
struct SoundDriverNull
{
void (*CloseFn)( struct SoundDriverNull * object );
int (*SoundStateFn)( struct SoundDriverNull * object );
SoundCBType soundcb;
int channelsPlay;
int spsPlay;
int channelsRec;
int spsRec;
};
void CloseSoundNull( struct SoundDriverNull * object )
{
free( object );
}
int SoundStateNull( struct SoundDriverNull * object )
{
return 0;
}
void * InitSoundNull( SoundCBType cb )
{
struct SoundDriverNull * r = malloc( sizeof( struct SoundDriverNull ) );
r->CloseFn = CloseSoundNull;
r->SoundStateFn = SoundStateNull;
r->soundcb = cb;
r->spsPlay = GetParameterI( "samplerate", 44100 );
r->channelsPlay = GetParameterI( "channels", 2 );
r->spsRec = r->spsPlay;
r->channelsRec = r->channelsRec;
return r;
}
REGISTER_SOUND( NullSound, 1, "NULL", InitSoundNull );

View file

@ -1,379 +0,0 @@
//Copyright 2015 <>< Charles Lohr under the ColorChord License.
//This file is really rough. Full duplex doesn't seem to work hardly at all.
#include "sound.h"
#include "os_generic.h"
#include "parameters.h"
#include <stdlib.h>
#include <pulse/simple.h>
#include <pulse/pulseaudio.h>
#include <pulse/error.h>
#include <stdio.h>
#include <string.h>
#define BUFFERSETS 3
//from http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/Samples/AsyncPlayback/
//also http://maemo.org/api_refs/5.0/5.0-final/pulseaudio/pacat_8c-example.html
struct SoundDriverPulse
{
void (*CloseFn)( struct SoundDriverPulse * object );
int (*SoundStateFn)( struct SoundDriverPulse * object );
SoundCBType callback;
int channelsPlay;
int spsPlay;
int channelsRec;
int spsRec;
const char * sourceName;
og_thread_t thread;
pa_stream * play;
pa_stream * rec;
pa_context * pa_ctx;
pa_mainloop *pa_ml;
int pa_ready;
int buffer;
//More fields may exist on a per-sound-driver basis
};
void CloseSoundPulse( struct SoundDriverPulse * r );
int SoundStatePulse( struct SoundDriverPulse * soundobject )
{
return ((soundobject->play)?1:0) | ((soundobject->rec)?2:0);
}
void CloseSoundPulse( struct SoundDriverPulse * r )
{
if( r )
{
if( r->play )
{
pa_stream_unref (r->play);
r->play = 0;
}
if( r->rec )
{
pa_stream_unref (r->rec);
r->rec = 0;
}
OGUSleep(2000);
OGCancelThread( r->thread );
free( r );
}
}
static void * SoundThread( void * v )
{
struct SoundDriverPulse * r = (struct SoundDriverPulse*)v;
while(1)
{
pa_mainloop_iterate( r->pa_ml, 1, NULL );
}
return 0;
}
/*
int i;
int error;
struct SoundDriverPulse * r = (struct SoundDriverPulse*)v;
float * bufr[BUFFERSETS];
float * bufp[BUFFERSETS];
for(i = 0; i < BUFFERSETS; i++ )
{
bufr[i] = malloc( r->buffer * sizeof(float) * r->channelsRec );
bufp[i] = malloc( r->buffer * sizeof(float) * r->channelsPlay );
}
while( r->play || r->rec )
{
i = (i+1)%BUFFERSETS;
if( r->rec )
{
if (pa_stream_read(r->rec, bufr[i], r->buffer * sizeof(float) * r->channelsRec, &error) < 0) {
fprintf(stderr, __FILE__": pa_stream_write() failed: %s\n", pa_strerror(error));
pa_stream_unref( r->play );
r->rec = 0;
}
}
int playbacksamples = 0;
r->callback( bufp[i], bufr[i], r->buffer, &playbacksamples, (struct SoundDriver*)r );
playbacksamples *= sizeof( float ) * r->channelsPlay;
if( r->play )
{
if (pa_stream_write(r->play, bufp[i], playbacksamples, NULL, 0LL, PA_SEEK_RELATIVE) < 0) {
fprintf(stderr, __FILE__": pa_stream_write() failed: %s\n", pa_strerror(error));
pa_stream_unref( r->play );
r->play = 0;
}
}
}
}*/
static void stream_request_cb(pa_stream *s, size_t length, void *userdata) {
// pa_usec_t usec;
struct SoundDriverPulse * r = (struct SoundDriverPulse*)userdata;
if( r->rec )
{
return;
}
/*
//Neat: You might want this:
pa_stream_get_latency(s,&usec,&neg);
if (sampleoffs*2 + length > sizeof(sampledata))
{
sampleoffs = 0;
}
if (length > sizeof(sampledata)) {
length = sizeof(sampledata);
}
*/
// pa_stream_write(s, &sampledata[sampleoffs], length, NULL, 0LL, PA_SEEK_RELATIVE);
int playbacksamples = 0;
float bufr[r->buffer*r->channelsRec];
float bufp[r->buffer*r->channelsPlay];
r->callback( bufp, bufr, r->buffer, &playbacksamples, (struct SoundDriver*)r );
//playbacksamples *= sizeof( float ) * r->channelsPlay;
pa_stream_write(r->play, &bufp, playbacksamples, NULL, 0LL, PA_SEEK_RELATIVE);
}
static void stream_record_cb(pa_stream *s, size_t length, void *userdata) {
// pa_usec_t usec;
// int neg;
struct SoundDriverPulse * r = (struct SoundDriverPulse*)userdata;
/* pa_stream_get_latency(s,&usec,&neg);
printf(" latency %8d us\n",(int)usec);
if (sampleoffs*2 + length > sizeof(sampledata))
{
sampleoffs = 0;
}
if (length > sizeof(sampledata)) {
length = sizeof(sampledata);
}*/
int playbacksamples = 0;
float * bufr;
if (pa_stream_peek(r->rec, (void*)&bufr, &length) < 0) {
fprintf(stderr, ("pa_stream_peek() failed: %s\n"), pa_strerror(pa_context_errno(r->pa_ctx)));
return;
}
float * buffer;
buffer = pa_xmalloc(length);
memcpy(buffer, bufr, length);
pa_stream_drop(r->rec);
float bufp[length*r->channelsPlay];
r->callback( bufp, buffer, length/sizeof(float)/r->channelsRec, &playbacksamples, (struct SoundDriver*)r );
//playbacksamples *= sizeof( float ) * r->channelsPlay;
pa_xfree( buffer );
if( r->play )
pa_stream_write(r->play, &bufp, playbacksamples*sizeof(float)*r->channelsPlay, NULL, 0LL, PA_SEEK_RELATIVE);
}
static void stream_underflow_cb(pa_stream *s, void *userdata) {
// We increase the latency by 50% if we get 6 underflows and latency is under 2s
// This is very useful for over the network playback that can't handle low latencies
printf("underflow\n");
// underflows++;
/* if (underflows >= 6 && latency < 2000000) {
latency = (latency*3)/2;
bufattr.maxlength = pa_usec_to_bytes(latency,&ss);
bufattr.tlength = pa_usec_to_bytes(latency,&ss);
pa_stream_set_buffer_attr(s, &bufattr, NULL, NULL);
underflows = 0;
printf("latency increased to %d\n", latency);
}*/
}
void pa_state_cb(pa_context *c, void *userdata) {
pa_context_state_t state;
int *pa_ready = userdata;
state = pa_context_get_state(c);
switch (state) {
// These are just here for reference
case PA_CONTEXT_UNCONNECTED:
case PA_CONTEXT_CONNECTING:
case PA_CONTEXT_AUTHORIZING:
case PA_CONTEXT_SETTING_NAME:
default:
break;
case PA_CONTEXT_FAILED:
case PA_CONTEXT_TERMINATED:
*pa_ready = 2;
break;
case PA_CONTEXT_READY:
*pa_ready = 1;
break;
}
}
void * InitSoundPulse( SoundCBType cb )
{
static pa_buffer_attr bufattr;
static pa_sample_spec ss;
int error;
pa_mainloop_api *pa_mlapi;
struct SoundDriverPulse * r = malloc( sizeof( struct SoundDriverPulse ) );
r->pa_ml = pa_mainloop_new();
pa_mlapi = pa_mainloop_get_api(r->pa_ml);
const char * title = GetParameterS( "title", "PA Test" );
r->pa_ctx = pa_context_new(pa_mlapi, title );
pa_context_connect(r->pa_ctx, NULL, 0, NULL);
//TODO: pa_context_set_state_callback
r->CloseFn = CloseSoundPulse;
r->SoundStateFn = SoundStatePulse;
r->callback = cb;
r->spsPlay = GetParameterI( "samplerate", 44100 );
r->channelsPlay = GetParameterI( "channels", 2 );
r->spsRec = r->spsPlay;
r->channelsRec = r->channelsPlay;
r->sourceName = GetParameterS( "sourcename", NULL );
if( strcmp( r->sourceName, "default" ) == 0 )
{
r->sourceName = 0;
}
r->play = 0;
r->rec = 0;
r->buffer = GetParameterI( "buffer", 1024 );
printf ("Pulse: from: %s (%s) / %dx%d (%d)\n", r->sourceName, title, r->spsPlay, r->channelsPlay, r->buffer );
memset( &ss, 0, sizeof( ss ) );
ss.format = PA_SAMPLE_FLOAT32NE;
ss.rate = r->spsPlay;
ss.channels = r->channelsPlay;
r->pa_ready = 0;
pa_context_set_state_callback(r->pa_ctx, pa_state_cb, &r->pa_ready);
while (r->pa_ready == 0)
{
pa_mainloop_iterate(r->pa_ml, 1, NULL);
}
int bufbytes = r->buffer * sizeof(float) * r->channelsRec;
if( GetParameterI( "play", 1 ) )
{
if (!(r->play = pa_stream_new(r->pa_ctx, "Play", &ss, NULL))) {
error = -3; //XXX ??? TODO
fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error));
goto fail;
}
pa_stream_set_underflow_callback(r->play, stream_underflow_cb, NULL);
pa_stream_set_write_callback(r->play, stream_request_cb, r );
bufattr.fragsize = (uint32_t)-1;
bufattr.maxlength = bufbytes*3; //XXX TODO Consider making this -1
bufattr.minreq = 0;
bufattr.prebuf = (uint32_t)-1;
bufattr.tlength = bufbytes*3;
int ret = pa_stream_connect_playback(r->play, NULL, &bufattr,
// PA_STREAM_INTERPOLATE_TIMING
// |PA_STREAM_ADJUST_LATENCY //Some servers don't like the adjust_latency flag.
// |PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
0, NULL, NULL );
printf( "Play stream.\n" );
if( ret < 0 )
{
fprintf(stderr, __FILE__": (PLAY) pa_stream_connect_playback() failed: %s\n", pa_strerror(ret));
goto fail;
}
}
if( GetParameterI( "rec", 1 ) )
{
if (!(r->rec = pa_stream_new(r->pa_ctx, "Record", &ss, NULL))) {
error = -3; //XXX ??? TODO
fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error));
goto fail;
}
pa_stream_set_read_callback(r->rec, stream_record_cb, r );
bufattr.fragsize = bufbytes;
bufattr.maxlength = (uint32_t)-1;//(uint32_t)-1; //XXX: Todo, should this be low?
bufattr.minreq = bufbytes;
bufattr.prebuf = (uint32_t)-1;
bufattr.tlength = bufbytes*3;
printf( "Source: %s\n", r->sourceName );
int ret = pa_stream_connect_record(r->rec, r->sourceName, &bufattr, 0
// |PA_STREAM_INTERPOLATE_TIMING
|PA_STREAM_ADJUST_LATENCY //Some servers don't like the adjust_latency flag.
// |PA_STREAM_AUTO_TIMING_UPDATE
// 0
);
printf( "Got handle: %d\n", ret );
if( ret < 0 )
{
fprintf(stderr, __FILE__": (REC) pa_stream_connect_playback() failed: %s\n", pa_strerror(ret));
goto fail;
}
}
printf( "Pulse initialized.\n" );
// SoundThread( r );
r->thread = OGCreateThread( SoundThread, r );
return r;
fail:
if( r )
{
if( r->play ) pa_xfree (r->play);
if( r->rec ) pa_xfree (r->rec);
free( r );
}
return 0;
}
REGISTER_SOUND( PulseSound, 11, "PULSE", InitSoundPulse );

View file

@ -1,190 +0,0 @@
//Copyright 2015 <>< Charles Lohr under the ColorChord License.
#include <windows.h>
#include "parameters.h"
#include "sound.h"
#include "os_generic.h"
#include <stdio.h>
#include <stdint.h>
#include <mmsystem.h>
#include <stdlib.h>
#if defined(WIN32) && !defined( TCC )
#pragma comment(lib,"winmm.lib")
#endif
#define BUFFS 2
struct SoundDriverWin
{
void (*CloseFn)( struct SoundDriverWin * object );
int (*SoundStateFn)( struct SoundDriverWin * object );
SoundCBType callback;
int channelsPlay;
int spsPlay;
int channelsRec;
int spsRec;
int buffer;
int isEnding;
int GOBUFF;
int recording;
HWAVEIN hMyWave;
WAVEHDR WavBuff[BUFFS];
};
static struct SoundDriverWin * w;
void CloseSoundWin( struct SoundDriverWin * r )
{
int i;
if( r )
{
waveInStop(r->hMyWave);
waveInReset(r->hMyWave);
for ( i=0;i<BUFFS;i++)
{
waveInUnprepareHeader(r->hMyWave,&(r->WavBuff[i]),sizeof(WAVEHDR));
free ((r->WavBuff[i]).lpData);
}
waveInClose(r->hMyWave);
free( r );
}
}
int SoundStateWin( struct SoundDriverWin * soundobject )
{
return soundobject->recording;
}
void CALLBACK HANDLEMIC(HWAVEIN hwi,UINT umsg, DWORD dwi, DWORD hdr, DWORD dwparm)
{
int ctr;
int ob;
long cValue;
unsigned int maxWave=0;
float buffer[w->buffer*w->channelsRec];
if (w->isEnding) return;
switch (umsg)
{
case MM_WIM_OPEN:
printf( "Mic Open.\n" );
w->recording = 1;
break;
case MM_WIM_DATA:
// printf( "Mic Data.\n");
ob = (w->GOBUFF+(BUFFS))%BUFFS;
// waveInPrepareHeader(w->hMyWave,&(w->WavBuff[w->Cbuff]),sizeof(WAVEHDR));
for (ctr=0;ctr<w->buffer * w->channelsRec;ctr++) {
float cv = (uint16_t)(((uint8_t)w->WavBuff[ob].lpData[ctr*2+1])*256+((uint8_t)w->WavBuff[ob].lpData[ctr*2])+32768)-32768;
cv /= 32768;
// if( ctr < 3 ) cv = -1;
// buffer[(w->buffer * w->channelsRec)-ctr-1] = cv;
buffer[ctr] = cv;
}
waveInAddBuffer(w->hMyWave,&(w->WavBuff[w->GOBUFF]),sizeof(WAVEHDR));
w->GOBUFF = ( w->GOBUFF + 1 ) % BUFFS;
int playbacksamples; //Unused
w->callback( 0, buffer, w->buffer, &playbacksamples, (struct SoundDriver*)w );
}
}
static struct SoundDriverWin * InitWinSound( struct SoundDriverWin * r )
{
int i;
WAVEFORMATEX wfmt;
memset( &wfmt, 0, sizeof(wfmt) );
printf ("WFMT Size (debugging temp for TCC): %d\n", sizeof(wfmt) );
if( GetParameterI( "play", 0 ) )
{
fprintf( stderr, "Error: This Windows Sound Driver does not support playback.\n" );
exit( -1 );
}
w = r;
printf( "WFMT: %d %d %d\n", r->channelsRec, r->spsRec,
r->spsRec * r->channelsRec );
wfmt.wFormatTag = WAVE_FORMAT_PCM;
wfmt.nChannels = r->channelsRec;
wfmt.nSamplesPerSec = r->spsRec;
wfmt.nAvgBytesPerSec = r->spsRec * r->channelsRec;
wfmt.nBlockAlign = r->channelsRec * 2;
wfmt.wBitsPerSample = 16;
wfmt.cbSize = 0;
long dwdevice;
dwdevice = GetParameterI( "wininput", WAVE_MAPPER );
printf( "Wave Devs: %d; WAVE_MAPPER: %d; Selected Input: %d\n", waveInGetNumDevs(), WAVE_MAPPER, dwdevice );
printf( "waveInOpen: %p, %p\n", r->hMyWave, &wfmt );
int p = waveInOpen(&r->hMyWave, dwdevice, &wfmt, (void*)(&HANDLEMIC) , 0, CALLBACK_FUNCTION);
if( p )
{
fprintf( stderr, "Error performing waveInOpen. Received code: %d\n", p );
}
for ( i=0;i<BUFFS;i++)
{
memset( &(r->WavBuff[i]), 0, sizeof(r->WavBuff[i]) );
(r->WavBuff[i]).dwBufferLength = r->buffer*2*r->channelsRec;
(r->WavBuff[i]).dwLoops = 1;
(r->WavBuff[i]).lpData=(char*) malloc(r->buffer*r->channelsRec*2);
p = waveInPrepareHeader(r->hMyWave,&(r->WavBuff[i]),sizeof(WAVEHDR));
printf( "WIP: %d\n", p );
waveInAddBuffer(r->hMyWave,&(r->WavBuff[i]),sizeof(WAVEHDR));
printf( "WIA: %d\n", p );
}
\
p = waveInStart(r->hMyWave);
if( p )
{
fprintf( stderr, "Error performing waveInStart. Received code %d\n", p );
}
return r;
}
void * InitSoundWin( SoundCBType cb )
{
struct SoundDriverWin * r = (struct SoundDriverWin *)malloc( sizeof( struct SoundDriverWin ) );
r->CloseFn = CloseSoundWin;
r->SoundStateFn = SoundStateWin;
r->callback = cb;
r->spsRec = GetParameterI( "samplerate", 44100 );
r->channelsRec = GetParameterI( "channels", 2 );
r->buffer = GetParameterI( "buffer", 384 );
r->recording = 0;
r->isEnding = 0;
printf( "Buffer: %d\n", r->buffer );
r->GOBUFF=0;
return InitWinSound(r);
}
REGISTER_SOUND( SoundWin, 10, "WIN", InitSoundWin );

View file

@ -0,0 +1,24 @@
all : ../colorchord.exe
OUTS := OutputVoronoi.o DisplayArray.o OutputLinear.o DisplayPie.o DisplayNetwork.o DisplayUSB2812.o DisplayDMX.o OutputProminent.o RecorderPlugin.o DisplayHIDAPI.o hidapi.o OutputCells.o DisplaySHM.o DisplayFileWrite.o
SRCS := ../main.c ../dft.c ../decompose.c ../filter.c ../color.c ../notefinder.c ../util.c ../outdrivers.c
SRCS += ../parameters.c ../chash.c ../OutputVoronoi.c ../OutputProminent.c ../DisplayArray.c
SRCS += ../OutputLinear.c ../DisplayPie.c ../DisplayNetwork.c ../hook.c ../RecorderPlugin.c
SRCS += ../../embeddedcommon/DFT32.c ../OutputCells.c ../configs.c ../hidapi.c ../DisplayHIDAPI.c
WINGCC:= clang -fcolor-diagnostics
WINGCCFLAGS:= -g -D_CRT_SECURE_NO_WARNINGS -Wno-deprecated-declarations -DICACHE_FLASH_ATTR= -I../../embeddedcommon -I../cnfa -I../rawdraw -I../ -O1 #-O2 -Wl,--relax -Wl,--gc-sections -ffunction-sections -fdata-sections
WINLDFLAGS:=-lwinmm -lgdi32 -lws2_32 -lsetupapi -lkernel32 -luser32 -ldbghelp -lole32 -lmmdevapi -lAvrt
RAWDRAWLIBS:=-lX11 -lm -lpthread -lXinerama -lXext
LDLIBS:=-lpthread -lasound -lm -lpulse-simple -lpulse -ludev -lrt
OBJS:=../main.o ../dft.o ../decompose.o ../filter.o ../color.o ../notefinder.o ../util.o ../outdrivers.o $(OUTS) ../parameters.o ../chash.o ../hook.o ../../embeddedcommon/DFT32.o ../configs.o
../colorchord.exe : $(SRCS)
$(WINGCC) $(WINGCCFLAGS) -o $@ $^ $(WINLDFLAGS)
clean :
rm -rf *.o *~ ../colorchord ../colorchord.exe ../embeddedcc $(OBJS)

View file

@ -0,0 +1,15 @@
@echo off
set CC="C:\Program Files\LLVM\bin\clang.exe"
rem To enable OpenGL rendering use the -DCNFGOGL option
set CCFLAGS=-g -D_CRT_SECURE_NO_WARNINGS -DCNFGOGL -DNO_WIN_HEADERS
set CCIFLAGS=-I../../embeddedcommon -I../cnfa -I../rawdraw -I../ -O2
set CCLFLAGS=-lwinmm -lgdi32 -lws2_32 -lsetupapi -lkernel32 -luser32 -ldbghelp -lole32 -lmmdevapi -lAvrt -lopengl32
set SOURCES=..\main.c ..\chash.c ..\color.c ..\configs.c ..\decompose.c ..\dft.c ..\filter.c ^
..\outdrivers.c ..\hidapi.c ..\hook.c ..\parameters.c ..\util.c ..\notefinder.c ^
..\..\embeddedcommon\DFT32.c symbol_enumerator.c ^
..\DisplayArray.c ..\DisplayDMX.c ..\DisplayFileWrite.c ..\DisplayHIDAPI.c ..\DisplayNetwork.c ^
..\DisplayOUTDriver.c ..\DisplayPie.c ..\DisplayRadialPoles.c ..\DisplaySHM.c ..\DisplayUSB2812.c ^
..\OutputCells.c ..\OutputLinear.c ..\OutputProminent.c ..\OutputVoronoi.c
@echo on
%CC% %CCFLAGS% %CCIFLAGS% -o ../colorchord.exe %SOURCES% %CCLFLAGS%

View file

@ -1,12 +1,25 @@
@echo off
echo Unzip http://download.savannah.gnu.org/releases/tinycc/tcc-0.9.27-win64-bin.zip to C:\tcc
echo Also, if compiling with OpenGL, download http://download.savannah.nongnu.org/releases/tinycc/winapi-full-for-0.9.27.zip and overwrite the include, etc. folders in C:\tcc.
echo Don't worry. It includes the i386 compiler in the win64 build.
set CFLAGS=-v -DHIDAPI -DWINDOWS -DWIN32 -DTCC -DRUNTIME_SYMNUM -Os -Itccinc -DINCLUDING_EMBEDDED -I.. -I. -I../../embeddedcommon -rdynamic -g
set LDFLAGS=-lkernel32 -lgdi32 -luser32 -lsetupapi -ldbghelp -lws2_32
set SOURCES=..\chash.c ..\color.c ..\configs.c ..\decompose.c ..\dft.c ..\DisplayNetwork.c ..\DisplayArray.c ..\DisplayHIDAPI.c ..\DisplayOUTDriver.c ..\DisplayPie.c ..\DrawFunctions.c ..\filter.c ..\hidapi.c ..\hook.c ..\main.c ..\os_generic.c ..\outdrivers.c ..\OutputCells.c ..\OutputLinear.c ..\OutputProminent.c ..\OutputVoronoi.c ..\parameters.c ..\sound.c ..\sound_win.c ..\sound_null.c ..\util.c ..\WinDriver.c ..\notefinder.c ..\..\embeddedcommon\DFT32.c tcc_stubs.c symbol_enumerator.c
set ARCH_SPECIFIC=-L32 C:\windows\system32\winmm.dll
set CC=C:\tcc\i386-win32-tcc.exe
set CFLAGS= -v -DHIDAPI -DWINDOWS -DWIN32 -DTCC -DRUNTIME_SYMNUM -O2 -Itccinc -DINCLUDING_EMBEDDED -rdynamic -g
set INCLUDES=-I../rawdraw -I../cnfa -I.. -I. -I../../embeddedcommon
set LDFLAGS=-lkernel32 -lole32 -lgdi32 -luser32 -lsetupapi -ldbghelp -lws2_32 -lAvrt -lopengl32
rem lots of source files
set SOURCES=..\main.c ..\chash.c ..\color.c ..\configs.c ..\decompose.c ..\dft.c ..\filter.c ^
..\outdrivers.c ..\hidapi.c ..\hook.c ..\parameters.c ..\util.c ..\notefinder.c ^
..\..\embeddedcommon\DFT32.c tcc_stubs.c symbol_enumerator.c ^
..\DisplayArray.c ..\DisplayDMX.c ..\DisplayFileWrite.c ..\DisplayHIDAPI.c ..\DisplayNetwork.c ^
..\DisplayOUTDriver.c ..\DisplayPie.c ..\DisplayRadialPoles.c ..\DisplaySHM.c ..\DisplayUSB2812.c ^
..\OutputCells.c ..\OutputLinear.c ..\OutputProminent.c ..\OutputVoronoi.c
set ARCH_SPECIFIC=-L32 C:\windows\system32\winmm.dll -DWIN32_LEAN_AND_MEAN
set CC=C:\tcc\tcc.exe
rem set CC=C:\tcc\i386-win32-tcc.exe
rem set CC=C:\tcc\x86_64-win32-tcc.exe
@echo on
%CC% %CFLAGS% %ARCH_SPECIFIC% %SOURCES% %LDFLAGS% -o ..\colorchord.exe
%CC% %CFLAGS% %INCLUDES% %ARCH_SPECIFIC% %SOURCES% %LDFLAGS% -o ..\colorchord.exe
@echo off
pause

220
docs/TheoryOfCCDFT.md Normal file
View file

@ -0,0 +1,220 @@
# Theory behind ColorChord's fast DFT.
Becaue
1) ColorChord's binning process needs to focus on notes in frequency
space instead of the bins a normal FFT produces, we have to use another
tactic to decompose incoming audio into chromatic bins instead of normal
bins.
2) We want bins which can be foldend on top of each other, i.e. each octave
can add to the previous.
3) Originally ColorChord accomplished this by using a GPU algorithm which
every frame looked at the last several milliseconds of sound and by brute
force multipled a sine and cosine for every bin against the incoming audio
with a window. This was prohibitively expensive.
4) One night I think around 2010 or 2011, Will Murnane, Charles Lohr's
roommate at the time had a pretty deep insight. Charles is not good at
math or algorithms, but Will is.
5) Much later on May 13, 2020, CaiB wanted to understand the process more,
and Will Murnane explained this in the ColorChord discord channel.
This following is not an exact transcript but has minor edits for flow,
grammar, and clarity.
Will: Hey @CaiB I helped Charles figure out the DFT-ish thing that's implemented in colorchord. I did the conceptual stuff, he wrote the actual code.
CaiB: Ooh, nice to meet you o/
Will: Basically the idea is this: if we can do something clever to get the note intensities within one octave, then we can average adjacent samples together as a simple low-pass filter, and repeat the clever thing to get notes an octave lower. So then the next insight is that we're looking for repeating patterns, and they can all be decomposed into sine waves. The reasoning behind this is complicated if you haven't seen it, but it's called fourier analysis, and there's a lot of tutorials around on the internet.
CaiB: I know enough about this stuff to kinda get the gist, but not enough to be practically useful in comprehensive understanding or implementation.
Will: So, okay, sine waves. let's start with a simple idea: suppose we just guess that the frequency is exactly some value. that is, our samples are `f(t) = sin(t*omega)` for some value of `omega`. So if we multiply our samples by `sin(t*omega)` again, we get `sin(t*omega)*sin(t*omega)` also notated `sin^2(t*omega)`
CaiB: OK, yeah (If it's easier for you, we can do a voice call so you don't have to type a bunch of stuff)
Charles: I would kind of like to document the text.
Will: The way I think of this is basically: if we happen to pick the right value of omega, those values will all tend to add up faster than if we pick the wrong value
(really, it's more like `sin(t*omega_actual) * sin(t*omega_guessed))`)
CaiB: OK, so kinda like constructive vs destructive interference but multiplied not added.
Will: Exactly! the intuition is that if we pick `omega_guessed` to be nearly equal to `omega_actual` the sum over the window we're considering will add up to a bigger value than if we pick a guess that's, say, 10% too big.
CaiB: Gotcha
Will: The next layer to add is this: We're assuming everything is gonna get decomposed into sine waves. However, a sine wave isn't just parameterized by a frequency, there's also phase information. So to account for this potential phase shift, we can multiply our signal with two functions with a 90 degree phase shift
two functions that happen to have a 90 degree phase shift are the sine and the cosine.
CaiB: Oooh, that way you can look at proprtions of the outputs to get an idea of the actual phase.
Will: So if we take `f(t) = sin(omega*t + phi)` and then do `f(t) * sin(omega * t) + f(t) * cos(omega*t)` then regardless of the value of phi the sum over our window adds up to the same value! You could extract phase information, but colorchord doesn't actually do that iirc.
CaiB: When you say "over the window", what kind of ballpark are we talking? 10 samples? 100?
Will: Usually it makes sense to think of the window as being in seconds rather than samples. Your sample rate is basically totally up to you, but humans perceive a fixed frequency range around 20hz-20khz
CaiB: I mean, for ColorChord it's just set by the system audio mode, so around 44.1k-48k usually..
Will: Yeah, but you can do it on a microcontroller like an AVR where you're doing the ADC yourself at some arbitrary rate and the window can be a sliding window, in the sense that you don't need to process entirely non-overlapping blocks. You could even get to doing this once per sample, just using the last N samples each time.
CaiB: Now if I'm remembering correctly, your window size significantly impacts what resolution your output is?
Will: The window size basically only determines the speed of reaction to new inputs. If you used a 10 second window, you wouldn't notice a new note until it had been around for a few seconds for example ok, so. we figured out how to determine how much a single frequency/note is happening in a window of time. But there's still a bunch of FP math going on, and depending on what system you're dealing with you might have a slow FPU or no sin/cos function in hardware and getting only the intensity of a single frequency doesn't give us very much pitch information at all. so how can we get more? make more guesses in parallel!
Will: In 12-tone just intonation, the rule is that each half-step is `2^(1/12) ~= 1.06` times higher than its neighbor. so within each octave, we have a separate data structure for the intensity of each half-step so we get 12 of these, with frequencies `[1, 1.06, 1.12, 1.19, 1.26, 1.33, 1.31, 1.50, ...]`
CaiB: Let's say you're guessing at 100 Hz and 110 Hz, both of them being let's say 50% strong. How do you know the difference between a sound signal that just has weak content at 100 and 110 vs strong content at 105?
Will: We'll get there... But ultimately the answer is that you can do curve fitting on the values you get out. you're right, feeding 105 hz into a thing with 100 and 110 hz guesses will give strong values for both (it's actually an exponential system, so the "halfway" point is `sqrt(100*110) = 104.88`, not `105`, but the point stands)
but we can also look at the adjacent bins even further outside to give an indication of where the peak is. suppose we're dealing with a `108 hz` signal, and we have bins at `90, 100, 110, 120`.
CaiB: Aah.
Will: We can fit a curve to the intensities in those bins, and we'll find a peak at approximately `108hz`. so as long as we have a decent number of bins, the location of the peak should be pretty easy to find.
CaiB: Because one at `108` is a sharper peak (less spread to further bins) than two at `100` and `110`.
Will: (so ultimately, the exact values of the bins don't really matter, but it's easier for me to think of it as a 12-tone scale) well... there isn't a bin at 108hz, only one at `100` and `110`. Usually music tends to have harmonic content, so you don't see `100hz` played next to `110hz`, you see a single `108hz` note. so colorchord basically assumes you're playing music and you would prefer the answer `108hz`.
CaiB: Sorry, I meant one peak in frequency content at `108hz` would mean bins `90hz` and `120hz` see lower numbers than an input with content at `100hz` and `110hz`, where `90hz` and `120hz` would see larger numbers.
Editorial Note from Charles: The section below is an extremely powerful concept. It provides a significant speedup from needing a GPU to being able to run on a CPU. That and realizing we could progressively update.
Will: Yeah, the spread of the distribution is important. And if you added more bins, you'd be able to see the distinction between `100hz+110hz` and `108hz` alone the expensive part of adding more resolution is adding more bins within a single octave, but there's a clever thing we can do to get all the octaves we can hear for only 2x the cost of doing a single octave. Effectively all of our base frequency bins are in the highest octave we care about. then by doing the 2x averaging i mentioned earlier, we get one octave lower. And by repeatedly averaging samples, we get additional octaves lower basically, think of it like this:
* Every sample we calculate octave 8.
* Every 2nd sample we average the last 2 samples from octave 8, and use them to calculate octave 7.
* Every 4th sample we average the last 2 samples from octave 7, and use them to calculate octave 6.
* 8th, 2, 6, 5
* 16th, 2, 5, 4
* ...
By cleverly interleaving the work, we can do a fixed amount of averaging-and-higher-octave-stuff every tick the octave we work on at each tick is determined by the number of zeroes at the end of the binary representation of the tick number.
`0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, ...`
CaiB: Neat. I'm still not quite understanding how the 2x averaging works though, you'd still need to compare it to a sine wave at the lower frequency, right?
SamE: Question: Why not use the aliasing property of the DFT to get the other frequency bins?
Will: By averaging, we're effectively getting samples that count for twice as much time, or half as much frequency, which means notes exactly one octave lower.
Will: @SamE: I may well not understand it well enough to take advantage of it correctly, but let's talk it out a bit.
SamE: TBH: I don't understand it either.
Will: @CaiB fyi here's [the definition of the DFT on wikipedia](https://en.wikipedia.org/wiki/Discrete_Fourier_transform#Definition) so you can see how the formalism correlates to the extremely-informal construction I gave above.
Will: Is there good math formatting maybe? `$f(x) = 3x$`
CaiB: Yeah, that just flies right over my head by the time I get a few sentences in. Haha, Discord is not fancy enough for anything other than the most basic of formatting.
Will: The second part with the sines and the cosines is basically what I was talking about. @Sam E so my understanding is basically that because of aliasing, frequencies higher than your sample rate wind up reflected off your sample rate. Which we avoid by using averaging as a low pass filter, so that there can be no frequencies higher than sample rate. iow (?) the highest octave is directly from captured audio samples, so it doesn't contain any information higher than nyquist allows. each of the lower octaves is downsampled to remove one octave off the top.
CaiB: OH, you're taking the average of every two samples, then treating that as the _same_ sample rate.
Will: Yes. you want to only keep half as many samples around, though, or you'll be considering twice as long a window in real-time.
CaiB: Essentially speeding up the audio by 2x, then comparing it to the same standard sine waves.
Will: Yep!
CaiB: Ooooh. I was thinking you were taking a 48kHz sample rate signal, averaging them and treating it as a 24kHz signal, which would just get you a worse version of the same result.
Will: Whoa, here's a picture from wikipedia that describes things very similar to what i'm talking about! ![WOLA Channelizer](https://en.wikipedia.org/wiki/Filter_bank#/media/File:WOLA_channelizer_example.png)
CaiB: And that way you can precompute the comparisons, one sine and one cosine for each bin in only one octave?
Will: Yeah! the other trick you can play is to just add the samples instead of averaging. this means you take (let's say) two 16-bit samples, and get one 17-bit result which is twice as large on average as a highest-octave 16-bit sample but you only have half as many of those 17-bit samples in your window, and by definition their sum is the same as the sum of the 16-bit samples so the magnitudes you get per octave are comparable.
CaiB: Then divide the output by two. Half the resolution, but about half the calculations. Oh!
Will: In other words you don't get a reading that says "note 6 in octave 3 is twice as loud as in octave 4" when in fact they're equal.
SamE: What I was saying is trying to be clever (always dangerous) and try and use the aliasing to stack all of the information we want into the same bins for us. One would probably have to add in a few more DSP filters in order to flip some of the higher frequency images. I might have to experiment with this later.
CaiB: So on the output, you're updating the top octave every sample, the second-highest octave every other sample, etc, until the lowest is only updating once in a while (comparatively)? And that still works OK?
Will: Using some kind of clever sub-sampling to fold things around might work: [Alias wikipedia page, section Folding](https://en.wikipedia.org/wiki/Aliasing#Folding) Yeah, if you think about it the bottom octave can't change too fast or it wouldn't be in the bottom octave anymore :open_mouth:
CaiB: But if a low tone starts and stops playing repeatedly, you may end up completely losing that info, and just seeing a weaker signal instead of the actual pulsing.
Will: Take a pathological example: consider a wave where the top half of each pulse is at 40 hz, and the bottom is at 38hz wellllll... that's really just a 39hz wave with some distortion!
CaiB: I'm thinking more along the lines of take a 40Hz sine signal, then multiply it by a 0-1 square wave at 200 Hz. You'd just see the 40Hz at a intensity corresponding to the duty cycle of the square wave, not the actual on-off pattern.
Will: Well, you'd see an output at 40hz corresponding to the amount of time that the 40hz signal is present for sure. but you'd also see a strong 200hz (and many higher-frequency!) signals corresponding to the 200hz square wave
CaiB: But you have no way of knowing that the 40Hz signal is actually starting and stopping vs just being weaker.
Will: a description of how you got to a particular waveform isn't the same as a description of what it sounds like :wink:
SamE: In the frequency domain, aren't they essentially the same thing.
Will: And if you want a system that changes its fundamental answer 400 times a second, you probably do not want an audio system.
CaiB: @SamE, My point is if you were to just watch a live graph of the frequency domain, you'd see the low frequencies refresh slowly, and won't be able to see a pulsing signal down there as actually pulsing. Whereas a 5kHz signal turning on and off at that same 200Hz you could clearly see turning on and off because your graph refreshes fast enough up there.
Will: I think if you wanted to see things at that level you could, by subtracting out old samples from that octave and adding in new ones live... but i don't think that audio sounds like you think.
SamE: Okay, I need to turn this off so that I can finish a presentation for work. Thanks for explain this to us mere mortals. :slight_smile:
CaiB: OK, it probably wouldn't be useful to audio but for actually analyzing a signal thoroughly it may be a problem. But I guess that's outside the scope I might try to implement this from scratch when I have some free time, since trying to look at the existing code just makes my brain stop working. So let's say your window is 1000 samples long. You're doing `1000 samples * (1 for sin + 1 for cos) * 24 bins` number of calculations every single sample, just to get the highest octave? Also, is this something you came up with yourself? If so, how? :exploding_head:
Will: Example Audio (chop.wav) / (chop.pv).
CaiB: Hmm, how is the actual sin/cos multiplication step done? Because if you just go in with no previous info, (assuming 1000 sample window) you'd need to multiply every sample by the precomputed sin and cos, so 2000 multiplications. Then, assuming you don't want phase info, you'd maybe just add each sin and cos value together, but then you also need to sum all of them up in order to compare the entire window, so 1000 adds. And now do that for every bin, so 24 times just for that one octave. That's a lot of math.
Will: there are a bunch of tricks you can play to not do so much work. for example, instead of using processing you could use more memory: you can remember the results of `sin*sample + cos*sample` and then just subtract the sample that's falling out of your window and add the one that's coming in. Or approximate your windowing function using an IIR filter: just multiply your "last" value by something like 0.999 each sample, and the influence of old samples will be forgotten implicitly over time.
CaiB: Charles does love his IIRs (assuming he's the one that made the post-DFT analysis).
Editorial Note: ColorChord almost exclusively uses IIRs for this purpose. In practice they work/feel better than other triangular solutions because of the logarithmic nature of how we perceive sound. Also, yes, Charles rarely works on a project more than a month before he finds a use for IIRs.
Will: I built the description in my head of why this works, but i did so by reading a lot on DFT and DTFT and taking a class on complex analysis. stay in school, i guess? Hah, so he does.
CaiB: "school" did a great job of making me hate every subject I took, even ones I used to love previously...
Will: PS: the audio file above is generated using the method you mentioned above. i think it shows that mostly what you hear is 200hz and harmonics of that.
CaiB: Not quite, your square wave is -1 to 1, but I get the point.
Will: yeah, I agree that school can be a backwards way of approaching subjects if you can become interested in the subject first, and then learn the formality behind it, it can be a great way of figuring out how to apply things, but if you have to slog through the formality with no idea where you're going it's not exactly gonna instill a love of the subject. Yup, so it is, change the definition of `c` to be all that stuff `+1` and it'll be what you were talking about. Doesn't sound much different though. (see chop2.wav)
![chopfast.png](https://raw.githubusercontent.com/cnlohr/colorchord/master/docs/TheoryOfCCDFT/chopfast.png)
CaiB: It's more just that the methods taught are often so abstract it feels pointless. An entire class I took was just doing RLC-type circuit analysis using various methods like Laplace transforms. When in the real world, you just plug it into a calculator and will never use that unless you are the 0.1% of people who develop those calculators or other weird applications... 40 and 200 were a bad choice, speeding it up by 4x, you can definitely hear both tones. (See chopfast.wav)
Will: Sure, but my point earlier is that colorchord will probably tell you "this is mostly 200hz content, with some 40" because that's what it sounds like.
CaiB: Yeah, good enough.
Will: Not "this is 40hz, now nothing, now 40hz, now nothing..."
CaiB: I mean, by the time it shows up, 200Hz changes would be completely smoothed to oblivion anyways. It's just steady this. ![cc-screenshot3.png](https://raw.githubusercontent.com/cnlohr/colorchord/master/docs/TheoryOfCCDFT/cc-screenshot3.png)
Will: Yeah, what's happening in colorchord is that it squashes all the octaves together.
CaiB: Yeah, I understand all the processing happening after the DFT. Just a couple of days ago I went through line-by-line, commented and rewrote some parts to actually understand what it does.
Will: So it sees 40hz, and 200/4 = 50hz, which is a perfect major third: (Wikipedia article on Major Thirds)[https://en.wikipedia.org/wiki/Major_third] so it makes green for one of those and red for the other.
CaiB: E1-ish = 40Hz, G1-ish = 50Hz. Mine is looking at the 4x version because ColorChord doesn't even look below 55Hz, so more like E3ish and G3ish. Also because my speakers can't output 40Hz enough to be useful here, so I 4xed it to hear the low tone.
Will: yeah, eventually you need a cutoff on how low you look, aka how many times you average pairs of samples. Also, depending on what shape of sine wave you pick for the highest rate, you can limit how high you listen but still get good resolution and you don't even necessarily need to use many samples, or good-quality samples for your sin/cos tables! Like your high-res sin table, in full, could be: `[0, 1, 0, -1]` And the one at the other end of the octave could be `[1, -1, 1, -1]` or so.
CaiB: So assuming 5 octaves like is used, and no clever interleaving, every `2 << (5 - 1) = 32` samples you need to calculate all octaves
Will: Yeah, but over those 32 samples you need to do: highest octave: 32 calculations next-highest: `16, 8, 4, 2, 1` total: 63 calculations (and 31 sums/averages)
CaiB: Wouldn't it be 63 * 2 because `sin` and `cos`.
Will: Well, sure, for some complicated version of "calculations"
CaiB: OK, fair.
Will: And you need to do some adds and so on. This is all handwavy.
CaiB: Hold up, 4 element tables? I'm amazed you don't just get literal garbage out of that.
Will: well it's not great, that's for sure!
CaiB: Looks like the table in the actual implementation is 256 long.
Will: Since you're aligning a longer sequence to it, you wind up re-using elements often enough to make it kinda work buuut don't start with a 4-element table as a goal. definitely a distorted sine wave.
CaiB: Yeah, like said when I have time, I'm going to try to recreate it from scratch, now that I can actually see the logic behind it. A decent while back I ported ColorChord to C#, and copy-pasted/re-wrote everything but the DFT because it looks so alien and scary, so in my fork it literally is still the original C code sitting there all alone. Well thank you very much for taking the time to explain this, very much appreciated
Will: Happy to help! hope your reimplementation goes well.

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

BIN
docs/TheoryOfCCDFT/chop.wav Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

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