#ifndef UDO_MELSEQUENCINGPORT #define UDO_MELSEQUENCINGPORT ## /* Extension to sequencing_melodic.udo which permits usage of k-rate frequency arrays This file is part of the SONICS UDO collection by Richard Knight 2021 License: GPL-2.0-or-later http://1bpm.net */ #include "__config__.udo" ; using fftsize for tuning #include "sequencing_melodic.udo" #include "wavetables.udo" #include "frequency_tools.udo" gimel_freqs ftgen 0, 0, -12, -7, 0 ; current notes: index 0 is the length gimel_amps ftgen 0, 0, -12, -7, 0 ; current notes: index 0 is the length gimel_portamento_beatratio init 0.5 ; portamento time as ratio of current beat time gimel_linetype init 0 ; 0=pre-section, 1=post-section /* Automate a frequency/amp line */ instr _mel_linedraw index = p4 ifreq = p5 iamp = p6 icurrentfreq table index, gimel_freqs if (icurrentfreq == 0 && ifreq != 0) then tablew ifreq, index, gimel_freqs elseif (ifreq != 0 && icurrentfreq != ifreq) then tablew line:k(icurrentfreq, p3, ifreq), index, gimel_freqs endif icurrentamp table index, gimel_amps if (icurrentamp != iamp) then tablew line:k(icurrentamp, p3, iamp), index, gimel_amps endif endin instr _mel_linestep_inner if (timeinstk() == 1) then turnoff2 "_mel_linedraw", 0, 0 endif if (table:i(1, gimel_next_notes) != 0) then index = 0 while (index < table:i(0, gimel_next_notes)) do event_i "i", "_mel_linedraw", 1/kr, p3, index, cpsmidinn(table:i(index + 1, gimel_next_notes)), 1 index += 1 od while (index < ftlen(gimel_freqs)) do event_i "i", "_mel_linedraw", 1/kr, p3, index, 0, 0 index += 1 od endif endin instr _mel_linestep icurrentduration mel_length ilinetime = (i(gkseq_beattime) * gimel_portamento_beatratio) if (gimel_linetype == 0) then inextline = icurrentduration - ilinetime else inextline = icurrentduration endif event_i "i", "_mel_linestep_inner", inextline, ilinetime turnoff endin /* Portamento manager: respond to gkmel_section_change trigger by calling _mel_linestep instrument */ instr _mel_linemanager ; set initial freqs index = 0 while (index < table:i(0, gimel_current_notes)) do tablew cpsmidinn(table:i(index + 1, gimel_current_notes)), index, gimel_freqs tablew 1, index, gimel_amps index += 1 od while (index < ftlen(gimel_freqs)) do tablew 0, index, gimel_amps index += 1 od schedkwhen gkmel_section_change, 0, 1, "_mel_linestep", 0, 1 endin schedule "_mel_linemanager", 0.1, 36000 ; notes not ready on 0 ;alwayson "_mel_linemanager" /* Recursively create a chord to be used by mel_tune_portamento; internal use only aout _mel_tune_chord_portamento kfreqmult, ifn, imaxmult, imult, index aout chord output kfreqmult frequency multiplier to apply to tuning ifn wavetable to use imaxmult multiples of harmonics to generate in tuning imult internal multiplier for recursion index internal index for recursion */ opcode _mel_tune_chord_portamento, a, kiipo kfreqmult, ifn, imaxmult, imult, index xin if (index + 1 > ftlen(gimel_amps)) then index = 0 imult += 1 endif aout = oscil(table:k(index, gimel_amps), kfreqmult * table:k(index, gimel_freqs) * pow:k(2, imult), ifn) * 0.1 ; recursion for all chord parts if (imult <= imaxmult) then aout += _mel_tune_chord_portamento(kfreqmult, ifn, imaxmult, imult, index + 1) endif xout aout endop /* PVS morph tuning to current melodic sequencer notes aoutL, aoutR mel_tune_portamento ainL, ainR, [ifn=gifnSine, imult=4, ifftrate=giFFTsize, ifftdiv=giFFTwinFactor, kfreqmult=1] aoutL, aoutR output audio ainL, ainR input audio ifn wavetable to use imaxmult multiples of harmonics to generate in tuning (defaults to 4) ifftrate fft size, defaults to config default ifftdiv fft window division factor (eg 4, 8, 16), defaults to config default kfreqmult frequency multiplier to apply to tuning */ opcode mel_tune_portamento, aa, aaooooP aL, aR, ifn, imaxmult, ifftrate, ifftdiv, kfreqmult xin ifn = (ifn == 0) ? gifnSine : ifn imaxmult = (imaxmult == 0) ? 4 : imaxmult ifftrate = (ifftrate == 0) ? giFFTsize : ifftrate ifftdiv = (ifftdiv == 0) ? giFFTwinFactor : ifftdiv fmods pvsanal _mel_tune_chord_portamento(kfreqmult, ifn, imaxmult), ifftrate, ifftrate/ifftdiv, ifftrate, 1 fL1 pvsanal aL, ifftrate, ifftrate/ifftdiv, ifftrate, 1 fR1 pvsanal aR, ifftrate, ifftrate/ifftdiv, ifftrate, 1 fL2 pvsmorph fL1, fmods, 0, 1 fR2 pvsmorph fR1, fmods, 0, 1 aL1 pvsynth fL2 aR1 pvsynth fR2 idel = (ifftrate)/sr aL1 balance aL1, delay(aL, idel) aR1 balance aR1, delay(aR, idel) xout aL1, aR1 endop /* Bandpass tuning for internal use only, applied to each note frequency for full spectrum bandpass aoutL, aoutR _mel_bandpass_portamento_freqgroup ainL, ainR, kfreq, kbw, iprecise, imult aoutL, aoutR output audio ainL, ainR input audio kfreq frequency to tune to kbw bandwidth of bandpass filters iprecise if 1, use two serial bandpass filters for more precision imult current multiplier for recursion */ opcode _mel_bandpass_portamento_freqgroup, aa, aakkip ainL, ainR, kfreq, kbw, iprecise, imult xin imaxmult = 24 aoutL butterbp ainL, kfreq*imult, kbw aoutR butterbp ainR, kfreq*imult, kbw if (iprecise == 1) then aoutL butterbp aoutL, kfreq*imult, kbw aoutR butterbp aoutR, kfreq*imult, kbw endif if (imult <= imaxmult) then aoutLrec, aoutRrec _mel_bandpass_portamento_freqgroup ainL, ainR, kfreq, kbw, iprecise, imult * 2 aoutL += aoutLrec aoutR += aoutRrec endif xout aoutL, aoutR endop /* Bandpass tuning to current melodic sequencer notes aoutL, aoutR mel_bandpass_portamento ainL, ainR [, kbw=1, iprecise=0, index=0] aoutL, aoutR output audio ainL, ainR input audio kbw bandwidth of bandpass filters iprecise if 1, use two serial bandpass filters for more precision index recursion index for internal use */ opcode mel_bandpass_portamento, aa, aaPoo ainL, ainR, kbw, iprecise, index xin kamp = table:k(index, gimel_amps) if (kamp > 0) then kfreq = table:k(index, gimel_freqs) aoutL, aoutR _mel_bandpass_portamento_freqgroup ainL, ainR, kfreq, kbw, iprecise aoutL *= kamp aoutR *= kamp else aoutL = 0 aoutR = 0 endif if (index < ftlen(gimel_amps)) then aoutLr, aoutRr mel_bandpass_portamento ainL, ainR, kbw, iprecise, index + 1 aoutL += aoutLr aoutR += aoutRr endif xout aoutL, aoutR endop /* Ringmod tuning to current melodic sequencer notes aoutL, aoutR mel_ringmod_portamento ainL, ainR kfreqmult, index aoutL, aoutR output audio ainL, ainR input audio kfreqmult frequency multiplier to apply to current frequencies index recursion index for internal use */ opcode mel_ringmod_portamento, aa, aaPo ainL, ainR, kfreqmult, index xin kamp = table:k(index, gimel_amps) if (kamp > 0) then aoutL, aoutR ringmod1 ainL, ainR, table:k(index, gimel_freqs) * kfreqmult aoutL *= kamp aoutR *= kamp else aoutL = 0 aoutR = 0 endif if (index < ftlen(gimel_amps)) then aoutLr, aoutRr mel_ringmod_portamento ainL, ainR, kfreqmult, index + 1 aoutL += aoutLr aoutR += aoutRr endif xout aoutL, aoutR endop /* Reson tuning to current melodic sequencer notes aoutL, aoutR mel_reson_portamento ainL, ainR kfreqmult, index aoutL, aoutR output audio ainL, ainR input audio kfreqmult frequency multiplier to apply to current frequencies index recursion index for internal use */ opcode mel_reson_portamento, aa, aaPo ainL, ainR, kfreqmult, index xin kamp = table:k(index, gimel_amps) kfreq = table:k(index, gimel_freqs) if (kamp > 0) then aoutL resony ainL, kfreq * kfreqmult, 2, 8, 10 aoutR resony ainR, kfreq * kfreqmult, 2, 8, 10 aoutL balance aoutL, ainL aoutR balance aoutR, ainR aoutL *= kamp aoutR *= kamp else aoutL = 0 aoutR = 0 endif if (index < ftlen(gimel_amps)) then aoutLr, aoutRr mel_reson_portamento ainL, ainR, kfreqmult, index + 1 aoutL += aoutLr aoutR += aoutRr endif xout aoutL, aoutR endop #end