├── bpmdelay_pingpong.ntkdigunit ├── manifest.json ├── project.mk ├── LICENSE.txt ├── README.md ├── Makefile └── bpmdelay_pingpong.cpp /bpmdelay_pingpong.ntkdigunit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hammondeggs/NTS1_BPM_pingpong/HEAD/bpmdelay_pingpong.ntkdigunit -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "header" : 3 | { 4 | "platform" : "nutekt-digital", 5 | "module" : "delfx", 6 | "api" : "1.1-0", 7 | "dev_id" : 0, 8 | "prg_id" : 0, 9 | "version" : "1.0-0", 10 | "name" : "bpm pingpong", 11 | "num_param" : 0 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /project.mk: -------------------------------------------------------------------------------- 1 | # ############################################################################# 2 | # Project Customization 3 | # ############################################################################# 4 | 5 | PROJECT = bpmdelay_pingpong 6 | 7 | UCSRC = 8 | 9 | UCXXSRC = bpmdelay_pingpong.cpp 10 | 11 | UINCDIR = 12 | 13 | UDEFS = 14 | 15 | ULIB = 16 | 17 | ULIBDIR = 18 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 hammondeggsmusic 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NTS1_BPM_pingpong 2 | A BPM-sync'd ping-pong delay for your KORG NTS-1 synthesizer 3 | 4 | **This is provided for informational and entertainment purposes only, any use of the following information is done solely at your own risk! No guarantee is made on the suitibility or accuracy of any information provided.** 5 | 6 | ### A quick word... 7 | I've been having a ton of fun creating these plugins, and if you like stuff like this and my other work, by all means feel free to contribute whatever you can! 8 | 9 | This can be done here : [Donate!](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=MSTCVLXMG7Z5J&source=url) 10 | 11 | This is a (hopefully) well commented example of creating a BPM-style delay for your KORG NTS-1 synthesizer. The prologue and minilogue-xd synthesizers already come with this style of delay - yet the NTS-1 does not. 12 | 13 | This particular flavour of this delay is "pingpong" - this means, the delay will bounce back and forth between the two channels. 14 | 15 | 16 | Place these files in your logue-sdk delfx\tests folder e.g. 17 | 18 | ```...logue-sdk\platform\nutekt-digital\delfx\tests\bpmdelay_pingpong ``` 19 | 20 | Feel free to modify and create your own variations etc - try adding filters etc! 21 | 22 | Have fun; 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # ############################################################################# 2 | # Prologue Mod. FX Makefile 3 | # ############################################################################# 4 | 5 | ifeq ($(OS),Windows_NT) 6 | ifeq ($(MSYSTEM), MSYS) 7 | detected_OS := $(shell uname -s) 8 | else 9 | detected_OS := Windows 10 | endif 11 | else 12 | detected_OS := $(shell uname -s) 13 | endif 14 | 15 | PLATFORMDIR = ../../.. 16 | PROJECTDIR = ../.. 17 | TOOLSDIR = $(PLATFORMDIR)/../../tools 18 | EXTDIR = $(PLATFORMDIR)/../ext 19 | 20 | CMSISDIR = $(EXTDIR)/CMSIS/CMSIS 21 | 22 | # ############################################################################# 23 | # configure archive utility 24 | # ############################################################################# 25 | 26 | ZIP = /usr/bin/zip 27 | ZIP_ARGS = -r -m -q 28 | 29 | ifeq ($(OS),Windows_NT) 30 | ifneq ($(MSYSTEM), MSYS) 31 | ifneq ($(MSYSTEM), MINGW64) 32 | ZIP = $(TOOLSDIR)/zip/bin/zip 33 | endif 34 | endif 35 | endif 36 | 37 | # ############################################################################# 38 | # Include project specific definition 39 | # ############################################################################# 40 | 41 | include ./project.mk 42 | 43 | # ############################################################################# 44 | # configure cross compilation 45 | # ############################################################################# 46 | 47 | MCU = cortex-m4 48 | 49 | GCC_TARGET = arm-none-eabi- 50 | GCC_BIN_PATH = $(TOOLSDIR)/gcc/gcc-arm-none-eabi-5_4-2016q3/bin 51 | 52 | CC = $(GCC_BIN_PATH)/$(GCC_TARGET)gcc 53 | CXXC = $(GCC_BIN_PATH)/$(GCC_TARGET)g++ 54 | LD = $(GCC_BIN_PATH)/$(GCC_TARGET)gcc 55 | #LD = $(GCC_BIN_PATH)/$(GCC_TARGET)g++ 56 | CP = $(GCC_BIN_PATH)/$(GCC_TARGET)objcopy 57 | AS = $(GCC_BIN_PATH)/$(GCC_TARGET)gcc -x assembler-with-cpp 58 | AR = $(GCC_BIN_PATH)/$(GCC_TARGET)ar 59 | OD = $(GCC_BIN_PATH)/$(GCC_TARGET)objdump 60 | SZ = $(GCC_BIN_PATH)/$(GCC_TARGET)size 61 | 62 | HEX = $(CP) -O ihex 63 | BIN = $(CP) -O binary 64 | 65 | LDDIR = $(PROJECTDIR)/ld 66 | RULESPATH = $(LDDIR) 67 | LDSCRIPT = $(LDDIR)/userdelfx.ld 68 | DLIBS = -lm 69 | 70 | DADEFS = -DSTM32F446xE -DCORTEX_USE_FPU=TRUE -DARM_MATH_CM4 71 | DDEFS = -DSTM32F446xE -DCORTEX_USE_FPU=TRUE -DARM_MATH_CM4 -D__FPU_PRESENT 72 | 73 | COPT = -std=c11 -mstructure-size-boundary=8 74 | CXXOPT = -std=c++11 -fno-rtti -fno-exceptions -fno-non-call-exceptions 75 | 76 | LDOPT = -Xlinker --just-symbols=$(LDDIR)/main_api.syms 77 | 78 | CWARN = -W -Wall -Wextra 79 | CXXWARN = 80 | 81 | FPU_OPTS = -mfloat-abi=hard -mfpu=fpv4-sp-d16 -fsingle-precision-constant -fcheck-new 82 | 83 | OPT = -g -Os -mlittle-endian 84 | OPT += $(FPU_OPTS) 85 | #OPT += -flto 86 | 87 | TOPT = -mthumb -mno-thumb-interwork -DTHUMB_NO_INTERWORKING -DTHUMB_PRESENT 88 | 89 | 90 | # ############################################################################# 91 | # set targets and directories 92 | # ############################################################################# 93 | 94 | PKGDIR = $(PROJECT) 95 | PKGARCH = $(PROJECT).ntkdigunit 96 | MANIFEST = manifest.json 97 | PAYLOAD = payload.bin 98 | BUILDDIR = $(PROJECTDIR)/build 99 | OBJDIR = $(BUILDDIR)/obj 100 | LSTDIR = $(BUILDDIR)/lst 101 | 102 | ASMSRC = $(UASMSRC) 103 | 104 | ASMXSRC = $(UASMXSRC) 105 | 106 | CSRC = $(PROJECTDIR)/tpl/_unit.c $(UCSRC) 107 | 108 | CXXSRC = $(UCXXSRC) 109 | 110 | vpath %.s $(sort $(dir $(ASMSRC))) 111 | vpath %.S $(sort $(dir $(ASMXSRC))) 112 | vpath %.c $(sort $(dir $(CSRC))) 113 | vpath %.cpp $(sort $(dir $(CXXSRC))) 114 | 115 | ASMOBJS := $(addprefix $(OBJDIR)/, $(notdir $(ASMSRC:.s=.o))) 116 | ASMXOBJS := $(addprefix $(OBJDIR)/, $(notdir $(ASMXSRC:.S=.o))) 117 | COBJS := $(addprefix $(OBJDIR)/, $(notdir $(CSRC:.c=.o))) 118 | CXXOBJS := $(addprefix $(OBJDIR)/, $(notdir $(CXXSRC:.cpp=.o))) 119 | 120 | OBJS := $(ASMXOBJS) $(ASMOBJS) $(COBJS) $(CXXOBJS) 121 | 122 | DINCDIR = $(PROJECTDIR)/inc \ 123 | $(PROJECTDIR)/inc/api \ 124 | $(PLATFORMDIR)/inc \ 125 | $(PLATFORMDIR)/inc/dsp \ 126 | $(PLATFORMDIR)/inc/utils \ 127 | $(CMSISDIR)/Include 128 | 129 | INCDIR := $(patsubst %,-I%,$(DINCDIR) $(UINCDIR)) 130 | 131 | DEFS := $(DDEFS) $(UDEFS) 132 | ADEFS := $(DADEFS) $(UADEFS) 133 | 134 | LIBS := $(DLIBS) $(ULIBS) 135 | 136 | LIBDIR := $(patsubst %,-I%,$(DLIBDIR) $(ULIBDIR)) 137 | 138 | 139 | # ############################################################################# 140 | # compiler flags 141 | # ############################################################################# 142 | 143 | MCFLAGS := -mcpu=$(MCU) 144 | ODFLAGS = -x --syms 145 | ASFLAGS = $(MCFLAGS) -g $(TOPT) -Wa,-alms=$(LSTDIR)/$(notdir $(<:.s=.lst)) $(ADEFS) 146 | ASXFLAGS = $(MCFLAGS) -g $(TOPT) -Wa,-alms=$(LSTDIR)/$(notdir $(<:.S=.lst)) $(ADEFS) 147 | CFLAGS = $(MCFLAGS) $(TOPT) $(OPT) $(COPT) $(CWARN) -Wa,-alms=$(LSTDIR)/$(notdir $(<:.c=.lst)) $(DEFS) 148 | CXXFLAGS = $(MCFLAGS) $(TOPT) $(OPT) $(CXXOPT) $(CXXWARN) -Wa,-alms=$(LSTDIR)/$(notdir $(<:.cpp=.lst)) $(DEFS) 149 | LDFLAGS = $(MCFLAGS) $(TOPT) $(OPT) -nostartfiles $(LIBDIR) -Wl,-Map=$(BUILDDIR)/$(PROJECT).map,--cref,--no-warn-mismatch,--library-path=$(RULESPATH),--script=$(LDSCRIPT) $(LDOPT) 150 | 151 | OUTFILES := $(BUILDDIR)/$(PROJECT).elf \ 152 | $(BUILDDIR)/$(PROJECT).hex \ 153 | $(BUILDDIR)/$(PROJECT).bin \ 154 | $(BUILDDIR)/$(PROJECT).dmp \ 155 | $(BUILDDIR)/$(PROJECT).list 156 | 157 | ############################################################################### 158 | # targets 159 | ############################################################################### 160 | 161 | all: PRE_ALL $(OBJS) $(OUTFILES) POST_ALL 162 | 163 | PRE_ALL: 164 | 165 | POST_ALL: package 166 | 167 | $(OBJS): | $(BUILDDIR) $(OBJDIR) $(LSTDIR) 168 | 169 | $(BUILDDIR): 170 | @echo Compiler Options 171 | @echo $(CC) -c $(CFLAGS) -I. $(INCDIR) 172 | @echo 173 | @mkdir -p $(BUILDDIR) 174 | 175 | $(OBJDIR): 176 | @mkdir -p $(OBJDIR) 177 | 178 | $(LSTDIR): 179 | @mkdir -p $(LSTDIR) 180 | 181 | $(ASMOBJS) : $(OBJDIR)/%.o : %.s Makefile 182 | @echo Assembling $( $@ 212 | @echo 213 | @$(SZ) $< 214 | @echo 215 | 216 | %.list: %.elf 217 | @echo Creating $@ 218 | @$(OD) -S $< > $@ 219 | 220 | clean: 221 | @echo Cleaning 222 | -rm -fR .dep $(BUILDDIR) $(PKGARCH) 223 | @echo 224 | @echo Done 225 | 226 | package: 227 | @echo Packaging to ./$(PKGARCH) 228 | @mkdir -p $(PKGDIR) 229 | @cp -a $(MANIFEST) $(PKGDIR)/ 230 | @cp -a $(BUILDDIR)/$(PROJECT).bin $(PKGDIR)/$(PAYLOAD) 231 | @$(ZIP) $(ZIP_ARGS) $(PROJECT).zip $(PKGDIR) 232 | @mv $(PROJECT).zip $(PKGARCH) 233 | @echo 234 | @echo Done 235 | -------------------------------------------------------------------------------- /bpmdelay_pingpong.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * File: bpmdelay_pingpong.cpp 3 | * 4 | * A simple BPM sync'd PingPong style delay for the NTS-1 5 | * 6 | * Note, this delay uses the dry/wet (shift-B) to set the wet / dry ratio 7 | * 8 | * 9 | * hammondeggsmusic.ca 2021 10 | * 11 | */ 12 | 13 | #include "userdelfx.h" 14 | 15 | 16 | // Defines 17 | #define NUM_DELAY_DIVISIONS 15 // # of bpm divisions in table 18 | #define DELAY_LINE_SIZE 0x40000 // Delay line size (*must be a power of 2) 19 | #define DELAY_LINE_SIZE_MASK 0x3FFFF // Mask for the delay line size for rollover 20 | #define DELAY_GLIDE_RATE 12000 // this value must not be lower than 1. larger values = slower glide rates for delay time 21 | #define MIN_BPM 56 // failsafe, likely never used 22 | #define NUM_NOTES_PER_BEAT 4 // The xd/prologue use quarter notes, hence '4'. 23 | #define SAMPLE_RATE 48000 // 48KHz is our fixed sample rate (the const k_samplerate is only listed in the osc_api.h not the fx_api.h) 24 | 25 | 26 | #define PSEUDO_STEREO_OFFSET (float)SAMPLE_RATE * .01f // How much time to offset the right channel in seconds for pseudo stereo(.01 = 10ms) 27 | 28 | // Delay BPM division with time knob from 0 to full: 29 | // 1/64, 1/48, 1/32, 1/24, 1/16, 1/12, 1/8, 1/6, 3/16, 1/4, 1/3, 3/8, 1/2, 3/4, 1 30 | float delayDivisions[NUM_DELAY_DIVISIONS] = 31 | {0.015625,.02083333,.03125,.04166666,.0625f,.08333333f,.125f,.16666667f,.1875f,.25f,.33333333f,.375f,.5f,.75f,1}; 32 | 33 | // Delay lines for left / right channel 34 | __sdram float delayLine_L[DELAY_LINE_SIZE]; 35 | __sdram float delayLine_R[DELAY_LINE_SIZE]; 36 | 37 | // Current position in the delay line we are writing to: 38 | // (integer value as it is per-sample) 39 | uint32_t delayLine_Wr = 0; 40 | 41 | // Smoothing (glide) for delay time: 42 | // This is the current delay time as we smooth it 43 | float currentDelayTime = 48000; 44 | 45 | // This is the delay time we actually wish to set to 46 | float targetDelayTime = 48000; 47 | 48 | // Depth knob value from 0-1 49 | float valDepth = 0; 50 | 51 | // Time value knob from 0-1 52 | float valTime = 0; 53 | 54 | // Delay time multiplier (will be pulled from delayDivisions table) 55 | float multiplier = 1; 56 | 57 | // Wet/Dry signal levels 58 | float wet = .5; 59 | float dry = .5; 60 | 61 | 62 | //////////////////////////////////////////////////////////////////////// 63 | // DELFX_INIT 64 | // - initialize the effect variables, including clearing the delay lines 65 | //////////////////////////////////////////////////////////////////////// 66 | void DELFX_INIT(uint32_t platform, uint32_t api) 67 | { 68 | // Initialize the variables used 69 | delayLine_Wr = 0; 70 | 71 | // Clear the delay lines. If you don't do this, it is entirely possible that "something" will already be there, and you might 72 | // get either old delay sounds, or very unpleasant noises from a previous effects. 73 | for (int i=0;i= NUM_DELAY_DIVISIONS) || (divIndex < 0)) 310 | { 311 | divIndex = NUM_DELAY_DIVISIONS - 1;//failsafe 312 | } 313 | 314 | // Get the time multiplier from the division table. 315 | multiplier = delayDivisions[divIndex]; 316 | break; 317 | 318 | case k_user_delfx_param_depth: 319 | //////////////////////////// 320 | // "B" / DEPTH KNOB 321 | //////////////////////////// 322 | // Set the delay feedback (0-1, tbd if i use an exp table) 323 | // Just store this value for the DSP loop to use. 324 | valDepth = valf; 325 | break; 326 | 327 | case k_user_delfx_param_shift_depth: 328 | //////////////////////////////// 329 | // "DELAY+B" / SHIFT-DEPTH KNOB 330 | //////////////////////////////// 331 | // For delays this is wet/dry, though you can technically use this 3rd parameter for whatever you want! 332 | //Left side of the knob (all dry to full mix) 333 | // Adapted from the korg example, this allows us to get a 50/50 split at full mix but a higher level for 334 | // full wet / full dry. 335 | 336 | // I've expanded the korg example here to make it a bit easier to follow 337 | //s_mix = (valf <= 0.49f) ? 1.02040816326530612244f * valf : (valf >= 0.51f) ? 0.5f + 1.02f * (valf-0.51f) : 0.5f; 338 | 339 | // Are we at the left half of the knob position? 340 | if (valf <= 0.49f) 341 | { 342 | // Yes, amplify the mix value slightly 343 | s_mix = 1.02040816326530612244f * valf; 344 | } 345 | // No, are we at the right half of the knob position? 346 | else if (valf >= 0.51f) 347 | { 348 | // Yes, amplify the mix value but also invert it and subtract the 0.51 offset from it (so the mix value will decrease when turning the knob higher) 349 | s_mix = 0.5f + 1.02f * (valf-0.51f); 350 | } 351 | else 352 | { 353 | // Midpoint, set the mix value to 50% 354 | s_mix = 0.5f; 355 | } 356 | // Calculate our wet / dry values 357 | dry = 1.0f - s_mix; 358 | wet = s_mix; 359 | break; 360 | 361 | default: 362 | // no default handling, there is no case for this. 363 | break; 364 | } 365 | } 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | --------------------------------------------------------------------------------