diff --git a/l2ork_addons/autotune/Makefile b/l2ork_addons/autotune/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..18df98e8af96ba1e55c549a040f4c92d9f84601d --- /dev/null +++ b/l2ork_addons/autotune/Makefile @@ -0,0 +1,284 @@ +## Pd library template version 1.0 +# For instructions on how to use this template, see: +# http://puredata.info/docs/developer/MakefileTemplate +LIBRARY_NAME = autotune~ + +# add your .c source files to the SOURCES variable, help files will be +# included automatically +SOURCES = autotune~.c + +# For objects that only build on certain platforms, add those to the SOURCES +# line for the right platforms. +SOURCES_android = +SOURCES_cygwin = +SOURCES_macosx = +SOURCES_iphoneos = +SOURCES_linux = +SOURCES_windows = + +# list all pd objects (i.e. myobject.pd) files here, and their helpfiles will +# be included automatically +# PDOBJECTS = mypdobject.pd + +# example patches and related files, in the 'examples' subfolder +# EXAMPLES = bothtogether.pd + +# manuals and related files, in the 'manual' subfolder +# MANUAL = manual.txt + +# if you want to include any other files in the source and binary tarballs, +# list them here. This can be anything from header files, example patches, +# documentation, etc. README.txt and LICENSE.txt are required and therefore +# automatically included +# EXTRA_DIST = + + + +#------------------------------------------------------------------------------# +# +# you shouldn't need to edit anything below here, if we did it right :) +# +#------------------------------------------------------------------------------# + +# get library version from meta file +#LIBRARY_VERSION = $(shell sed -n 's|^\#X text [0-9][0-9]* [0-9][0-9]* VERSION \(.*\);|\1|p' $(LIBRARY_NAME)-meta.pd) +LIBRARY_VERSION = 0.9 + +# where Pd lives +PD_PATH = ../../pd +# where to install the library +prefix = /usr +libdir = $(prefix)/lib +pkglibdir = $(libdir)/extra +objectsdir = $(pkglibdir) + + +INSTALL = install +INSTALL_FILE = $(INSTALL) -p -m 644 +INSTALL_DIR = $(INSTALL) -p -m 755 -d + +CFLAGS = -DPD -I ../../pd/src -Wall -W -g +LDFLAGS = +LIBS = +ALLSOURCES := $(SOURCES) $(SOURCES_android) $(SOURCES_cygwin) $(SOURCES_macosx) \ + $(SOURCES_iphoneos) $(SOURCES_linux) $(SOURCES_windows) + +UNAME := $(shell uname -s) +ifeq ($(UNAME),Darwin) + CPU := $(shell uname -p) + ifeq ($(CPU),arm) # iPhone/iPod Touch + SOURCES += $(SOURCES_iphoneos) + EXTENSION = pd_darwin + OS = iphoneos + IPHONE_BASE=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin + CC=$(IPHONE_BASE)/gcc + CPP=$(IPHONE_BASE)/cpp + CXX=$(IPHONE_BASE)/g++ + ISYSROOT = -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.0.sdk + IPHONE_CFLAGS = -miphoneos-version-min=3.0 $(ISYSROOT) -arch armv6 + OPT_CFLAGS = -fast -funroll-loops -fomit-frame-pointer + CFLAGS := $(IPHONE_CFLAGS) $(OPT_CFLAGS) $(CFLAGS) \ + -I/Applications/Pd-extended.app/Contents/Resources/include + LDFLAGS += -arch armv6 -bundle -undefined dynamic_lookup $(ISYSROOT) + LIBS += -lc + STRIP = strip -x + DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION) + DISTBINDIR=$(DISTDIR)-$(OS) + else # Mac OS X + SOURCES += $(SOURCES_macosx) + EXTENSION = pd_darwin + OS = macosx + OPT_CFLAGS = -ftree-vectorize -ftree-vectorizer-verbose=2 -fast +# build universal 32-bit on 10.4 and 32/64 on newer + ifeq ($(shell uname -r | sed 's|\([0-9][0-9]*\)\.[0-9][0-9]*\.[0-9][0-9]*|\1|'), 8) + FAT_FLAGS = -arch i386 -mmacosx-version-min=10.4 + else + FAT_FLAGS = -arch i386 -arch x86_64 -mmacosx-version-min=10.4 + SOURCES += $(SOURCES_iphoneos) + endif + CFLAGS += $(FAT_FLAGS) -fPIC -I/sw/include \ + -I/Applications/Pd-extended.app/Contents/Resources/include + LDFLAGS += $(FAT_FLAGS) -bundle -undefined dynamic_lookup -L/opt/local/lib + # if the 'pd' binary exists, check the linking against it to aid with stripping + LDFLAGS += $(shell test -e $(PD_PATH)/bin/pd && echo -bundle_loader $(PD_PATH)/bin/pd) + LIBS += -lc + STRIP = strip -x + DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION) + DISTBINDIR=$(DISTDIR)-$(OS) +# install into ~/Library/Pd on Mac OS X since /usr/local isn't used much + pkglibdir=$(HOME)/Library/Pd + endif +endif +ifeq ($(UNAME),Linux) + SOURCES += $(SOURCES_linux) + EXTENSION = pd_linux + OS = linux + OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer + CFLAGS += -fPIC + LDFLAGS += -Wl,--export-dynamic -shared -fPIC + LIBS += -lc + STRIP = strip --strip-unneeded -R .note -R .comment + DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION) + DISTBINDIR=$(DISTDIR)-$(OS)-$(shell uname -m) +endif +ifeq (CYGWIN,$(findstring CYGWIN,$(UNAME))) + SOURCES += $(SOURCES_cygwin) + EXTENSION = dll + OS = cygwin + OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer + CFLAGS += + LDFLAGS += -Wl,--export-dynamic -shared -L$(PD_PATH)/src + LIBS += -lc -lpd + STRIP = strip --strip-unneeded -R .note -R .comment + DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION) + DISTBINDIR=$(DISTDIR)-$(OS) +endif +ifeq (MINGW,$(findstring MINGW,$(UNAME))) + SOURCES += $(SOURCES_windows) + EXTENSION = dll + OS = windows + OPT_CFLAGS = -O3 -funroll-loops -fomit-frame-pointer -march=i686 -mtune=pentium4 + CFLAGS += -mms-bitfields + LDFLAGS += -s -shared -Wl,--enable-auto-import + LIBS += -L$(PD_PATH)/src -L$(PD_PATH)/bin -L$(PD_PATH)/obj -lpd -lwsock32 -lkernel32 -luser32 -lgdi32 + STRIP = strip --strip-unneeded -R .note -R .comment + DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION) + DISTBINDIR=$(DISTDIR)-$(OS) +endif + +CFLAGS += $(OPT_CFLAGS) + + +.PHONY = install libdir_install single_install install-doc install-exec install-examples install-manual clean dist etags + +all: $(SOURCES:.c=.$(EXTENSION)) + +%.o: %.c + $(CC) $(CFLAGS) -o "$*.o" -c "$*.c" + +%.$(EXTENSION): %.o + $(CC) $(LDFLAGS) -o "$*.$(EXTENSION)" "$*.o" $(LIBS) + chmod a-x "$*.$(EXTENSION)" + +# this links everything into a single binary file +$(LIBRARY_NAME): $(SOURCES:.c=.o) $(LIBRARY_NAME).o + $(CC) $(LDFLAGS) -o $(LIBRARY_NAME).$(EXTENSION) $(SOURCES:.c=.o) $(LIBRARY_NAME).o $(LIBS) + chmod a-x $(LIBRARY_NAME).$(EXTENSION) + + +install: libdir_install + +# The meta and help files are explicitly installed to make sure they are +# actually there. Those files are not optional, then need to be there. +libdir_install: $(SOURCES:.c=.$(EXTENSION)) install-doc install-examples install-manual + $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) + $(INSTALL_FILE) $(LIBRARY_NAME)-meta.pd \ + $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) + test -z "$(strip $(SOURCES))" || (\ + $(INSTALL_FILE) $(SOURCES:.c=.$(EXTENSION)) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) && \ + $(STRIP) $(addprefix $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/,$(SOURCES:.c=.$(EXTENSION)))) + test -z "$(strip $(PDOBJECTS))" || \ + $(INSTALL_FILE) $(PDOBJECTS) \ + $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) + +# install library linked as single binary +single_install: $(LIBRARY_NAME) install-doc install-exec + $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) + $(INSTALL_FILE) $(LIBRARY_NAME).$(EXTENSION) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) + $(STRIP) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/$(LIBRARY_NAME).$(EXTENSION) + +install-doc: + $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) + test -z "$(strip $(SOURCES))" || \ + $(INSTALL_FILE) $(SOURCES:.c=-help.pd) \ + $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) + test -z "$(strip $(PDOBJECTS))" || \ + $(INSTALL_FILE) $(PDOBJECTS:.pd=-help.pd) \ + $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) + $(INSTALL_FILE) README.txt $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/README.txt + $(INSTALL_FILE) LICENSE.txt $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/LICENSE.txt + +install-examples: + test -z "$(strip $(EXAMPLES))" || \ + $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/examples && \ + for file in $(EXAMPLES); do \ + $(INSTALL_FILE) examples/$$file $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/examples; \ + done + +install-manual: + test -z "$(strip $(MANUAL))" || \ + $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/manual && \ + for file in $(MANUAL); do \ + $(INSTALL_FILE) manual/$$file $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/manual; \ + done + + +clean: + -rm -f -- $(SOURCES:.c=.o) + -rm -f -- $(SOURCES:.c=.$(EXTENSION)) + -rm -f -- $(LIBRARY_NAME).o + -rm -f -- $(LIBRARY_NAME).$(EXTENSION) + +distclean: clean + -rm -f -- $(DISTBINDIR).tar.gz + -rm -rf -- $(DISTBINDIR) + -rm -f -- $(DISTDIR).tar.gz + -rm -rf -- $(DISTDIR) + + +$(DISTBINDIR): + $(INSTALL_DIR) $(DISTBINDIR) + +libdir: all $(DISTBINDIR) + $(INSTALL_FILE) $(LIBRARY_NAME)-meta.pd $(DISTBINDIR) + $(INSTALL_FILE) $(SOURCES) $(DISTBINDIR) + $(INSTALL_FILE) $(SOURCES:.c=-help.pd) $(DISTBINDIR) + test -z "$(strip $(EXTRA_DIST))" || \ + $(INSTALL_FILE) $(EXTRA_DIST) $(DISTBINDIR) +# tar --exclude-vcs -czpf $(DISTBINDIR).tar.gz $(DISTBINDIR) + +$(DISTDIR): + $(INSTALL_DIR) $(DISTDIR) + +dist: $(DISTDIR) + $(INSTALL_FILE) Makefile $(DISTDIR) + $(INSTALL_FILE) README.txt $(DISTDIR) + $(INSTALL_FILE) LICENSE.txt $(DISTDIR) + $(INSTALL_FILE) $(LIBRARY_NAME)-meta.pd $(DISTDIR) + test -z "$(strip $(ALLSOURCES))" || \ + $(INSTALL_FILE) $(ALLSOURCES) $(DISTDIR) + test -z "$(strip $(ALLSOURCES))" || \ + $(INSTALL_FILE) $(ALLSOURCES:.c=-help.pd) $(DISTDIR) + test -z "$(strip $(PDOBJECTS))" || \ + $(INSTALL_FILE) $(PDOBJECTS) $(DISTDIR) + test -z "$(strip $(PDOBJECTS))" || \ + $(INSTALL_FILE) $(PDOBJECTS:.pd=-help.pd) $(DISTDIR) + test -z "$(strip $(EXTRA_DIST))" || \ + $(INSTALL_FILE) $(EXTRA_DIST) $(DISTDIR) + test -z "$(strip $(EXAMPLES))" || \ + $(INSTALL_DIR) $(DISTDIR)/examples && \ + for file in $(EXAMPLES); do \ + $(INSTALL_FILE) examples/$$file $(DISTDIR)/examples; \ + done + test -z "$(strip $(MANUAL))" || \ + $(INSTALL_DIR) $(DISTDIR)/manual && \ + for file in $(MANUAL); do \ + $(INSTALL_FILE) manual/$$file $(DISTDIR)/manual; \ + done + tar --exclude-vcs -czpf $(DISTDIR).tar.gz $(DISTDIR) + + +etags: + etags *.h $(SOURCES) ../../pd/src/*.[ch] /usr/include/*.h /usr/include/*/*.h + +showsetup: + @echo "PD_PATH: $(PD_PATH)" + @echo "objectsdir: $(objectsdir)" + @echo "LIBRARY_NAME: $(LIBRARY_NAME)" + @echo "LIBRARY_VERSION: $(LIBRARY_VERSION)" + @echo "SOURCES: $(SOURCES)" + @echo "PDOBJECTS: $(PDOBJECTS)" + @echo "ALLSOURCES: $(ALLSOURCES)" + @echo "UNAME: $(UNAME)" + @echo "CPU: $(CPU)" + @echo "pkglibdir: $(pkglibdir)" diff --git a/l2ork_addons/autotune/autotune_scale_warp.png b/l2ork_addons/autotune/autotune_scale_warp.png new file mode 100644 index 0000000000000000000000000000000000000000..0daebc5d922ad94d8be06eecc41f4916b5689ed1 Binary files /dev/null and b/l2ork_addons/autotune/autotune_scale_warp.png differ diff --git a/l2ork_addons/autotune/autotune~-help.pd b/l2ork_addons/autotune/autotune~-help.pd new file mode 100644 index 0000000000000000000000000000000000000000..490d83c452adacf9a77d436cf4b1c02bb7a284b8 --- /dev/null +++ b/l2ork_addons/autotune/autotune~-help.pd @@ -0,0 +1,289 @@ +#N canvas 294 14 558 668 10; +#X obj 0 1102 cnv 15 552 21 empty \$0-pddp.cnv.footer empty 20 12 0 +14 -228856 -66577 0; +#X obj 0 0 cnv 15 552 40 empty \$0-pddp.cnv.header autotune~ 3 12 0 +18 -204280 -1 0; +#X obj 0 518 cnv 3 550 3 empty \$0-pddp.cnv.inlets inlets 8 12 0 13 +-228856 -1 0; +#N canvas 819 379 370 278 META 0; +#X text 7 104 OUTLET_0 signal; +#X text 7 11 KEYWORDS signal pitch-tracking autotune; +#X text 7 31 LICENSE GNU GPL v3; +#X text 7 51 DESCRIPTION pitch correction (a.k.a. autotuning); +#X text 7 171 LIBRARY L2Ork addons; +#X text 7 191 AUTHOR Ivica Ico Bukvic; +#X text 7 211 WEBSITE http://l2ork.music.vt.edu; +#X text 7 231 RELEASE_DATE 2015; +#X text 7 251 HELP_PATCH_AUTHORS Ivica Ico Bukvic; +#X text 7 71 INLET_0 signal list correct mix shift tune smooth lfodepth +lforate lfoshape lfosym warp scwarp fwarp pull pullpitch; +#X text 7 127 OUTLET_1 float confidence (0-1); +#X text 7 150 OUTLET_2 float perceived frequency (Hz); +#X restore 500 1104 pd META; +#X obj 0 929 cnv 3 550 3 empty \$0-pddp.cnv.outlets outlets 8 12 0 +13 -228856 -1 0; +#X obj 0 1010 cnv 3 550 3 empty \$0-pddp.cnv.argument arguments 8 12 +0 13 -228856 -1 0; +#X obj 0 1054 cnv 3 550 3 empty \$0-pddp.cnv.more_info more_info 8 +12 0 13 -228856 -1 0; +#N canvas 436 542 428 109 Related_objects 0; +#X obj 1 1 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0 +14 -204280 -1 0; +#X text 8 2 [osc~] Related Objects; +#X obj 23 42 fiddle~; +#X obj 83 42 sigmund~; +#X restore 102 1104 pd Related_objects; +#X obj 78 527 cnv 3 3 393 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856 +-162280 0; +#X obj 78 938 cnv 17 3 17 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856 +-162280 0; +#X text 18 488 audio output; +#X text 98 526 signal; +#X text 98 937 signal; +#X text 98 546 list; +#X obj 4 1104 pddp/pddplink all_about_help_patches.pd -text Usage Guide +; +#X text 11 23 real-time pitch correction; +#X obj 482 12 autotune~; +#X obj 12 53 loadbang; +#X obj 12 188 mtof; +#X obj 12 76 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1 1 +; +#X obj 12 165 + 69; +#X obj 12 119 counter 2 0 120; +#X obj 12 142 / 10; +#X obj 12 96 metro 25; +#X obj 12 211 osc~ 440; +#X obj 12 333 autotune~, f 19; +#X floatatom 67 376 5 0 0 0 - - -, f 5; +#X floatatom 123 356 10 0 0 0 - - -, f 10; +#X text 189 355 detected frequency; +#X obj 31 295 r params; +#X text 152 77 The [autotune~] object pitch corrects inputted signal +in real-time based on detected formant. Using extreme settings can +produce Cher- and T-pain-like pop culture results.[autotune~] is +based on LADSPA Autotalent plugin. It also borrows code structure from +Maxus Germanus' autoutuned~; +#X text 45 143 microtonalpitch scale; +#N canvas 100 26 804 418 1.basics 0; +#X obj 28 385 s params; +#X msg 655 292 mix \$1; +#X floatatom 483 152 5 0 1 0 - - -, f 5; +#X floatatom 655 152 5 0 1 0 - - -, f 5; +#X msg 483 292 correct \$1; +#X msg 28 260 0 -1 0 -1 -1 0 -1 0 -1 0 -1 -1; +#X text 219 260 pentatonic scale; +#X msg 28 207 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1; +#X msg 28 182 1 -1 1 -1 1 1 -1 1 -1 1 -1 1; +#X obj 28 158 loadbang; +#X obj 483 106 loadbang; +#X msg 483 129 1; +#X text 25 57 -1 = removes pitch from the scale 0 = includes pitch +and allows off-tune notes except when surrounded by removed (-1) notes +1 = snaps any nearby pitch to note, f 91; +#X text 26 110 Once the scale is initialized \, increase amount of +pitch correction (e.g. 1) and increase wet signal (e.g. 1).; +#X text 25 19 First \, set up a scale you wish to use. The twelve notes +always start with the note A and go up to G#. Default none.; +#X text 493 174 The amount of pitchcorrection (0-1)Default 0; +#X text 665 174 Dry/wet mix(0 = dry \, 1 = wet)also available inthe +main patcherDefault 0; +#X text 208 182 Major scale with snapping notes; +#X text 214 207 Whole tone scale (no snapping necessarysince all valid +notes are surrounded withdisabled notes); +#X text 178 285 chromatic scale without snappingessentially with no +effect; +#X msg 28 285 0 0 0 0 0 0 0 0 0 0 0 0; +#X text 178 321 chromatic scale with snapping; +#X msg 28 321 1 1 1 1 1 1 1 1 1 1 1 1; +#X connect 1 0 0 0; +#X connect 2 0 4 0; +#X connect 3 0 1 0; +#X connect 4 0 0 0; +#X connect 5 0 0 0; +#X connect 7 0 0 0; +#X connect 8 0 0 0; +#X connect 9 0 8 0; +#X connect 10 0 11 0; +#X connect 11 0 2 0; +#X connect 20 0 0 0; +#X connect 22 0 0 0; +#X restore 148 295 pd 1.basics; +#X msg 90 295 mix \$1; +#X floatatom 90 191 5 0 1 0 - - -, f 5; +#X obj 95 401 loadbang; +#X msg 95 424 0.1; +#N canvas 47 82 1060 330 2.tuning 0; +#X floatatom 27 45 5 -12 12 0 - - -, f 5; +#X msg 27 234 shift \$1; +#X floatatom 137 45 5 400 480 0 - - -, f 5; +#X msg 137 234 tune \$1; +#X floatatom 261 45 5 0 1 0 - - -, f 5; +#X msg 261 234 smooth \$1; +#X floatatom 738 45 5 0 1 0 - - -, f 5; +#X msg 738 234 pull \$1; +#X floatatom 884 45 5 -36 12 0 - - -, f 5; +#X msg 884 234 pullpitch \$1; +#X obj 426 294 s params; +#X text 220 14 GENERAL TUNING OPTIONS; +#X text 760 15 PULL PITCH OPTIONS; +#X text 893 67 Specify pull pitch insemitones away fromnote A (-36 +to 12)Default 0; +#X text 747 67 How strong ofa pull is theretowards a specificnote +(0-1)0 = none1 = 100% pulled pitchDefault 0; +#X floatatom 406 45 5 -1 1 0 - - -, f 5; +#X msg 406 234 warp \$1; +#X obj 522 45 tgl 15 0 empty empty empty 17 7 0 10 -258113 -1 -1 0 +1; +#X msg 522 234 fcorr \$1; +#X text 35 66 Pitch-shiftoutput 12semi-tonesbelow or above(-12 +to 12)Default 0; +#X text 146 66 Note A tuning(400-480Hz)Default 440Hz; +#X text 269 66 Smoothen transitionsbetween pitches (0-1)0 = jumpy1 += glissando-likeDefault 0; +#X text 415 67 Warp formantsto alter voiceage/gender (0-1)Default +0; +#X text 530 67 Toggle formantcorrection tominimize chipmunkeffectIMPORTANT! +Requiresvery soft input signal0 = off1 = onDefault 0; +#X connect 0 0 1 0; +#X connect 1 0 10 0; +#X connect 2 0 3 0; +#X connect 3 0 10 0; +#X connect 4 0 5 0; +#X connect 5 0 10 0; +#X connect 6 0 7 0; +#X connect 7 0 10 0; +#X connect 8 0 9 0; +#X connect 9 0 10 0; +#X connect 15 0 16 0; +#X connect 16 0 10 0; +#X connect 17 0 18 0; +#X connect 18 0 10 0; +#X restore 223 295 pd 2.tuning; +#N canvas 511 188 508 330 3.vibrato 0; +#X obj 209 291 s params; +#X floatatom 25 85 5 0 1 0 - - -, f 5; +#X msg 25 224 lfodepth \$1; +#X floatatom 118 85 7 0 20000 0 - - -, f 7; +#X msg 118 224 lforate \$1; +#X floatatom 246 85 5 -1 1 0 - - -, f 5; +#X msg 246 224 lfoshape \$1; +#X floatatom 399 85 5 -1 1 0 - - -, f 5; +#X msg 399 224 lfosym \$1; +#X text 210 14 VIBRATO OPTIONS; +#X text 76 36 Apply a vibrato to the outputting voice using a built-in +LFO (low frequency oscillator); +#X text 33 106 LFO depth oramplitude(0-1)Default 0; +#X text 126 106 LFO frequency(0 to 1/2 Samplingrate)Default 0; +#X text 255 106 LFO waveform (-1 to 1)-1 = square 0 = sine 1 = +triangleDefault 0; +#X text 408 107 LFO waveformsymmetry(-1 to 1)Default 0; +#X connect 1 0 2 0; +#X connect 2 0 0 0; +#X connect 3 0 4 0; +#X connect 4 0 0 0; +#X connect 5 0 6 0; +#X connect 6 0 0 0; +#X connect 7 0 8 0; +#X connect 8 0 0 0; +#X restore 298 295 pd 3.vibrato; +#X text 126 179 dry/wet mix (0 = dry \, 1 = wet) \, requires scale +definition and pitch correction to be enabled \, see 1.basics where +we loadbang a major scale; +#X obj 12 447 output~; +#X text 165 274 open subpatchers below to learn about additional options +; +#X text 167 937 - the outgoing processed signal.; +#X text 80 1028 N/A; +#N canvas 373 124 569 357 4.scale.modifications 0; +#X floatatom 57 51 5 -5 5 0 - - -, f 5; +#X msg 57 73 scwarp \$1; +#X obj 57 303 s params; +#X obj 398 170 ggee/image @pd_extra/images/autotune_scale_warp.png +0; +#X obj 261 329 pddplink http://tombaran.info/autotalent-0.2_refcard.pdf +; +#X text 230 311 Image excerpt from Tom Baran's Autotalent documentation. +; +#X text 66 97 Modulate existing scaleusing a single parameter.(-5 +to 5).See the chart on the rightto figure out what value touse.Default +0; +#X connect 0 0 1 0; +#X connect 1 0 2 0; +#X restore 379 295 pd 4.scale.modifications; +#X text 167 1064 See subpatchers 1.basics \, 2.tuning \, 3.vibrato +\, and 4.scale.presets for additional info.; +#X text 168 526 - an incoming signal to be pitch-corrected.; +#X text 98 592 correct; +#X text 98 612 mix; +#X text 98 632 shift; +#X text 98 665 tune; +#X text 167 665 - tune note A (400-480 Hz \, default 440).; +#X text 167 632 - shift pitch-corrected output by a number of semitones +(-36 to 12 \, default 0).; +#X text 167 612 - dry/wet mix (0 = dry \, 1 = wet \, default 0).; +#X text 167 592 - amount of correction (0-1 \, default 0).; +#X text 167 546 - a list of 12 values corresponding to each pitch in +a chromatic scale (-1 = disabled \, 0 = normal \, 1 = snapping \, default +none); +#X obj 78 962 cnv 17 3 17 empty \$0-pddp.cnv.let.0 1 5 9 0 16 -228856 +-162280 0; +#X obj 78 986 cnv 17 3 17 empty \$0-pddp.cnv.let.0 2 5 9 0 16 -228856 +-162280 0; +#X text 98 961 float; +#X text 98 985 float; +#X text 167 961 - confidence value (> 0.7 should be considered voiced). +; +#X text 167 985 - perceived pitch (Hz).; +#X text 103 376 confidence (> 0.7 should be considered voiced); +#X text 98 685 smooth; +#X text 167 685 - smoothen transitions between pitches (0-1 \, default +0).; +#X text 98 705 lfodepth; +#X text 98 725 lforate; +#X text 98 745 lfoshape; +#X text 98 765 lfosym; +#X text 98 785 warp; +#X text 98 805 scwarp; +#X text 98 871 pull; +#X text 98 891 pullpitch; +#X text 167 705 - vibrato LFO depth/amplitude (0-1 \, default 0).; +#X text 167 725 - LFO frequency (0 to 1/2 sampling rate \, default +0).; +#X text 167 745 - LFO waveform (-1=square \, 0=sine \, 1=triangle \, +default 0).; +#X text 167 765 - LFO waveform symmetry (-1 to 1 \, default 0).; +#X text 167 785 - formant warp to alter age/gender (0-1 \, default +0).; +#X text 167 805 - scale warp (-5 to 5 \, default 0). See 4.scale.modifications +for more info.; +#X text 167 871 - pull to pitch \, (0=no pull \, 1=100% pull \, default +0).; +#X text 167 891 - specify pull pitch from -36 semitones below A to +12 semitones above (-36 to 12 \, default 0).; +#X obj 12 260 *~ 1; +#X floatatom 33 239 5 0.05 1 0 - - -, f 5; +#X text 98 838 fcorr; +#X text 167 838 - formant correction REQUIRES softer input signal \, +e.g. use 0.05 multiplier (0=off \, 1=on \, default 0).; +#X text 68 239 multiplier--use 0.05 when fcorr is enabled (see 2.tuning) +; +#X connect 17 0 19 0; +#X connect 18 0 24 0; +#X connect 19 0 23 0; +#X connect 20 0 18 0; +#X connect 21 0 22 0; +#X connect 22 0 20 0; +#X connect 23 0 21 0; +#X connect 24 0 81 0; +#X connect 25 0 40 0; +#X connect 25 0 40 1; +#X connect 25 1 26 0; +#X connect 25 2 27 0; +#X connect 29 0 25 0; +#X connect 33 0 25 0; +#X connect 34 0 33 0; +#X connect 35 0 36 0; +#X connect 36 0 40 2; +#X connect 81 0 25 0; +#X connect 82 0 81 1; diff --git a/l2ork_addons/autotune/autotune~.c b/l2ork_addons/autotune/autotune~.c new file mode 100644 index 0000000000000000000000000000000000000000..3bf5282c6c235eb6347679df750a9c1d74e2fef6 --- /dev/null +++ b/l2ork_addons/autotune/autotune~.c @@ -0,0 +1,1456 @@ +/* + autotune.c + + An auto-tuning PD External, based on + autotalent an auto-tuning LADSPA plugin and an older port by Maxus Germanus + + Free software by Thomas A. Baran. + http://web.mit.edu/tbaran/www/autotune.html + VERSION 0.2 + + Ivica Ico Bukvic <ico.bukvic.net> + VERSION 0.9 + + changes: + *compatible with the latest version of autotalent + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <math.h> +#include "m_pd.h" + +#ifndef MAYER_H +#define MAYER_H +#define REAL float +void mayer_realfft(int n, REAL *real); +void mayer_realifft(int n, REAL *real); +#endif + +#define PI (float)3.14159265358979323846 +#define L2SC (float)3.32192809488736218171 + +#ifndef CLIP +#define CLIP(a, lo, hi) ( (a)>(lo)?( (a)<(hi)?(a):(hi) ):(lo) ) +#endif + +// Variables for FFT routine +typedef struct +{ + int nfft; // size of FFT + int numfreqs; // number of frequencies represented (nfft/2 + 1) + float* fft_data; // array for writing/reading to/from FFT function +} fft_vars; + +// Constructor for FFT routine +fft_vars* fft_con(int nfft) +{ + fft_vars* membvars = (fft_vars*) malloc(sizeof(fft_vars)); + + membvars->nfft = nfft; + membvars->numfreqs = nfft/2 + 1; + + membvars->fft_data = (float*) calloc(nfft, sizeof(float)); + + return membvars; +} + +// Destructor for FFT routine +void fft_des(fft_vars* membvars) +{ + free(membvars->fft_data); + + free(membvars); +} + +// Perform forward FFT of real data +// Accepts: +// x - pointer to struct of FFT variables +// input - pointer to an array of (real) input values, size nfft +// output_re - pointer to an array of the real part of the output, +// size nfft/2 + 1 +// output_im - pointer to an array of the imaginary part of the output, +// size nfft/2 + 1 +void fft_forward(fft_vars* membvars, float* input, float* output_re, float* output_im) +{ + int ti; + int nfft; + int hnfft; + int numfreqs; + + nfft = membvars->nfft; + hnfft = nfft/2; + numfreqs = membvars->numfreqs; + + for (ti=0; ti<nfft; ti++) { + membvars->fft_data[ti] = input[ti]; + } + + mayer_realfft(nfft, membvars->fft_data); + + output_im[0] = 0; + for (ti=0; ti<hnfft; ti++) { + output_re[ti] = membvars->fft_data[ti]; + output_im[ti+1] = membvars->fft_data[nfft-1-ti]; + } + output_re[hnfft] = membvars->fft_data[hnfft]; + output_im[hnfft] = 0; +} + +// Perform inverse FFT, returning real data +// Accepts: +// x - pointer to struct of FFT variables +// input_re - pointer to an array of the real part of the output, +// size nfft/2 + 1 +// input_im - pointer to an array of the imaginary part of the output, +// size nfft/2 + 1 +// output - pointer to an array of (real) input values, size nfft +void fft_inverse(fft_vars* membvars, float* input_re, float* input_im, float* output) +{ + int ti; + int nfft; + int hnfft; + int numfreqs; + + nfft = membvars->nfft; + hnfft = nfft/2; + numfreqs = membvars->numfreqs; + + for (ti=0; ti<hnfft; ti++) { + membvars->fft_data[ti] = input_re[ti]; + membvars->fft_data[nfft-1-ti] = input_im[ti+1]; + } + membvars->fft_data[hnfft] = input_re[hnfft]; + + mayer_realifft(nfft, membvars->fft_data); + + for (ti=0; ti<nfft; ti++) { + output[ti] = membvars->fft_data[ti]; + } +} + +// DONE WITH FFT CODE + + + +void *autotune_class; + +typedef struct _autotune // Data structure for this object PD +{ + t_object x_obj; + float x_f; + + // parameters + + float fTune; + float fFixed; + float fPull; + float fA; + float fBb; + float fB; + float fC; + float fDb; + float fD; + float fEb; + float fE; + float fF; + float fGb; + float fG; + float fAb; + float fAmount; + //float fGlide; + float fSmooth; + float fScwarp; + float fLfoamp; + float fShift; + float fLforate; + float fLfoshape; + float fLfosymm; + float fLfoquant; + float fFcorr; + float fFwarp; + float fMix; + float fPitch; + float fConf; + + fft_vars* fx; // member variables for fft routine + unsigned long fs; // Sample rate + unsigned long cbsize; // size of circular buffer + unsigned long corrsize; // cbsize/2 + 1 + unsigned long cbiwr; + unsigned long cbord; + float* cbi; // circular input buffer + float* cbf; // circular formant correction buffer + float* cbo; // circular output buffer + float* cbwindow; // hann of length N/2, zeros for the rest + float* acwinv; // inverse of autocorrelation of window + float* hannwindow; // length-N hann + int noverlap; + float* ffttime; + float* fftfreqre; + float* fftfreqim; + + // VARIABLES FOR LOW-RATE SECTION + float aref; // A tuning reference (Hz) + float inpitch; // Input pitch (semitones) + float conf; // Confidence of pitch period estimate (between 0 and 1) + float outpitch; // Output pitch (semitones) + float vthresh; // Voiced speech threshold + float pperiod; // Pitch period (seconds) + float pitch; // Pitch (semitones) + float pitchpers; // Pitch persist (semitones) + float pmax; // Maximum allowable pitch period (seconds) + float pmin; // Minimum allowable pitch period (seconds) + unsigned long nmax; // Maximum period index for pitch prd est + unsigned long nmin; // Minimum period index for pitch prd est + float lrshift; // Shift prescribed by low-rate section + int ptarget; // Pitch target, between 0 and 11 + float sptarget; // Smoothed pitch target + //float sptarget; // Smoothed pitch target + //int wasvoiced; // 1 if previous frame was voiced + //float persistamt; // Proportion of previous pitch considered during next voiced period + //float glidepersist; + float lfophase; + + // VARIABLES FOR PITCH SHIFTER + //float phprd; // phase period + float phprdd; // default (unvoiced) phase period + float inphinc; // input phase increment + float outphinc; // input phase increment + float phincfact; // factor determining output phase increment + float phasein; + float phaseout; + float* frag; // windowed fragment of speech + unsigned long fragsize; // size of fragment in samples + float clockinterval; + void *periodout, *confout; // floatout for pitch + void *clock; + + // VARIABLES FOR FORMANT CORRECTOR + unsigned int ford; + float falph; + float flamb; + float* fk; + float* fb; + float* fc; + float* frb; + float* frc; + float* fsig; + float* fsmooth; + float fhp; + float flp; + float flpa; + float** fbuff; + float* ftvec; + float fmute; + float fmutealph; + +} t_autotune; + + + +//prototypes for methods +void *autotune_new(); +void autotune_free(t_autotune *x); +void autotune_init(t_autotune *x, unsigned long sr); +t_int *autotune_perform(t_int *w); +void autotune_dsp(t_autotune *x, t_signal **sp, short *count); +void autotune_assist(t_autotune *x, void *b, long m, long a, char *s); +//void autotune_setparam(t_autotune *x, t_symbol *m, short argc, t_atom *argv); +void autotune_list(t_autotune *x, t_symbol *m, short argc, t_atom *argv); +void autotune_mix(t_autotune *x, t_floatarg f); +void autotune_shift(t_autotune *x, t_floatarg f); +void autotune_pull(t_autotune *x, t_floatarg f); +void autotune_pull_pitch(t_autotune *x, t_floatarg f); +void autotune_tune(t_autotune *x, t_floatarg f); +void autotune_correction(t_autotune *x, t_floatarg f); +void autotune_smooth(t_autotune *x, t_floatarg f); +void autotune_lfo_depth(t_autotune *x, t_floatarg f); +void autotune_lfo_rate(t_autotune *x, t_floatarg f); +void autotune_lfo_shape(t_autotune *x, t_floatarg f); +void autotune_lfo_symmetry(t_autotune *x, t_floatarg f); +void autotune_formant_correction(t_autotune *x, t_floatarg f); +void autotune_formant_warp(t_autotune *x, t_floatarg f); +void autotune_scale_warp(t_autotune *x, t_floatarg f); +void autotune_processclock(t_autotune *x); + + + + +void autotune_tilde_setup(void) +{ + autotune_class = class_new(gensym("autotune~"), (t_newmethod)autotune_new, + (t_method)autotune_free ,sizeof(t_autotune), 0,A_GIMME,0); + CLASS_MAINSIGNALIN(autotune_class, t_autotune, x_f ); + class_addmethod(autotune_class,(t_method)autotune_dsp, gensym("dsp"), 0); + class_addmethod(autotune_class,(t_method)autotune_assist, gensym("assist"), 0); + class_addmethod(autotune_class,(t_method)autotune_list,gensym("list"),A_GIMME,0); + class_addmethod(autotune_class,(t_method)autotune_mix,gensym("mix"),A_FLOAT,0); + class_addmethod(autotune_class,(t_method)autotune_shift,gensym("shift"),A_FLOAT,0); + class_addmethod(autotune_class,(t_method)autotune_pull,gensym("pull"),A_FLOAT,0); + class_addmethod(autotune_class,(t_method)autotune_pull_pitch,gensym("pullpitch"),A_FLOAT,0); + class_addmethod(autotune_class,(t_method)autotune_tune,gensym("tune"),A_FLOAT,0); + class_addmethod(autotune_class,(t_method)autotune_correction,gensym("correct"),A_FLOAT,0); + class_addmethod(autotune_class,(t_method)autotune_smooth,gensym("smooth"),A_FLOAT,0); + class_addmethod(autotune_class,(t_method)autotune_lfo_depth,gensym("lfodepth"),A_FLOAT,0); + class_addmethod(autotune_class,(t_method)autotune_lfo_rate,gensym("lforate"),A_FLOAT,0); + class_addmethod(autotune_class,(t_method)autotune_lfo_shape,gensym("lfoshape"),A_FLOAT,0); + class_addmethod(autotune_class,(t_method)autotune_lfo_symmetry,gensym("lfosym"),A_FLOAT,0); + class_addmethod(autotune_class,(t_method)autotune_formant_correction,gensym("fcorr"),A_FLOAT,0); + class_addmethod(autotune_class,(t_method)autotune_formant_warp,gensym("warp"),A_FLOAT,0); + class_addmethod(autotune_class,(t_method)autotune_scale_warp,gensym("scwarp"),A_FLOAT,0); + post("autotune~ v.0.9"); + post("Ivica Ico Bukvic 2015"); +} + + + +// Create - Contruction of signal inlets and outlets +void *autotune_new() +{ + unsigned long sr; + t_autotune *x = (t_autotune *)pd_new(autotune_class); + + if(sys_getsr()) sr = sys_getsr(); + else sr = 44100; + + autotune_init(x , sr); + + // second argument = number of signal inlets + //dsp_setup((t_object *)x, 1); + //inlet_new(&x->x_obj, &x->x_obj.ob_pd,gensym("signal")); + //inlet_new(&x->x_obj, &x->x_obj.ob_pd,gensym("signal"), gensym("signal")); + + //floatinlet_new (&x->x_obj, &x->fAmount); + //inlet_new (&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("correct")); + + //floatinlet_new (&x->x_obj, &x->fGlide); + + //floatinlet_new (&x->x_obj, &x->fSmooth); + //inlet_new (&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("smooth")); + + //floatinlet_new (&x->x_obj, &x->fMix); + //inlet_new (&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("mix")); + + //floatinlet_new (&x->x_obj, &x->fShift); + //inlet_new (&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("shift")); + + //floatinlet_new (&x->x_obj, &x->fTune); + //inlet_new (&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("tune")); + + //floatinlet_new (&x->x_obj, &x->fPersist); + //symbolinlet_new (&x->x_obj, &x->fAmount); + + outlet_new(&x->x_obj, gensym("signal")); + //x->f_out = outlet_new(&x->x_obj, &s_float); + x->confout = outlet_new(&x->x_obj, &s_float); + x->periodout = outlet_new(&x->x_obj, &s_float); + //x->confout = outlet_new(x, "float"); + //x->periodout = outlet_new(x, "float"); + x->clock = clock_new(x,(t_method)autotune_processclock); + x->clockinterval = 10.; + + return (x); // Return the pointer +} + +// Destroy +void autotune_free(t_autotune *x) +{ + //dsp_free((t_object *)x); // Always call dsp_free first in this routine + unsigned int ti; + clock_unset(x->clock); + clock_free(x->clock); + fft_des(x->fx); + freebytes(x->cbi,0); + freebytes(x->cbf,0); + freebytes(x->cbo,0); + //freebytes(x->cbonorm,0); + freebytes(x->cbwindow,0); + freebytes(x->hannwindow,0); + freebytes(x->acwinv,0); + freebytes(x->frag,0); + freebytes(x->ffttime,0); + freebytes(x->fftfreqre,0); + freebytes(x->fftfreqim,0); + free(x->fk); + free(x->fb); + free(x->fc); + free(x->frb); + free(x->frc); + free(x->fsmooth); + free(x->fsig); + for (ti=0; ti<x->ford; ti++) { + free(x->fbuff[ti]); + } + free(x->fbuff); + free(x->ftvec); +} + +void autotune_init(t_autotune *x,unsigned long sr) +{ + unsigned long ti; + + x->fs = sr; + x->aref = 440; + x->fTune = x->aref; + + if (x->fs >=88200) { + x->cbsize = 4096; + } + else { + x->cbsize = 2048; + } + x->corrsize = x->cbsize / 2 + 1; + + x->pmax = 1/(float)70; // max and min periods (ms) + x->pmin = 1/(float)2400; // eventually may want to bring these out as sliders + + x->pperiod = x->pmax; + + x->nmax = (unsigned long)(x->fs * x->pmax); + if (x->nmax > x->corrsize) { + x->nmax = x->corrsize; + } + x->nmin = (unsigned long)(x->fs * x->pmin); + + x->cbi = (float*) calloc(x->cbsize, sizeof(float)); + x->cbf = (float*) calloc(x->cbsize, sizeof(float)); + x->cbo = (float*) calloc(x->cbsize, sizeof(float)); + //x->cbonorm = (float*) calloc(x->cbsize, sizeof(float)); + + x->cbiwr = 0; + x->cbord = 0; + + x->lfophase = 0; + + // Initialize formant corrector + x->ford = 7; // should be sufficient to capture formants + x->falph = pow(0.001, (float) 80 / (x->fs)); + x->flamb = -(0.8517*sqrt(atan(0.06583*x->fs))-0.1916); // or about -0.88 @ 44.1kHz + x->fk = calloc(x->ford, sizeof(float)); + x->fb = calloc(x->ford, sizeof(float)); + x->fc = calloc(x->ford, sizeof(float)); + x->frb = calloc(x->ford, sizeof(float)); + x->frc = calloc(x->ford, sizeof(float)); + x->fsig = calloc(x->ford, sizeof(float)); + x->fsmooth = calloc(x->ford, sizeof(float)); + x->fhp = 0; + x->flp = 0; + x->flpa = pow(0.001, (float) 10 / (x->fs)); + x->fbuff = (float**) malloc((x->ford)*sizeof(float*)); + for (ti=0; ti<x->ford; ti++) { + x->fbuff[ti] = calloc(x->cbsize, sizeof(float)); + } + x->ftvec = calloc(x->ford, sizeof(float)); + x->fmute = 1; + x->fmutealph = pow(0.001, (float)1 / (x->fs)); + + // Standard raised cosine window, max height at N/2 + x->hannwindow = (float*) calloc(x->cbsize, sizeof(float)); + for (ti=0; ti<x->cbsize; ti++) { + x->hannwindow[ti] = -0.5*cos(2*PI*ti/(x->cbsize - 1)) + 0.5; + } + + // Generate a window with a single raised cosine from N/4 to 3N/4 + x->cbwindow = (float*) calloc(x->cbsize, sizeof(float)); + for (ti=0; ti<(x->cbsize / 2); ti++) { + x->cbwindow[ti+x->cbsize/4] = -0.5*cos(4*PI*ti/(x->cbsize - 1)) + 0.5; + } + + x->noverlap = 4; + + x->fx = fft_con(x->cbsize); + + x->ffttime = (float*) calloc(x->cbsize, sizeof(float)); + x->fftfreqre = (float*) calloc(x->corrsize, sizeof(float)); + x->fftfreqim = (float*) calloc(x->corrsize, sizeof(float)); + + + // ---- Calculate autocorrelation of window ---- + x->acwinv = (float*) calloc(x->cbsize, sizeof(float)); + for (ti=0; ti<x->cbsize; ti++) { + x->ffttime[ti] = x->cbwindow[ti]; + } + fft_forward(x->fx, x->cbwindow, x->fftfreqre, x->fftfreqim); + for (ti=0; ti<x->corrsize; ti++) { + x->fftfreqre[ti] = (x->fftfreqre[ti])*(x->fftfreqre[ti]) + (x->fftfreqim[ti])*(x->fftfreqim[ti]); + x->fftfreqim[ti] = 0; + } + fft_inverse(x->fx, x->fftfreqre, x->fftfreqim, x->ffttime); + for (ti=1; ti<x->cbsize; ti++) { + x->acwinv[ti] = x->ffttime[ti]/x->ffttime[0]; + if (x->acwinv[ti] > 0.000001) { + x->acwinv[ti] = (float)1/x->acwinv[ti]; + } + else { + x->acwinv[ti] = 0; + } + } + x->acwinv[0] = 1; + // ---- END Calculate autocorrelation of window ---- + + x->lrshift = 0; + x->ptarget = 0; + x->sptarget = 0; + //x->sptarget = 0; + //x->wasvoiced = 0; + //x->persistamt = 0; + + //x->glidepersist = 100; // 100 ms glide persist + + x->vthresh = 0.7; // The voiced confidence (unbiased peak) threshold level + + // Pitch shifter initialization + x->phprdd = 0.01; // Default period + //x->phprd = x->phprdd; + x->inphinc = (float)1/(x->phprdd * x->fs); + x->phincfact = 1; + x->phasein = 0; + x->phaseout = 0; + x->frag = (float*) calloc(x->cbsize, sizeof(float)); + x->fragsize = 0; +} + +//*********************// +// DSP Methods PD // +//*********************// + +void autotune_dsp(t_autotune *x, t_signal **sp, short *count) +{ + clock_delay(x->clock, 0.); + + if(x->fs != sp[0]->s_sr) autotune_init(x, sp[0]->s_sr); + + dsp_add(autotune_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + + +//*************************// +// Perform Routine PD// +//*************************// +t_int *autotune_perform(t_int *w) +{ + t_autotune *x = (t_autotune *)(w[1]); // object is first arg + t_float *in = (t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + unsigned long SampleCount = (unsigned long)(w[4]); + + // copy struct variables to local + + /*float fA = x->fA; + float fBb = x->fBb; + float fB = x->fB; + float fC = x->fC; + float fDb = x->fDb; + float fD = x->fD; + float fEb = x->fEb; + float fE = x->fE; + float fF = x->fF; + float fGb = x->fGb; + float fG = x->fG; + float fAb = x->fAb;*/ + //float fGlide = x->fGlide; + //float fPersist = x->glidepersist; + + int iNotes[12]; + int iPitch2Note[12]; + int iNote2Pitch[12]; + int numNotes; + + float fAmount = x->fAmount; + float fSmooth = x->fSmooth * 0.8; + float fTune = x->fTune; + iNotes[0] = (int) x->fA; + iNotes[1] = (int) x->fBb; + iNotes[2] = (int) x->fB; + iNotes[3] = (int) x->fC; + iNotes[4] = (int) x->fDb; + iNotes[5] = (int) x->fD; + iNotes[6] = (int) x->fEb; + iNotes[7] = (int) x->fE; + iNotes[8] = (int) x->fF; + iNotes[9] = (int) x->fGb; + iNotes[10] = (int) x->fG; + iNotes[11] = (int) x->fAb; + float fFixed = x->fFixed; + float fPull = x->fPull; + float fShift = x->fShift; + int iScwarp = x->fScwarp; + float fLfoamp = x->fLfoamp; + float fLforate = x->fLforate; + float fLfoshape = x->fLfoshape; + float fLfosymm = x->fLfosymm; + int iLfoquant = x->fLfoquant; + int iFcorr = x->fFcorr; + float fFwarp = x->fFwarp; + float fMix = x->fMix; + + //x->aref = (float)440*pow(2,fTune/12); + + unsigned long int lSampleIndex; + + unsigned long N = x->cbsize; + unsigned long Nf = x->corrsize; + unsigned long fs = x->fs; + + float pmax = x->pmax; + float pmin = x->pmin; + unsigned long nmax = x->nmax; + unsigned long nmin = x->nmin; + + //float pperiod = x->pperiod; + //float pitch = x->pitch; + + // + + long int ti; + long int ti2; + long int ti3; + long int ti4; + float tf; + float tf2; + float tf3; + + // Variables for cubic spline interpolator + float indd; + int ind0; + int ind1; + int ind2; + int ind3; + float vald; + float val0; + float val1; + float val2; + float val3; + + int lowersnap; + int uppersnap; + float lfoval; + + float pperiod; + float inpitch; + float conf; + float outpitch; + float aref; + float fa; + float fb; + float fc; + float fk; + float flamb; + float frlamb; + float falph; + float foma; + float f1resp; + float f0resp; + float flpa; + int ford; + + // Some logic for the semitone->scale and scale->semitone conversion + // If no notes are selected as being in the scale, instead snap to all notes + ti2 = 0; + for (ti=0; ti<12; ti++) { + if (iNotes[ti]>=0) { + iPitch2Note[ti] = ti2; + iNote2Pitch[ti2] = ti; + ti2 = ti2 + 1; + } + else { + iPitch2Note[ti] = -1; + } + } + numNotes = ti2; + while (ti2<12) { + iNote2Pitch[ti2] = -1; + ti2 = ti2 + 1; + } + if (numNotes==0) { + for (ti=0; ti<12; ti++) { + iNotes[ti] = 1; + iPitch2Note[ti] = ti; + iNote2Pitch[ti] = ti; + } + numNotes = 12; + } + iScwarp = (iScwarp + numNotes*5)%numNotes; + + ford = x->ford; + falph = x->falph; + foma = (float)1 - falph; + flpa = x->flpa; + flamb = x->flamb; + tf = pow((float)2,fFwarp/2)*(1+flamb)/(1-flamb); + frlamb = (tf - 1)/(tf + 1); + + x->aref = (float)fTune; + + N = x->cbsize; + Nf = x->corrsize; + fs = x->fs; + + pmax = x->pmax; + pmin = x->pmin; + nmax = x->nmax; + nmin = x->nmin; + + aref = x->aref; + pperiod = x->pmax; + inpitch = x->inpitch; + conf = x->conf; + outpitch = x->outpitch; + + + //******************// + // MAIN DSP LOOP // + //******************// + for (lSampleIndex = 0; lSampleIndex < SampleCount; lSampleIndex++) + { + + // load data into circular buffer + tf = (float) *(in++); + ti4 = x->cbiwr; + //fprintf(stderr,"ti4=%d N=%d\n", ti4, N); + x->cbi[ti4] = tf; + /*x->cbiwr++; + if (x->cbiwr >= N) { + x->cbiwr = 0; + }*/ + + if (iFcorr>=1) { + // Somewhat experimental formant corrector + // formants are removed using an adaptive pre-filter and + // re-introduced after pitch manipulation using post-filter + // tf is signal input + fa = tf - x->fhp; // highpass pre-emphasis filter + x->fhp = tf; + fb = fa; + for (ti=0; ti<(long)ford; ti++) { + x->fsig[ti] = fa*fa*foma + x->fsig[ti]*falph; + fc = (fb-x->fc[ti])*flamb + x->fb[ti]; + x->fc[ti] = fc; + x->fb[ti] = fb; + fk = fa*fc*foma + x->fk[ti]*falph; + x->fk[ti] = fk; + tf = fk/(x->fsig[ti] + 0.000001); + tf = tf*foma + x->fsmooth[ti]*falph; + x->fsmooth[ti] = tf; + x->fbuff[ti][ti4] = tf; + fb = fc - tf*fa; + fa = fa - tf*fc; + } + x->cbf[ti4] = fa; + // Now hopefully the formants are reduced + // More formant correction code at the end of the DSP loop + } + else { + x->cbf[ti4] = tf; + } + + //fprintf(stderr,"x->cbf[ti4]=%f\n", x->cbf[ti4]); + + // Input write pointer logic + x->cbiwr++; + if (x->cbiwr >= N) { + x->cbiwr = 0; + } + + + // ********************// + // * Low-rate section *// + // ********************// + + //fprintf(stderr,"overlap=%d outpitch=%f inpitch=%f\n", (x->cbiwr)%(N/x->noverlap), outpitch, inpitch); + + // Every N/noverlap samples, run pitch estimation / correction code + if ((x->cbiwr)%(N/x->noverlap) == 0) { + + //fprintf(stderr,"ti4=%d N=%d\n", ti4, N); + // ---- Obtain autocovariance ---- // + + // Window and fill FFT buffer + ti2 = (long) x->cbiwr; + for (ti=0; ti<(long)N; ti++) { + x->ffttime[ti] = (float)(x->cbi[(ti2-ti)%N]*x->cbwindow[ti]); + } + + // Calculate FFT + fft_forward(x->fx, x->ffttime, x->fftfreqre, x->fftfreqim); + + // Remove DC + x->fftfreqre[0] = 0; + x->fftfreqim[0] = 0; + + // Take magnitude squared + for (ti=1; ti< (long) Nf; ti++) { + x->fftfreqre[ti] = (x->fftfreqre[ti])*(x->fftfreqre[ti]) + (x->fftfreqim[ti])*(x->fftfreqim[ti]); + x->fftfreqim[ti] = 0; + } + + // Calculate IFFT + fft_inverse(x->fx, x->fftfreqre, x->fftfreqim, x->ffttime); + + // Normalize + for (ti=1; ti<(long)N; ti++) { + x->ffttime[ti] = x->ffttime[ti] / x->ffttime[0]; + } + x->ffttime[0] = 1; + + // ---- END Obtain autocovariance ---- + + + // ---- Calculate pitch and confidence ---- + + // Calculate pitch period + // Pitch period is determined by the location of the max (biased) + // peak within a given range + // Confidence is determined by the corresponding unbiased height + tf2 = 0; + pperiod = pmin; + for (ti=nmin; ti<(long)nmax; ti++) { + ti2 = ti-1; + ti3 = ti+1; + if (ti2<0) { + ti2 = 0; + } + if (ti3>(long)Nf) { + ti3 = Nf; + } + tf = x->ffttime[ti]; + + if (tf>x->ffttime[ti2] && tf>=x->ffttime[ti3] && tf>tf2) { + tf2 = tf; + ti4 = ti; + //conf = tf*x->acwinv[ti]; + //pperiod = (float)ti/fs; + } + } + if (tf2>0) { + conf = tf2*x->acwinv[ti4]; + if (ti4>0 && ti4<(long)Nf) { + // Find the center of mass in the vicinity of the detected peak + tf = x->ffttime[ti4-1]*(ti4-1); + tf = tf + x->ffttime[ti4]*(ti4); + tf = tf + x->ffttime[ti4+1]*(ti4+1); + tf = tf/(x->ffttime[ti4-1] + x->ffttime[ti4] + x->ffttime[ti4+1]); + pperiod = tf/fs; + } + else { + pperiod = (float)ti4/fs; + } + } + + // Convert to semitones + tf = (float) -12*log10((float)aref*pperiod)*L2SC; + //fprintf(stderr,"tf=%f aref=%f pperiod=%f\n", tf, aref, pperiod); + if (conf>=x->vthresh) { + inpitch = tf; + x->inpitch = tf; // update pitch only if voiced + } + x->conf = conf; + + x->fPitch = inpitch; + x->fConf = conf; + + //x->pitch = pitch; + x->pperiod = pperiod; + //x->conf = conf; + + // ---- END Calculate pitch and confidence ---- + + + /* + // ---- Determine pitch target ---- + + // If voiced + if (conf>=x->vthresh) { + // TODO: Scale sliders + // Determine pitch target + tf = -1; + tf2 = 0; + tf3 = 0; + for (ti=0; ti<12; ti++) { + switch (ti) { + case 0: + tf2 = fA; + break; + case 1: + tf2 = fBb; + break; + case 2: + tf2 = fB; + break; + case 3: + tf2 = fC; + break; + case 4: + tf2 = fDb; + break; + case 5: + tf2 = fD; + break; + case 6: + tf2 = fEb; + break; + case 7: + tf2 = fE; + break; + case 8: + tf2 = fF; + break; + case 9: + tf2 = fGb; + break; + case 10: + tf2 = fG; + break; + case 11: + tf2 = fAb; + break; + } + // if (ti==x->ptarget) { + // tf2 = tf2 + 0.01; // add a little hysteresis + // } + tf2 = tf2 - (float)fabs( (pitch-(float)ti)/6 - 2*floorf(((pitch-(float)ti)/12 + 0.5)) ); // like a likelihood function + if (tf2>=tf) { // that we're maximizing + tf3 = (float)ti; // to find the target pitch + tf = tf2; + } + } + x->ptarget = tf3; + + // Glide persist + if (x->wasvoiced == 0) { + x->wasvoiced = 1; + tf = x->persistamt; + x->sptarget = (1-tf)*x->ptarget + tf*x->sptarget; + x->persistamt = 1; + } + + // Glide on circular scale + tf3 = (float)x->ptarget - x->sptarget; + tf3 = tf3 - (float)12*floorf(tf3/12 + 0.5); + if (fGlide>0) { + tf2 = (float)1-pow((float)1/24, (float)N * 1000/ (x->noverlap*fs*fGlide)); + } + else { + tf2 = 1; + } + x->sptarget = x->sptarget + tf3*tf2; + } + // If not voiced + else { + x->wasvoiced = 0; + + // Keep track of persist amount + if (fPersist>0) { + tf = pow((float)1/2, (float)N * 1000/ (x->noverlap*fs*fPersist)); + } + else { + tf = 0; + } + x->persistamt = x->persistamt * tf; // Persist amount decays exponentially + } + // END If voiced + + // ---- END Determine pitch target ---- + + + // ---- Determine correction to feed to the pitch shifter ---- + tf = x->sptarget - pitch; // Correction amount + tf = tf - (float)12*floorf(tf/12 + 0.5); // Never do more than +- 6 semitones of correction + if (conf<x->vthresh) { + tf = 0; + } + x->lrshift = fShift + fAmount*tf; // Add in pitch shift slider + + + // ---- Compute variables for pitch shifter that depend on pitch --- + x->phincfact = (float)pow(2, x->lrshift/12); + if (conf>=x->vthresh) { // Keep old period when unvoiced + x->inphinc = (float)1/(pperiod*fs); + x->phprd = pperiod*2; + } + } + // ************************ + // * END Low-Rate Section * + // ************************ + */ + //fprintf(stderr,"%f %f %f %f", inpitch, outpitch, pperiod, ti4); + + // ---- Modify pitch in all kinds of ways! ---- + + outpitch = inpitch; + + //fprintf(stderr,"outpitch=%f\n", outpitch); + + // Pull to fixed pitch + outpitch = (1-fPull)*outpitch + fPull*fFixed; + + // -- Convert from semitones to scale notes -- + ti = (int)(outpitch/12 + 32) - 32; // octave + tf = outpitch - ti*12; // semitone in octave + ti2 = (int)tf; + ti3 = ti2 + 1; + // a little bit of pitch correction logic, since it's a convenient place for it + if (iNotes[ti2%12]<0 || iNotes[ti3%12]<0) { // if between 2 notes that are more than a semitone apart + lowersnap = 1; + uppersnap = 1; + } + else { + lowersnap = 0; + uppersnap = 0; + if (iNotes[ti2%12]==1) { // if specified by user + lowersnap = 1; + } + if (iNotes[ti3%12]==1) { // if specified by user + uppersnap = 1; + } + } + // (back to the semitone->scale conversion) + // finding next lower pitch in scale + while (iNotes[(ti2+12)%12]<0) { + ti2 = ti2 - 1; + } + // finding next higher pitch in scale + while (iNotes[ti3%12]<0) { + ti3 = ti3 + 1; + } + tf = (tf-ti2)/(ti3-ti2) + iPitch2Note[(ti2+12)%12]; + if (ti2<0) { + tf = tf - numNotes; + } + outpitch = tf + numNotes*ti; + // -- Done converting to scale notes -- + + // The actual pitch correction + ti = (int)(outpitch+128) - 128; + tf = outpitch - ti - 0.5; + ti2 = ti3-ti2; + if (ti2>2) { // if more than 2 semitones apart, put a 2-semitone-like transition halfway between + tf2 = (float)ti2/2; + } + else { + tf2 = (float)1; + } + if (fSmooth<0.001) { + tf2 = tf*tf2/0.001; + } + else { + tf2 = tf*tf2/fSmooth; + } + if (tf2<-0.5) tf2 = -0.5; + if (tf2>0.5) tf2 = 0.5; + tf2 = 0.5*sin(PI*tf2) + 0.5; // jumping between notes using horizontally-scaled sine segment + tf2 = tf2 + ti; + if ( (tf<0.5 && lowersnap) || (tf>=0.5 && uppersnap) ) { + outpitch = fAmount*tf2 + ((float)1-fAmount)*outpitch; + } + + // Add in pitch shift + outpitch = outpitch + fShift; + + // LFO logic + tf = fLforate*N/(x->noverlap*fs); + if (tf>1) tf=1; + x->lfophase = x->lfophase + tf; + if (x->lfophase>1) x->lfophase = x->lfophase-1; + lfoval = x->lfophase; + tf = (fLfosymm + 1)/2; + if (tf<=0 || tf>=1) { + if (tf<=0) lfoval = 1-lfoval; + } + else { + if (lfoval<=tf) { + lfoval = lfoval/tf; + } + else { + lfoval = 1 - (lfoval-tf)/(1-tf); + } + } + if (fLfoshape>=0) { + // linear combination of cos and line + lfoval = (0.5 - 0.5*cos(lfoval*PI))*fLfoshape + lfoval*(1-fLfoshape); + lfoval = fLfoamp*(lfoval*2 - 1); + } + else { + // smoosh the sine horizontally until it's squarish + tf = 1 + fLfoshape; + if (tf<0.001) { + lfoval = (lfoval - 0.5)*2/0.001; + } + else { + lfoval = (lfoval - 0.5)*2/tf; + } + if (lfoval>1) lfoval = 1; + if (lfoval<-1) lfoval = -1; + lfoval = fLfoamp*sin(lfoval*PI*0.5); + } + // add in quantized LFO + if (iLfoquant>=1) { + outpitch = outpitch + (int)(numNotes*lfoval + numNotes + 0.5) - numNotes; + } + + + // Convert back from scale notes to semitones + outpitch = outpitch + iScwarp; // output scale rotate implemented here + ti = (int)(outpitch/numNotes + 32) - 32; + tf = outpitch - ti*numNotes; + ti2 = (int)tf; + ti3 = ti2 + 1; + outpitch = iNote2Pitch[ti3%numNotes] - iNote2Pitch[ti2]; + if (ti3>=numNotes) { + outpitch = outpitch + 12; + } + outpitch = outpitch*(tf - ti2) + iNote2Pitch[ti2]; + outpitch = outpitch + 12*ti; + outpitch = outpitch - (iNote2Pitch[iScwarp] - iNote2Pitch[0]); //more scale rotation here + + // add in unquantized LFO + if (iLfoquant<=0) { + outpitch = outpitch + lfoval*2; + } + + if (outpitch<-36) outpitch = -48; + if (outpitch>24) outpitch = 24; + + x->outpitch = outpitch; + + // ---- END Modify pitch in all kinds of ways! ---- + + // Compute variables for pitch shifter that depend on pitch + x->inphinc = aref*pow(2,inpitch/12)/fs; + x->outphinc = aref*pow(2,outpitch/12)/fs; + x->phincfact = x->outphinc/x->inphinc; + } + // ************************ + // * END Low-Rate Section * + // ************************ + + + // ***************** + // * Pitch Shifter * + // ***************** + + // Pitch shifter (kind of like a pitch-synchronous version of Fairbanks' technique) + // Note: pitch estimate is naturally N/2 samples old + x->phasein = x->phasein + x->inphinc; + x->phaseout = x->phaseout + x->inphinc*x->phincfact; + + // If it happens that there are no snippets placed at the output, grab a new snippet! + /* if (x->cbonorm[((long int)x->cbord + (long int)(N/2*(1 - (float)1 / x->phincfact)))%N] < 0.2) { */ + /* post( "help!"); */ + /* x->phasein = 1; */ + /* x->phaseout = 1; */ + /* } */ + + // When input phase resets, take a snippet from N/2 samples in the past + if (x->phasein >= 1) { + x->phasein = x->phasein - 1; + ti2 = x->cbiwr - (long int)N/2; + for (ti=-((long int)N)/2; ti<(long int)N/2; ti++) { + x->frag[ti%N] = x->cbi[(ti + ti2)%N]; + } + } + + // When output phase resets, put a snippet N/2 samples in the future + if (x->phaseout >= 1) { + x->fragsize = x->fragsize*2; + if (x->fragsize >= N) { + x->fragsize = N; + } + x->phaseout = x->phaseout - 1; + ti2 = x->cbord + N/2; + ti3 = (long int)(((float)x->fragsize) / x->phincfact); + if (ti3>=(long int)N/2) { + ti3 = N/2 - 1; + } + for (ti=-ti3/2; ti<(ti3/2); ti++) { + tf = x->hannwindow[(long int)N/2 + ti*(long int)N/ti3]; + // 3rd degree polynomial interpolator - based on eqns from Hal Chamberlin's book + indd = x->phincfact*ti; + ind1 = (int)indd; + ind2 = ind1+1; + ind3 = ind1+2; + ind0 = ind1-1; + val0 = x->frag[(ind0+N)%N]; + val1 = x->frag[(ind1+N)%N]; + val2 = x->frag[(ind2+N)%N]; + val3 = x->frag[(ind3+N)%N]; + vald = 0; + vald = vald - (float)0.166666666667 * val0 * (indd - ind1) * (indd - ind2) * (indd - ind3); + vald = vald + (float)0.5 * val1 * (indd - ind0) * (indd - ind2) * (indd - ind3); + vald = vald - (float)0.5 * val2 * (indd - ind0) * (indd - ind1) * (indd - ind3); + vald = vald + (float)0.166666666667 * val3 * (indd - ind0) * (indd - ind1) * (indd - ind2); + x->cbo[(ti + ti2 + N)%N] = x->cbo[(ti + ti2 + N)%N] + vald*tf; + } + x->fragsize = 0; + } + x->fragsize++; + + // Get output signal from buffer + tf = x->cbo[x->cbord]; + /*// Normalize + if (tf>0.5) { + tf = (float)1/tf; + } + else { + tf = 1; + }*/ + //tf = tf*x->cbo[x->cbord]; // read buffer + tf = x->cbo[x->cbord]; + x->cbo[x->cbord] = 0; // erase for next cycle + //x->cbonorm[x->cbord] = 0; + x->cbord++; // increment read pointer + if (x->cbord >= N) { + x->cbord = 0; + } + + // ********************* + // * END Pitch Shifter * + // ********************* + + ti4 = (x->cbiwr + 2)%N; + if (iFcorr>=1) { + // The second part of the formant corrector + // This is a post-filter that re-applies the formants, designed + // to result in the exact original signal when no pitch + // manipulation is performed. + // tf is signal input + // gotta run it 3 times because of a pesky delay free loop + // first time: compute 0-response + tf2 = tf; + fa = 0; + fb = fa; + for (ti=0; ti<ford; ti++) { + fc = (fb-x->frc[ti])*frlamb + x->frb[ti]; + tf = x->fbuff[ti][ti4]; + fb = fc - tf*fa; + x->ftvec[ti] = tf*fc; + fa = fa - x->ftvec[ti]; + } + tf = -fa; + for (ti=ford-1; ti>=0; ti--) { + tf = tf + x->ftvec[ti]; + } + f0resp = tf; + // second time: compute 1-response + fa = 1; + fb = fa; + for (ti=0; ti<ford; ti++) { + fc = (fb-x->frc[ti])*frlamb + x->frb[ti]; + tf = x->fbuff[ti][ti4]; + fb = fc - tf*fa; + x->ftvec[ti] = tf*fc; + fa = fa - x->ftvec[ti]; + } + tf = -fa; + for (ti=ford-1; ti>=0; ti--) { + tf = tf + x->ftvec[ti]; + } + f1resp = tf; + // now solve equations for output, based on 0-response and 1-response + tf = (float)2*tf2; + tf2 = tf; + tf = ((float)1 - f1resp + f0resp); + if (tf!=0) { + tf2 = (tf2 + f0resp) / tf; + } + else { + tf2 = 0; + } + // third time: update delay registers + fa = tf2; + fb = fa; + for (ti=0; ti<ford; ti++) { + fc = (fb-x->frc[ti])*frlamb + x->frb[ti]; + x->frc[ti] = fc; + x->frb[ti] = fb; + tf = x->fbuff[ti][ti4]; + fb = fc - tf*fa; + fa = fa - tf*fc; + } + tf = tf2; + tf = tf + flpa*x->flp; // lowpass post-emphasis filter + x->flp = tf; + // Bring up the gain slowly when formant correction goes from disabled + // to enabled, while things stabilize. + if (x->fmute>0.5) { + tf = tf*(x->fmute - 0.5)*2; + } + else { + tf = 0; + } + tf2 = x->fmutealph; + x->fmute = (1-tf2) + tf2*x->fmute; + // now tf is signal output + // ...and we're done messing with formants + } + else { + x->fmute = 0; + } + + // Write audio to output of plugin + // Mix (blend between original (delayed) =0 and shifted/corrected =1) + *(out++) = fMix*tf + (1-fMix)*x->cbi[ti4]; + //*(pfOutput++) = (float) fMix*tf + (1-fMix)*x->cbi[(x->cbiwr - N + 1)%N]; + + } + + return (w + 5); // always add one more than the 2nd argument in dsp_add() +} + +void autotune_mix(t_autotune *x, t_floatarg f) +{ + x->fMix = CLIP(f,0.,1.); + //post("mix=%f", x->fMix); +} + +void autotune_shift(t_autotune *x, t_floatarg f) +{ + x->fShift = CLIP(f,-12.,12.); + //post("shift=%f", x->fShift); +} + +void autotune_pull(t_autotune *x, t_floatarg f) +{ + x->fPull = CLIP(f,0.,1.); + //post("pull=%f", x->fPull); +} + +void autotune_pull_pitch(t_autotune *x, t_floatarg f) +{ + x->fFixed = CLIP(f,-36.,12.); + //post("pullpitch=%f", x->fFixed); +} + +void autotune_tune(t_autotune *x, t_floatarg f) +{ + x->fTune = CLIP(f,400.,480.); + //post("tune=%f", x->fTune); +} + +void autotune_correction(t_autotune *x, t_floatarg f) +{ + x->fAmount = CLIP(f,0.,1.); + //post("correction=%f", x->fAmount); +} + +void autotune_smooth(t_autotune *x, t_floatarg f) +{ + x->fSmooth = CLIP(f,0.,1.); + //post("smooth=%f", x->fSmooth); +} + +void autotune_lfo_depth(t_autotune *x, t_floatarg f) +{ + x->fLfoamp = CLIP(f,0.,1.); + //post("smooth=%f", x->fSmooth); +} + +void autotune_lfo_rate(t_autotune *x, t_floatarg f) +{ + x->fLforate = CLIP(f,0., x->fs/2); + //post("lfo_rate=%f", x->fLforate); +} + +void autotune_lfo_shape(t_autotune *x, t_floatarg f) +{ + //square -> sine ->tri (-1 to 1) + x->fLfoshape = CLIP(f,-1.,1.); + //post("lfo_shape=%f", x->fLfoshape); +} + +void autotune_lfo_symmetry(t_autotune *x, t_floatarg f) +{ + x->fLfosymm = CLIP(f,-1.,1.); + //post("symmetry=%f", x->fLfosymm); +} + +void autotune_formant_correction(t_autotune *x, t_floatarg f) +{ + x->fFcorr = CLIP((int)f, 0.,1.); + //post("fcorr=%f", x->fFcorr); +} + +void autotune_formant_warp(t_autotune *x, t_floatarg f) +{ + x->fFwarp = CLIP(f,-1.,1.); + //post("warp=%f", x->fFwarp); +} + +void autotune_scale_warp(t_autotune *x, t_floatarg f) +{ + x->fScwarp = CLIP(f,-1.,1.); + //post("scwarp=%f", x->fScwarp); +} + +void autotune_list(t_autotune *x, t_symbol *m, short argc, t_atom *argv) +{ + if(argc== 12) + { + x->fA = CLIP(atom_getfloat(argv), -1., 1.) ; + x->fBb = CLIP(atom_getfloat(argv+1), -1., 1.) ; + x->fB = CLIP(atom_getfloat(argv+2), -1., 1.) ; + x->fC = CLIP(atom_getfloat(argv+3), -1., 1.) ; + x->fDb = CLIP(atom_getfloat(argv+4), -1., 1.) ; + x->fD = CLIP(atom_getfloat(argv+5), -1., 1.) ; + x->fEb = CLIP(atom_getfloat(argv+6), -1., 1.) ; + x->fE = CLIP(atom_getfloat(argv+7), -1., 1.) ; + x->fF = CLIP(atom_getfloat(argv+8), -1., 1.) ; + x->fGb = CLIP(atom_getfloat(argv+9), -1., 1.) ; + x->fG = CLIP(atom_getfloat(argv+10), -1., 1.) ; + x->fAb = CLIP(atom_getfloat(argv+11), -1., 1.) ; + /*post("pitches: %d %d %d %d %d %d %d %d %d %d %d %d", + (int)x->fA, (int)x->fBb, (int)x->fB, (int)x->fC, (int)x->fDb, + (int)x->fD, (int)x->fEb, (int)x->fE, (int)x->fF, (int)x->fGb, + (int)x->fG, (int)x->fAb);*/ + } + else {post("bad list"); }; + +} + +void autotune_processclock(t_autotune *x) +{ + clock_delay(x->clock, x->clockinterval); // schedule the next clock + + outlet_float(x->confout, x->conf); + outlet_float(x->periodout, 1.f / x->pperiod); +} + +void autotune_assist(t_autotune *x, void *b, long m, long a, char *s) +{ + if (m == 1) //input + { + sprintf(s,"Signal Input, Messages"); + } + else + { + switch (a) { + case 0: + sprintf(s,"Signal Output"); + break; + case 1: + sprintf(s,"confidence (0-1)(float)"); + break; + case 2: + + sprintf(s,"frequency (hz) (float)"); + break; + + } + } + +}