├── .gitignore ├── .gitmodules ├── junologue-chorus-controls.png ├── manifest.json ├── Makefile ├── .vscode ├── settings.json └── c_cpp_properties.json ├── LICENSE ├── README.md ├── tpl └── _unit.c ├── junologue-chorus.cpp └── junologue-chorus.mk /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | junologue-chorus.*unit 3 | junologue-chorus-*.zip -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "logue-sdk"] 2 | path = logue-sdk 3 | url = https://github.com/korginc/logue-sdk.git 4 | -------------------------------------------------------------------------------- /junologue-chorus-controls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterall/junologue-chorus/HEAD/junologue-chorus-controls.png -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "header" : 3 | { 4 | "platform" : "will-be-replaced", 5 | "module" : "modfx", 6 | "api" : "1.1-0", 7 | "dev_id" : 0, 8 | "prg_id" : 0, 9 | "version" : "will-be-replaced", 10 | "name" : "Juno-60", 11 | "num_param" : 0 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TOPTARGETS := all 2 | 3 | PLATFORMS := prologue minilogue-xd nutekt-digital 4 | 5 | VERSION=1.2-1 6 | PACKAGE = junologue-chorus-$(VERSION).zip 7 | 8 | $(TOPTARGETS): $(PLATFORMS) package 9 | 10 | clean: 11 | @echo Cleaning ./build and packages 12 | @rm -fR .dep ./build 13 | @rm -f junologue-chorus.*unit 14 | @rm -f junologue-chorus-*.zip 15 | 16 | $(PLATFORMS): 17 | @PLATFORM=$@ VERSION=$(VERSION) $(MAKE) -f junologue-chorus.mk $(MAKECMDGOALS) 18 | 19 | package: 20 | @echo Packaging to ./${PACKAGE} 21 | @zip -q9 - *.*unit > ${PACKAGE} 22 | @echo All done -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "random": "cpp", 4 | "*.tcc": "cpp", 5 | "cstdint": "cpp", 6 | "atomic": "cpp", 7 | "bitset": "cpp", 8 | "chrono": "cpp", 9 | "complex": "cpp", 10 | "deque": "cpp", 11 | "forward_list": "cpp", 12 | "list": "cpp", 13 | "string": "cpp", 14 | "unordered_map": "cpp", 15 | "unordered_set": "cpp", 16 | "vector": "cpp", 17 | "functional": "cpp", 18 | "rope": "cpp", 19 | "slist": "cpp", 20 | "fstream": "cpp", 21 | "future": "cpp", 22 | "istream": "cpp", 23 | "mutex": "cpp", 24 | "ostream": "cpp", 25 | "scoped_allocator": "cpp", 26 | "sstream": "cpp", 27 | "streambuf": "cpp", 28 | "system_error": "cpp", 29 | "thread": "cpp", 30 | "regex": "cpp", 31 | "tuple": "cpp", 32 | "valarray": "cpp" 33 | } 34 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 Peter Allwin 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "gcc-arm-none-eabi", 5 | "includePath": [ 6 | "${workspaceFolder}/logue-sdk/platform/nutekt-digital/inc", 7 | "${workspaceFolder}/logue-sdk/platform/nutekt-digital/inc/dsp", 8 | "${workspaceFolder}/logue-sdk/platform/nutekt-digital/inc/utils", 9 | "${workspaceFolder}/logue-sdk/platform/ext/CMSIS/CMSIS/Include" 10 | ], 11 | "defines": [ 12 | "STM32F446xE", 13 | "CORTEX_USE_FPU=TRUE", 14 | "DARM_MATH_CM4", 15 | "__FPU_PRESENT", 16 | "_USE_MATH_DEFINES", 17 | "uint32_t=unsigned long int", 18 | "SUB_ENABLED=true" 19 | ], 20 | "compilerPath": "${workspaceFolder}/logue-sdk/tools/gcc/gcc-arm-none-eabi-5_4-2016q3/bin/arm-none-eabi-gcc", 21 | "cStandard": "c11", 22 | "cppStandard": "c++14", 23 | "intelliSenseMode": "gcc-arm" 24 | } 25 | ], 26 | "version": 4 27 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Junologue Chorus - Juno 60 Chorus Emulation 2 | ## For Korg Prologue, Minilogue XD and Nu:tekt NTS-1 3 | 4 | ![Controls](https://raw.githubusercontent.com/peterall/junologue-chorus/main/junologue-chorus-controls.png) 5 | 6 | **Speed** knob switches between the three modes: **I, I-II, II**. Within the mode the timbre is brightened by turning clockwise. 7 | 8 | **Depth** knob controls dry/wet ratio. The sweet Juno sound is somewhere 12 o'clock. 9 | 10 | Audio examples are available on [Soundcloud](https://soundcloud.com/vriff-polo/sets/junologue-juno-60-chorus-for-korg-prologue-minilogue-xd-and-nts-1). 11 | 12 | _Note that the I-II* mode is different from the Juno 60 "both buttons in" mode. I'm not a big fan of the original mono vibrato effect so in this emulation I-II* mode enables both choruses in parallel, similar to the TAL U-NO-LX._ 13 | 14 | Thanks to [Andy Harman](https://github.com/pendragon-andyh) and [JP Cimalando](https://github.com/jpcima) for Juno 60 chorus research, and [Émilie Gillet](https://github.com/pichenettes) for [stmlib](https://github.com/pichenettes/stmlib) Softlimit function. 15 | 16 | Have fun! -------------------------------------------------------------------------------- /tpl/_unit.c: -------------------------------------------------------------------------------- 1 | /* 2 | BSD 3-Clause License 3 | 4 | Copyright (c) 2018, KORG INC. 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | 17 | * Neither the name of the copyright holder nor the names of its 18 | contributors may be used to endorse or promote products derived from 19 | this software without specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 25 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | //*/ 33 | 34 | /** 35 | * @file _unit.c 36 | * @brief Modulation effect entry template. 37 | * 38 | * @addtogroup api 39 | * @{ 40 | */ 41 | 42 | #include "usermodfx.h" 43 | 44 | /*===========================================================================*/ 45 | /* Externs and Types. */ 46 | /*===========================================================================*/ 47 | 48 | /** 49 | * @name Externs and Types. 50 | * @{ 51 | */ 52 | 53 | extern uint8_t _bss_start; 54 | extern uint8_t _bss_end; 55 | 56 | extern void (*__init_array_start []) (void); 57 | extern void (*__init_array_end []) (void); 58 | 59 | typedef void (*__init_fptr)(void); 60 | 61 | /** @} */ 62 | 63 | /*===========================================================================*/ 64 | /* Local Constants and Vars. */ 65 | /*===========================================================================*/ 66 | 67 | /** 68 | * @name Local Constants and Vars. 69 | * @{ 70 | */ 71 | 72 | __attribute__((used, section(".hooks"))) 73 | static const user_modfx_hook_table_t s_hook_table = { 74 | .magic = {'U','M','O','D'}, 75 | .api = USER_API_VERSION, 76 | .platform = USER_TARGET_PLATFORM>>8, 77 | .reserved0 = {0}, 78 | .func_entry = _entry, 79 | .func_process = _hook_process, 80 | .func_suspend = _hook_suspend, 81 | .func_resume = _hook_resume, 82 | .func_param = _hook_param, 83 | .reserved1 = {0} 84 | }; 85 | 86 | /** @} */ 87 | 88 | /*===========================================================================*/ 89 | /* Default Hooks. */ 90 | /*===========================================================================*/ 91 | 92 | /** 93 | * @name Default Hooks. 94 | * @{ 95 | */ 96 | 97 | __attribute__((used)) 98 | void _entry(uint32_t platform, uint32_t api) 99 | { 100 | // Ensure zero-clear BSS segment 101 | uint8_t * __restrict bss_p = (uint8_t *)&_bss_start; 102 | const uint8_t * const bss_e = (uint8_t *)&_bss_end; 103 | 104 | for (; bss_p != bss_e;) 105 | *(bss_p++) = 0; 106 | 107 | // Call constructors if any. 108 | const size_t count = __init_array_end - __init_array_start; 109 | for (size_t i = 0; i 8 | 9 | static constexpr float samplerate = 48000.f; 10 | static constexpr float _0db = 1.f, _3db = M_1_SQRT2, _infdb = 0.f; 11 | 12 | typedef struct { 13 | float min; 14 | float max; 15 | } range_t; 16 | 17 | static constexpr f32pair_t delay_gains[] = { 18 | { _0db, _infdb }, 19 | { _3db, _3db }, 20 | { _infdb, _0db } 21 | }; 22 | 23 | static constexpr size_t mode_count = sizeof(delay_gains) / sizeof(f32pair_t); 24 | 25 | static constexpr range_t delay_times[] = { 26 | { .min = 0.00154, .max = 0.00515 }, 27 | { .min = 0.00151, .max = 0.00540 } 28 | }; 29 | 30 | static constexpr float rates[] = { 31 | 0.513f, 0.863f 32 | }; 33 | 34 | static constexpr range_t pre_lpf_cutoff = { 35 | .min = 2000.f, .max = 23500.f 36 | }; 37 | 38 | static constexpr range_t post_lpf_cutoff = { 39 | .min = 6000.f, .max = 23500.f 40 | }; 41 | 42 | constexpr uint32_t nextpow2_u32_constexpr(uint32_t x) { 43 | x--; 44 | x |= x>>1; x |= x>>2; 45 | x |= x>>4; x |= x>>8; 46 | x |= x>>16; 47 | return ++x; 48 | } 49 | 50 | inline float fastsqrt(float number) { 51 | float x2 = number * 0.5f; 52 | float y = number; 53 | long i = * ( long * ) &y; 54 | i = 0x5f3759df - ( i >> 1 ); 55 | y = * ( float * ) &i; 56 | y = y * ( 1.5f - ( x2 * y * y ) ); 57 | y = y * ( 1.5f - ( x2 * y * y ) ); 58 | 59 | return 1.f / fabsf(y); 60 | } 61 | 62 | inline float SoftLimit(float x) { 63 | return x * (27.0f + x * x) / (27.0f + 9.0f * x * x); 64 | } 65 | 66 | static constexpr size_t delay_size = 67 | nextpow2_u32_constexpr(static_cast(std::max(delay_times[0].max, delay_times[1].max) * samplerate) + 1); 68 | 69 | static dsp::DelayLine main_delay, sub_delay;; 70 | static __sdram float main_delay_ram[delay_size], sub_delay_ram[delay_size]; 71 | 72 | static dsp::SimpleLFO lfo_1, lfo_2; 73 | 74 | static dsp::BiQuad main_pre_lpf, sub_pre_lpf, 75 | main_post_lpf_l, main_post_lpf_r, 76 | sub_post_lpf_l, sub_post_lpf_r; 77 | 78 | static f32pair_t delay_gain = delay_gains[0]; 79 | static float dry_gain = _3db, wet_gain = _3db; 80 | 81 | // the chips are effectively sampling at about 70kHz 82 | // a 12dB low-pass filter is used before the signal is sampled 83 | 84 | inline void setLpfCutoff(const float fr) { 85 | main_pre_lpf.mCoeffs.setFOLP(fx_tanpif(linintf(fr * fr, 86 | pre_lpf_cutoff.min / samplerate, pre_lpf_cutoff.max / samplerate))); 87 | sub_pre_lpf.mCoeffs = main_pre_lpf.mCoeffs; 88 | 89 | main_post_lpf_l.mCoeffs.setFOLP(fx_tanpif(linintf(fr * fr, 90 | post_lpf_cutoff.min / samplerate, post_lpf_cutoff.max / samplerate))); 91 | main_post_lpf_r.mCoeffs = main_post_lpf_l.mCoeffs; 92 | sub_post_lpf_l.mCoeffs = main_post_lpf_l.mCoeffs; 93 | sub_post_lpf_r.mCoeffs = main_post_lpf_l.mCoeffs; 94 | } 95 | 96 | void MODFX_INIT(uint32_t platform, uint32_t api) 97 | { 98 | main_delay.setMemory(main_delay_ram, delay_size); 99 | main_delay.clear(); 100 | sub_delay.setMemory(sub_delay_ram, delay_size); 101 | sub_delay.clear(); 102 | 103 | main_pre_lpf.flush(); 104 | sub_pre_lpf.flush(); 105 | 106 | lfo_1.reset(); 107 | lfo_1.setF0(rates[0], 1.f / samplerate); 108 | lfo_2.reset(); 109 | lfo_2.setF0(rates[1], 1.f / samplerate); 110 | 111 | main_post_lpf_l.flush(); 112 | main_post_lpf_r.flush(); 113 | sub_post_lpf_l.flush(); 114 | sub_post_lpf_r.flush(); 115 | 116 | setLpfCutoff(.5f); 117 | } 118 | 119 | inline float readDelay(dsp::DelayLine &delay, const range_t &t, const float p) { 120 | return delay.readFrac((t.min + ((t.max - t.min) * p)) * samplerate); 121 | } 122 | 123 | inline f32pair_t readDelays(dsp::DelayLine &delay, const float p) { 124 | return { 125 | readDelay(delay, delay_times[0], p), 126 | readDelay(delay, delay_times[1], 1.f - p) 127 | }; 128 | } 129 | 130 | inline f32pair_t f32pair_process_fo(dsp::BiQuad &fa, dsp::BiQuad &fb, const f32pair_t &s) { 131 | return { fa.process_fo(s.a), fb.process_fo(s.b) }; 132 | } 133 | 134 | inline float f32pair_reduce(const f32pair_t &p) { 135 | return (p.a + p.b); //* _3db; 136 | } 137 | 138 | void MODFX_PROCESS(const float *main_xn, float *main_yn, 139 | const float *sub_xn, float *sub_yn, 140 | uint32_t frames) 141 | { 142 | const f32pair_t __restrict__ *main_input = reinterpret_cast(main_xn); 143 | f32pair_t __restrict__ *main_output = reinterpret_cast(main_yn); 144 | 145 | const f32pair_t __restrict__ *sub_input = reinterpret_cast(sub_xn); 146 | f32pair_t __restrict__ *sub_output = reinterpret_cast(sub_yn); 147 | 148 | const f32pair_t *main_output_end = main_output + frames; 149 | 150 | while(main_output != main_output_end) { 151 | main_delay.write(main_pre_lpf.process_fo(SoftLimit(f32pair_reduce(*main_input)))); 152 | if(SUB_ENABLED) { 153 | sub_delay.write(sub_pre_lpf.process_fo(SoftLimit(f32pair_reduce(*sub_input)))); 154 | } 155 | 156 | lfo_1.cycle(); 157 | lfo_2.cycle(); 158 | 159 | const float lfo_val1 = lfo_1.triangle_uni(), lfo_val2 = lfo_2.triangle_uni(); 160 | 161 | const auto main = f32pair_add( 162 | f32pair_mulscal(readDelays(main_delay, lfo_val1), delay_gain.a), 163 | f32pair_mulscal(readDelays(main_delay, lfo_val2), delay_gain.b)); 164 | 165 | *main_output++ = f32pair_add( 166 | f32pair_mulscal(*main_input++, dry_gain), 167 | f32pair_mulscal(f32pair_process_fo(main_post_lpf_l, main_post_lpf_r, main), wet_gain)); 168 | 169 | if(SUB_ENABLED) { 170 | const auto sub = f32pair_add( 171 | f32pair_mulscal(readDelays(sub_delay, lfo_val1), delay_gain.a), 172 | f32pair_mulscal(readDelays(sub_delay, lfo_val2), delay_gain.b)); 173 | 174 | *sub_output++ = f32pair_add( 175 | f32pair_mulscal(*sub_input++, dry_gain), 176 | f32pair_mulscal(f32pair_process_fo(sub_post_lpf_l, sub_post_lpf_r, sub), wet_gain)); 177 | } 178 | } 179 | } 180 | 181 | inline f32pair_t interpolateGain(const float fr, const uint32_t x, const uint32_t y) { 182 | return { 183 | linintf(fr, delay_gains[x].a, delay_gains[y].a), 184 | linintf(fr, delay_gains[x].b, delay_gains[y].b) 185 | }; 186 | } 187 | 188 | inline void updateTimeParam(const float value) { 189 | const auto value_scaled = value * (static_cast(mode_count) - 0.0001f); 190 | const auto mode = static_cast(value_scaled); 191 | const auto mode_frac = value_scaled - mode; 192 | 193 | if(mode_frac < .1f && mode > 0) { 194 | const float fr = .5f + (mode_frac * 5.f); 195 | delay_gain = interpolateGain(fr, mode - 1, mode); 196 | setLpfCutoff(1.f - fr); 197 | 198 | } else if(mode_frac > .9f && mode < mode_count - 1) { 199 | const float fr = (mode_frac - .9f) * 5.f; 200 | delay_gain = interpolateGain(fr, mode, mode + 1); 201 | setLpfCutoff(1.f - fr); 202 | 203 | } else { 204 | delay_gain = delay_gains[mode]; 205 | setLpfCutoff(std::max(0.f, std::min((mode_frac - .1f) * 1.25f, 1.f))); 206 | } 207 | } 208 | 209 | void MODFX_PARAM(uint8_t index, int32_t value) 210 | { 211 | const float valf = q31_to_f32(value); 212 | switch (index) { 213 | case k_user_modfx_param_time: 214 | updateTimeParam(valf); 215 | break; 216 | case k_user_modfx_param_depth: 217 | dry_gain = fastsqrt(1.f - valf); 218 | wet_gain = fastsqrt(valf); 219 | break; 220 | default: 221 | break; 222 | } 223 | } 224 | 225 | -------------------------------------------------------------------------------- /junologue-chorus.mk: -------------------------------------------------------------------------------- 1 | # ############################################################################# 2 | # Prologue Mod. FX Makefile 3 | # ############################################################################# 4 | 5 | PROJECT = junologue-chorus 6 | 7 | UCXXSRC = junologue-chorus.cpp 8 | 9 | ifeq ($(OS),Windows_NT) 10 | ifeq ($(MSYSTEM), MSYS) 11 | detected_OS := $(shell uname -s) 12 | else 13 | detected_OS := Windows 14 | endif 15 | else 16 | detected_OS := $(shell uname -s) 17 | endif 18 | 19 | PLATFORMDIR = logue-sdk/platform/$(PLATFORM) 20 | PROJECTDIR = . 21 | TOOLSDIR = $(PLATFORMDIR)/../../tools 22 | EXTDIR = $(PLATFORMDIR)/../ext 23 | 24 | CMSISDIR = $(EXTDIR)/CMSIS/CMSIS 25 | 26 | # ############################################################################# 27 | # Set platform specific options 28 | # ############################################################################# 29 | 30 | ifeq ($(PLATFORM), prologue) 31 | PKGSUFFIX = prlgunit 32 | SUB_ENABLED = true 33 | endif 34 | 35 | ifeq ($(PLATFORM), minilogue-xd) 36 | PKGSUFFIX = mnlgxdunit 37 | SUB_ENABLED = false 38 | endif 39 | 40 | ifeq ($(PLATFORM), nutekt-digital) 41 | PKGSUFFIX = ntkdigunit 42 | SUB_ENABLED = false 43 | endif 44 | 45 | # ############################################################################# 46 | # configure archive utility 47 | # ############################################################################# 48 | 49 | ZIP = /usr/bin/zip 50 | ZIP_ARGS = -r -m -q 51 | 52 | ifeq ($(OS),Windows_NT) 53 | ifneq ($(MSYSTEM), MSYS) 54 | ifneq ($(MSYSTEM), MINGW64) 55 | ZIP = $(TOOLSDIR)/zip/bin/zip 56 | endif 57 | endif 58 | endif 59 | 60 | # ############################################################################# 61 | # configure cross compilation 62 | # ############################################################################# 63 | 64 | MCU = cortex-m4 65 | 66 | GCC_TARGET = arm-none-eabi- 67 | GCC_BIN_PATH = $(TOOLSDIR)/gcc/gcc-arm-none-eabi-5_4-2016q3/bin 68 | 69 | CC = $(GCC_BIN_PATH)/$(GCC_TARGET)gcc 70 | CXXC = $(GCC_BIN_PATH)/$(GCC_TARGET)g++ 71 | LD = $(GCC_BIN_PATH)/$(GCC_TARGET)gcc 72 | #LD = $(GCC_BIN_PATH)/$(GCC_TARGET)g++ 73 | CP = $(GCC_BIN_PATH)/$(GCC_TARGET)objcopy 74 | AS = $(GCC_BIN_PATH)/$(GCC_TARGET)gcc -x assembler-with-cpp 75 | AR = $(GCC_BIN_PATH)/$(GCC_TARGET)ar 76 | OD = $(GCC_BIN_PATH)/$(GCC_TARGET)objdump 77 | SZ = $(GCC_BIN_PATH)/$(GCC_TARGET)size 78 | 79 | HEX = $(CP) -O ihex 80 | BIN = $(CP) -O binary 81 | 82 | LDDIR = $(PLATFORMDIR)/modfx/ld 83 | RULESPATH = $(LDDIR) 84 | LDSCRIPT = $(LDDIR)/usermodfx.ld 85 | DLIBS = -lm 86 | 87 | DADEFS = -DSTM32F446xE -DCORTEX_USE_FPU=TRUE -DARM_MATH_CM4 88 | DDEFS = -DSTM32F446xE -DCORTEX_USE_FPU=TRUE -DARM_MATH_CM4 -D__FPU_PRESENT -DSUB_ENABLED=$(SUB_ENABLED) 89 | 90 | COPT = -std=c11 -mstructure-size-boundary=8 -funroll-loops -ffast-math 91 | CXXOPT = -std=c++14 -fno-rtti -fno-exceptions -fno-non-call-exceptions -funroll-loops -ffast-math 92 | 93 | LDOPT = -Xlinker --just-symbols=$(LDDIR)/main_api.syms 94 | 95 | CWARN = -W -Wall -Wextra 96 | CXXWARN = 97 | 98 | FPU_OPTS = -mfloat-abi=hard -mfpu=fpv4-sp-d16 -fsingle-precision-constant -fcheck-new 99 | 100 | OPT = -g -O3 -mlittle-endian 101 | OPT += $(FPU_OPTS) 102 | #OPT += -flto 103 | 104 | TOPT = -mthumb -mno-thumb-interwork -DTHUMB_NO_INTERWORKING -DTHUMB_PRESENT 105 | 106 | 107 | # ############################################################################# 108 | # set targets and directories 109 | # ############################################################################# 110 | 111 | PKGDIR = $(PROJECT) 112 | PKGARCH = $(PROJECT).$(PKGSUFFIX) 113 | MANIFEST = manifest.json 114 | PAYLOAD = payload.bin 115 | BUILDDIR = $(PROJECTDIR)/build/$(PLATFORM) 116 | OBJDIR = $(BUILDDIR)/obj 117 | LSTDIR = $(BUILDDIR)/lst 118 | 119 | ASMSRC = $(UASMSRC) 120 | 121 | ASMXSRC = $(UASMXSRC) 122 | 123 | CSRC = $(PROJECTDIR)/tpl/_unit.c $(UCSRC) 124 | 125 | CXXSRC = $(UCXXSRC) 126 | 127 | vpath %.s $(sort $(dir $(ASMSRC))) 128 | vpath %.S $(sort $(dir $(ASMXSRC))) 129 | vpath %.c $(sort $(dir $(CSRC))) 130 | vpath %.cpp $(sort $(dir $(CXXSRC))) 131 | 132 | ASMOBJS := $(addprefix $(OBJDIR)/, $(notdir $(ASMSRC:.s=.o))) 133 | ASMXOBJS := $(addprefix $(OBJDIR)/, $(notdir $(ASMXSRC:.S=.o))) 134 | COBJS := $(addprefix $(OBJDIR)/, $(notdir $(CSRC:.c=.o))) 135 | CXXOBJS := $(addprefix $(OBJDIR)/, $(notdir $(CXXSRC:.cpp=.o))) 136 | 137 | OBJS := $(ASMXOBJS) $(ASMOBJS) $(COBJS) $(CXXOBJS) 138 | 139 | DINCDIR = $(PROJECTDIR)/inc \ 140 | $(PROJECTDIR)/inc/api \ 141 | $(PLATFORMDIR)/inc \ 142 | $(PLATFORMDIR)/inc/dsp \ 143 | $(PLATFORMDIR)/inc/utils \ 144 | $(CMSISDIR)/Include 145 | 146 | INCDIR := $(patsubst %,-I%,$(DINCDIR) $(UINCDIR)) 147 | 148 | DEFS := $(DDEFS) $(UDEFS) 149 | ADEFS := $(DADEFS) $(UADEFS) 150 | 151 | LIBS := $(DLIBS) $(ULIBS) 152 | 153 | LIBDIR := $(patsubst %,-I%,$(DLIBDIR) $(ULIBDIR)) 154 | 155 | 156 | # ############################################################################# 157 | # compiler flags 158 | # ############################################################################# 159 | 160 | MCFLAGS := -mcpu=$(MCU) 161 | ODFLAGS = -x --syms 162 | ASFLAGS = $(MCFLAGS) -g $(TOPT) -Wa,-alms=$(LSTDIR)/$(notdir $(<:.s=.lst)) $(ADEFS) 163 | ASXFLAGS = $(MCFLAGS) -g $(TOPT) -Wa,-alms=$(LSTDIR)/$(notdir $(<:.S=.lst)) $(ADEFS) 164 | CFLAGS = $(MCFLAGS) $(TOPT) $(OPT) $(COPT) $(CWARN) -Wa,-alms=$(LSTDIR)/$(notdir $(<:.c=.lst)) $(DEFS) 165 | CXXFLAGS = $(MCFLAGS) $(TOPT) $(OPT) $(CXXOPT) $(CXXWARN) -Wa,-alms=$(LSTDIR)/$(notdir $(<:.cpp=.lst)) $(DEFS) 166 | LDFLAGS = $(MCFLAGS) $(TOPT) $(OPT) -nostartfiles $(LIBDIR) -Wl,-Map=$(BUILDDIR)/$(PROJECT).map,--cref,--no-warn-mismatch,--library-path=$(RULESPATH),--script=$(LDSCRIPT) $(LDOPT) 167 | 168 | OUTFILES := $(BUILDDIR)/$(PROJECT).elf \ 169 | $(BUILDDIR)/$(PROJECT).hex \ 170 | $(BUILDDIR)/$(PROJECT).bin \ 171 | $(BUILDDIR)/$(PROJECT).dmp \ 172 | $(BUILDDIR)/$(PROJECT).list 173 | 174 | ############################################################################### 175 | # targets 176 | ############################################################################### 177 | 178 | all: PRE_ALL $(OBJS) $(OUTFILES) POST_ALL 179 | 180 | PRE_ALL: 181 | 182 | POST_ALL: package 183 | 184 | $(OBJS): | $(BUILDDIR) $(OBJDIR) $(LSTDIR) 185 | 186 | $(BUILDDIR): 187 | @echo C++ Compiler Options 188 | @echo $(CXXC) -c $(CXXFLAGS) -I. $(INCDIR) 189 | @echo 190 | @mkdir -p $(BUILDDIR) 191 | 192 | $(OBJDIR): 193 | @mkdir -p $(OBJDIR) 194 | 195 | $(LSTDIR): 196 | @mkdir -p $(LSTDIR) 197 | 198 | $(ASMOBJS) : $(OBJDIR)/%.o : %.s Makefile 199 | @echo Assembling $( $@ 229 | @echo 230 | @$(SZ) $< 231 | @echo 232 | 233 | %.list: %.elf 234 | @echo Creating $@ 235 | @$(OD) -S $< > $@ 236 | 237 | clean: 238 | @echo Cleaning 239 | -rm -fR .dep $(BUILDDIR) $(PKGARCH) 240 | @echo 241 | @echo Done 242 | 243 | package: 244 | @echo Packaging $(VERSION) for $(PLATFORM) to ./$(PKGARCH) 245 | @mkdir -p $(PKGDIR) 246 | @cat $(MANIFEST) | jq '.header.platform="$(PLATFORM)"|.header.version="$(VERSION)"' > $(PKGDIR)/manifest.json 247 | @cp -a $(BUILDDIR)/$(PROJECT).bin $(PKGDIR)/$(PAYLOAD) 248 | @$(ZIP) $(ZIP_ARGS) $(PROJECT).zip $(PKGDIR) 249 | @mv $(PROJECT).zip $(PKGARCH) 250 | @echo 251 | @echo Done 252 | --------------------------------------------------------------------------------