From 1e169bf7bcbb07d466b92f73d25c24e0c7542c5b Mon Sep 17 00:00:00 2001 From: Ben Marx Date: Sat, 30 Dec 2023 22:42:12 +0100 Subject: [PATCH] Flute synth (incl. gated version) --- .../ruby/lib/sonicpi/synths/synthinfo.rb | 192 ++++++++++++++++++ .../gated/sonic-pi-flute_gated.scsyndef | Bin 0 -> 6462 bytes .../compiled/sonic-pi-flute.scsyndef | Bin 0 -> 6442 bytes etc/synthdefs/designs/supercollider/flute.scd | 114 +++++++++++ .../supercollider/gated/flute_gated.scd | 121 +++++++++++ 5 files changed, 427 insertions(+) create mode 100644 etc/synthdefs/compiled/gated/sonic-pi-flute_gated.scsyndef create mode 100644 etc/synthdefs/compiled/sonic-pi-flute.scsyndef create mode 100644 etc/synthdefs/designs/supercollider/flute.scd create mode 100644 etc/synthdefs/designs/supercollider/gated/flute_gated.scd diff --git a/app/server/ruby/lib/sonicpi/synths/synthinfo.rb b/app/server/ruby/lib/sonicpi/synths/synthinfo.rb index 05893c09dc..c3c76a97e6 100644 --- a/app/server/ruby/lib/sonicpi/synths/synthinfo.rb +++ b/app/server/ruby/lib/sonicpi/synths/synthinfo.rb @@ -4927,6 +4927,197 @@ def specific_arg_info end end + class Flute < SonicPiSynth + def name + "Flute" + end + + def introduced + Version.new(4,6,0) + end + + def synth_name + "flute" + end + + def on_start(studio, args_h) + args_h[:rand_buf] = studio.rand_buf_id + end + + def doc + "A synth that can create sounds of different types of flutes or even of organ pipes. Adapted from [Perry Cook's Slide Flute](https://ccrma.stanford.edu/software/clm/compmus/clm-tutorials/pm.html#s-f), this synth is created based on a physical model of a slide flute, a so called waveguide. Unlike most other synths, it does not have a conventional oscillator for sound creation. The sound builds up from feedback. This is also the reason why only a range of notes will produce good sounds, just like playing a real flute. The range of notes producing a clear tonality is from `:g1` (MIDI 31) to `:b5` (MIDI 83), approximately." + end + + def arg_defaults + { + :note => 40, + :note_slide => 0, + :amp => 0.4, + :amp_slide => 0, + :amp_slide_shape => 1, + :amp_slide_curve => 0, + :pan => 0, + :pan_slide => 0, + :pan_slide_shape => 1, + :pan_slide_curve => 0, + + :attack => 0, + :decay => 0, + :sustain => 0, + :release => 1, + :attack_level => 1, + :decay_level => :sustain_level, + :sustain_level => 1, + + :noise_attack => 0.06, + :noise_decay => 0.2, + :noise_sustain => 0, + :noise_release => 0.2, + :noise_attack_level => 0.99, + :noise_decay_level => :noise_sustain_level, + :noise_sustain_level => 0.9, + + :vibrato_attack => 0.5, + :vibrato_decay => 0.5, + :vibrato_sustain => 0, + :vibrato_release => 0.5, + :vibrato_attack_level => 0, + :vibrato_decay_level => :vibrato_sustain_level, + :vibrato_sustain_level => 1, + + :ibreath => 0.13, + :ibreath_slide => 0, + :ibreath_slide_shape => 1, + :ibreath_slide_curve => 0, + + :ifeedbk1 => 0.5, + :ifeedbk1_slide => 0, + :ifeedbk1_slide_shape => 1, + :ifeedbk1_slide_curve => 0, + + :ifeedbk2 => 0.57, + :ifeedbk2_slide => 0, + :ifeedbk2_slide_shape => 1, + :ifeedbk2_slide_curve => 0, + } + end + + def specific_arg_info + { + :noise_attack => + { + :doc => "Attack time for noise level. Amount of time (in beats) for sound to reach full noise value.", + :validations => [v_positive(:noise_attack)], + :modulatable => false, + :bpm_scale => true + }, + :noise_decay => + { + :doc => "Decay time for noise level. Amount of time (in beats) for sound to move from full noise value (noise attack level) to the noise sustain level.", + :validations => [v_positive(:noise_decay)], + :modulatable => false, + :bpm_scale => true + }, + :noise_sustain => + { + :doc => "Amount of time (in beats) for noise value to remain at sustain level.", + :validations => [v_positive(:noise_sustain)], + :modulatable => false, + :bpm_scale => true + }, + :noise_release => + { + :doc => "Amount of time (in beats) for sound to move from noise sustain value to noise zero.", + :validations => [v_positive(:noise_release)], + :modulatable => false, + :bpm_scale => true + }, + :noise_attack_level => + { + :doc => "The peak noise (value of noise at peak of attack).", + :validations => [v_between_inclusive(:noise_attack_level, 0, 1)], + :modulatable => false + }, + :noise_decay_level => + { + :doc => "The level of noise after the decay phase.", + :validations => [v_between_inclusive(:noise_decay_level, 0, 1)], + :modulatable => false + }, + :noise_sustain_level => + { + :doc => "The sustain noise level (value of noise at sustain time).", + :validations => [v_between_inclusive(:noise_sustain_level, 0, 1)], + :modulatable => false + }, + :vibrato_attack => + { + :doc => "Attack time for vibrato level. Amount of time (in beats) for sound to reach full vibrato value.", + :validations => [v_positive(:vibrato_attack)], + :modulatable => false, + :bpm_scale => true + }, + :vibrato_decay => + { + :doc => "Decay time for vibrato level. Amount of time (in beats) for sound to move from full vibrato value (vibrato attack level) to the vibrato sustain level.", + :validations => [v_positive(:vibrato_decay)], + :modulatable => false, + :bpm_scale => true + }, + :vibrato_sustain => + { + :doc => "Amount of time (in beats) for vibrato value to remain at sustain level.", + :validations => [v_positive(:vibrato_sustain)], + :modulatable => false, + :bpm_scale => true + }, + :vibrato_release => + { + :doc => "Amount of time (in beats) for sound to move from vibrato sustain value to vibrato zero.", + :validations => [v_positive(:vibrato_release)], + :modulatable => false, + :bpm_scale => true + }, + :vibrato_attack_level => + { + :doc => "The peak vibrato (value of vibrato at peak of attack).", + :validations => [v_between_inclusive(:vibrato_attack_level, 0, 1)], + :modulatable => false + }, + :vibrato_decay_level => + { + :doc => "The level of vibrato after the decay phase.", + :validations => [v_between_inclusive(:vibrato_decay_level, 0, 1)], + :modulatable => false + }, + :vibrato_sustain_level => + { + :doc => "The sustain vibrato level (value of vibrato at sustain time).", + :validations => [v_between_inclusive(:vibrato_sustain_level, 0, 1)], + :modulatable => false + }, + :ibreath => + { + :doc => "Determines how strongly the airflow can be appreciated.", + :validations => [v_between_inclusive(:ibreath, 0, 1)], + :modulatable => true + }, + :ifeedbk1 => + { + :doc => "`ifeedbk1` and `ifeedbk2` together control the amount of feedback that determines tonality and timbre. If both are set to zero, then there is no feedback at all, resulting in just noise. Driving them up towards 1 results in a rougher timbre. Both values together should not exceed 1.1. If they do `ifeedbk2` is clipped.", + :validations => [v_between_inclusive(:ifeedbk1, 0, 1)], + :modulatable => true + }, + :ifeedbk2 => + { + :doc => "`ifeedbk1` and `ifeedbk2` together control the amount of feedback that determines tonality and timbre. If both are set to zero, then there is no feedback at all, resulting in just noise. Driving them up towards 1 results in a rougher timbre. Both values together should not exceed 1.1. If they do `ifeedbk2` is clipped.", + :validations => [v_between_inclusive(:ifeedbk2, 0, 1)], + :modulatable => true + }, + } + end + end + class StudioInfo < SonicPiSynth def user_facing? false @@ -9644,6 +9835,7 @@ class BaseInfo :sc808_open_hihat => SC808OpenHihat.new, :sc808_cymbal => SC808OpenCymbal.new, :gabberkick => Gabberkick.new, + :flute => Flute.new, :sound_in => SoundIn.new, :sound_in_stereo => SoundInStereo.new, :noise => Noise.new, diff --git a/etc/synthdefs/compiled/gated/sonic-pi-flute_gated.scsyndef b/etc/synthdefs/compiled/gated/sonic-pi-flute_gated.scsyndef new file mode 100644 index 0000000000000000000000000000000000000000..b45c242f9846f892a3a8687b9bb151f617378d99 GIT binary patch literal 6462 zcmcIo%W@M(6zv(wl5JU%pS+C01`HT*2sjA|3AVvtAQ$4aMru!w6s2;jrtkE(xprKVK`&7t76tpL^mp`~tZ~=wVBHjJ^DW^uofz(%P%nOFzAOy-c>{WqKH6*IrTm z1!JqfEVJvwjNQ93$(G1|^@q{w&A;e{v5PmRx#qF(wn%ND5xZ5)I()F<`CaO3&6htf zQXb@D&`aOfe&xPmu?Su~5T#YS#lB;|ar++1z>nCFrI9~>bX$%2=JpPZv6%O~Mmjz~TDR}Z>Y8qzXtAl*$ym*+Q0x8y z(z^9PR@Zdv!4{ipol1C(hL?Xv0fzbte%{-qw{)`JtT((8!e)}&e%bfxNHjYnz`3%& z;D@RDY@H`-QD%mnht9MpbmY)8p+X9q^3lP!Z3nh?gni zoQil^M?9w*N|TOw-gKyl7vk7MDB66o+HB;unss`AgPwYFrCMohSIZ7nAQ$N~`gf9g zRPnZV*K3b%`xQhH58ta0(23vKczh{va2qee8}^aU<^G`?rS`}$J8Xc;7yy5hK!-7K zyJKen3v(hE5&&oh3$Py`k`WC|Y|^KjZ=k!#5t{Z1UW!7E%czYUV01Ac4~;n}!U?E+ zA9WJ9Dm!u-4wF&P!#giCbX2BBz2C4WWIZW0pAm{f2MU)V!w+LhxOkZk@xw7HF&Wb@ z>(b4#rr$Qn@rX%IBbsfTF~%7H6tj(?FM@#&M-&5}?oJH-5e#EUF^XZln?4LgFdXQ_ zaIlS`!>4dacQ$m{VpS?RBwr5qzHIg6NmkpkQ5BfR5BoFB(0H_pg3@0KOP9i`Q!>Mixtu9Qo)2e?g( ztB0^NhI<)v+jc?#1PJ8<03?$r+JW3cg~Q@S3Bu8a zU46VVGeqJdB8!Y+h*@S7hwN1VY+n}g<^V~Y?lllHx&|haD=2J?=*l7hk}>mN7?%-< zL@@R^m+E45L&RcP=rQntf zJOLfvk5#z3?1#GF+WMi+V;lnzb-hmY+K*Wghux9-1ax>m?!wh&KV}S^IUh5KMZ)Pw z^Am~mDeFp>0kJE5NDt+BzgfOsC}4>PTg6*&k=zqXZfrfy@p&2KzSL)-!^h?UTt=Vw z)qx?IHE`zGs43xRDB<~0N&0N3vBCbbPLFjvP^$`G!3GERI5PMP0rF5N{uA9YGMj)H zJ>FNBVde~+IWqjrCoDhONvjFE&}n3_Kt2YE4G13}5z>!Pu>tYneFA_?wJ-ca{3#$L zJQnGXP$6ikvR=<)$>O1HTt*p`nz^B?`g61HXyo{14?!#V*~156PBN`Q9ut7N%*bA z21JhE07bHe!Ulvdk3!~{b(3q=yjQ+kk(1mq(UuAJ(VRprK|X(O;-+8rc2^K3*x`yC z82k@iK;3Qq2Pt61z32(|Tke49BL|Rv%7LNlPo?)6AS5LeZftF16V={M82ZDbtYqI*C(Ek7s@e4!% literal 0 HcmV?d00001 diff --git a/etc/synthdefs/compiled/sonic-pi-flute.scsyndef b/etc/synthdefs/compiled/sonic-pi-flute.scsyndef new file mode 100644 index 0000000000000000000000000000000000000000..4facec4b25874b6134b988e1c890a8ed4e68c94a GIT binary patch literal 6442 zcmcIoNmCm~6z&-b2@sN4y3A1u-M%BMYr1)&!KPX#V->GNtvkC&>(*UaUDK_* z8*Hj|D&f^?UhWwM=;_$;bKX9^rIXcqwdNHNHj~`-i@sMyqS+n+-YWWgevu5lDc(fa z`_i(Zw)FRu%7v=GrCA12!l1YY)1n?Q4oOSEd#t}Ph>m@{uOXDSo)~BfsO={+dxh;? zuT~CnIh9r>mCbNgS%Q2{Yg>@f$e_X*BXVXyIhsf2?2xjXBQ%<%w0y7jl*V%`E#LJ# zjt?|h^jw}Z>}o#GcNX%#zq9@9DutWKsEUp^IoNE{0WX*i6>&;MyhahHRm5vL;*4r2 zO*-O5)1e|>N|x)jt?hc10$!%4np`TEYP;p4Llwvs`g;DAq&7?5?*3Y3^R8b)5b^Nc z3Gtlxz4gae0|&S9BD`S-`CRVrxm9S6470uDd-5{(j)DiQp1=ME?%ZX{BZP3Ovdy#!~rC0`fZaOkC@~%BH6|n zW1Im%G20kAA{h8^L^1H`ZpF|U!7zXnqZkH{(ub}HhGVT5jyEy1_!Lg)&W0{qtV$&( z5Fe%MvoL#AKWfq@(|{kEA?Tf$itzzH3$ z(;_S{&&v1F|9`m7NUBq2cgQ{>eNe+8e%MD*keoLCs<*<;MohnLxYRa;kCrEA`&h>! z7WybyZ48J!27pS@#xNeia1H^Y7|tJsVIqQIvK7OHqcBWHFif{%m^li=bOghtRt&h8 zw48^T2!=OWF}&Ht(7i#o{i6TK6F((*V=7Xs%S**V%I{st>!&R4rqr zo%lxm`78jzy(EdYB<~8v;?Rw|3l31x}-9X4_4ooE1QP>#Km3aUpW9GjwE+P<#U~F?P)y3+Th{dwd;d8kJ zmoZ0&>cEhwHLB*kaW1)^qZs4&X?o|^X3|Ng&3>p$!5tZR2s*qU%WxgBAL^cJ>xVjz zaSTAz^*YsVKPE&Rc2DYY=gwv7cClcxN)|D&+VpsT(9?J1S zy?AqH2TMHID&B&N8Vy@gZ*WV9_x0XRu#U2bq;KEWbnrUV`~4Y-URAF2aGGHu|@k>O`P;rP)` zT20WURwIK2@-aQuk%5no2O1H zTt?}Znz^B?`g61HXypS1(fVe#s<{ACM-W=qktYFlJHxJ4Tv1S0g7ZBg$)Q_4u#Ay z>n2yrIj^`$m(}O{{wNe5vKqE literal 0 HcmV?d00001 diff --git a/etc/synthdefs/designs/supercollider/flute.scd b/etc/synthdefs/designs/supercollider/flute.scd new file mode 100644 index 0000000000..a5238f8b77 --- /dev/null +++ b/etc/synthdefs/designs/supercollider/flute.scd @@ -0,0 +1,114 @@ +// Copyright notices + +// Perry Cook's Slide Flute: A Simple Flute Physical Model +// https://ccrma.stanford.edu/software/clm/compmus/clm-tutorials/pm.html#s-f + +// Source for SuperCollider adapation taken from: +// https://github.com/everythingwillbetakenaway/Synthdefs/blob/master/flute.scd + +// Originally found at http://ecmc.rochester.edu/ecmc/docs/supercollider/scbook/Ch21_Interface_Investigations/ixi%20SC%20tutorial/ixi_SC_tutorial_10.html +//by Wilson, Cottle and Collins +//also available at Bruno Ruviaro Collection https://github.com/brunoruviaro/SynthDefs-for-Patterns/blob/master/flute.scd + +( +SynthDef('sonic-pi-flute', {| + note = 40, note_slide = 0, note_slide_shape = 1, note_slide_curve = 0, + amp = 0.4, amp_slide = 0, amp_slide_shape = 1, amp_slide_curve = 0, + pan = 0, pan_slide = 0, pan_slide_shape = 1, pan_slide_curve = 0, + attack = 0, decay = 0, sustain = 0, release = 1, + attack_level = 1, decay_level = -1, sustain_level = 1, + noise_attack = 0.06, noise_decay = 0.2, noise_sustain = 0, noise_release = 0.2, + noise_attack_level = 0.99, noise_decay_level = -1, noise_sustain_level = 0.9, + vibrato_attack = 0.5, vibrato_decay = 0.5, vibrato_sustain = 0, vibrato_release = 0.5, + vibrato_attack_level = 0, vibrato_decay_level = -1, vibrato_sustain_level = 1, + ibreath = 0.13, ibreath_slide = 0, ibreath_slide_shape = 1, ibreath_slide_curve = 0, + ifeedbk1 = 0.5, ifeedbk1_slide = 0, ifeedbk1_slide_shape = 1, ifeedbk1_slide_curve = 0, + ifeedbk2 = 0.57, ifeedbk2_slide = 0, ifeedbk2_slide_shape = 1, ifeedbk2_slide_curve = 0, + out_bus = 0| + + var kenv1, kenv2, kenvibr, kvibr, sr, cr, block; + var poly, signalOut; + var aflow1, asum1, asum2, afqc, atemp1, ax, apoly, asum3, avalue, atemp2, aflute1; + var ifeedbk2_clipped, fdbckArray; + + sr = SampleRate.ir; + cr = ControlRate.ir; + block = cr.reciprocal; + + note = note.midicps; + note = note.varlag(note_slide, note_slide_curve, note_slide_shape); + decay_level = Select.kr(decay_level < 0, [decay_level, sustain_level]); + amp = amp.varlag(amp_slide, amp_slide_curve, amp_slide_shape); + pan = pan.varlag(pan_slide, pan_slide_curve, pan_slide_shape); + + decay_level = Select.kr(decay_level < 0, [decay_level, sustain_level]); + noise_decay_level = Select.kr(noise_decay_level < 0, [noise_decay_level, noise_sustain_level]); + vibrato_decay_level = Select.kr(vibrato_decay_level < 0, [vibrato_decay_level, vibrato_sustain_level]); + + ibreath = ibreath.varlag(ibreath_slide, ibreath_slide_curve, ibreath_slide_shape); + ifeedbk1 = ifeedbk1.varlag(ifeedbk1_slide, ifeedbk1_slide_curve, ifeedbk1_slide_shape); + ifeedbk2 = ifeedbk2.varlag(ifeedbk2_slide, ifeedbk2_slide_curve, ifeedbk2_slide_shape); + + ibreath = 0.7 * ibreath; + ifeedbk1 = 0.8 * ifeedbk1; + ifeedbk2 = 0.7 * ifeedbk2; + ifeedbk2_clipped = Select.kr((ifeedbk1 + ifeedbk2) > 1.1, [ifeedbk2, (1.1 - ifeedbk1)]); + + // Noise envelope + kenv1 = EnvGen.kr(Env.new( + [0, noise_attack_level, noise_decay_level, noise_sustain_level, 0], + [noise_attack,noise_decay,noise_sustain,noise_release], + \linear + ) + ); + + // Amp envelope + kenv2 = EnvGen.kr(Env.new( + [0, attack_level, decay_level, sustain_level, 0], + [attack,decay,sustain,release], + \sine + ), + doneAction: 2 + ); + + // vibrato envelope + kenvibr = EnvGen.kr(Env.new( + [0, vibrato_attack_level, vibrato_decay_level, vibrato_sustain_level, 0], + [vibrato_attack,vibrato_decay,vibrato_sustain,vibrato_release], + \linear + ) + ); + + // Create air flow and vibrato + // The actual tone is created by feeding back this noise signal (see LocalOut below). + // Hence, kenv1 determines if one hears anything. + aflow1 = LFClipNoise.ar( sr, kenv1 ); + kvibr = SinOsc.ar( 5, 0, 0.1 * kenvibr ); + + asum1 = ( ibreath * aflow1 ) + kenv1 + kvibr; + afqc = note.reciprocal - ( asum1/20000 ) - ( 9/sr ) + ( note/12000000 ) - block; + + fdbckArray = LocalIn.ar( 1 ); + + aflute1 = fdbckArray; + asum2 = asum1 + ( aflute1 * ifeedbk1 ); + + ax = DelayC.ar( asum2, note.reciprocal - block * 0.5, afqc * 0.5 - ( asum1/note/cr ) + 0.001 ); + + apoly = ax - ( ax.cubed ); + asum3 = apoly + ( aflute1 * ifeedbk2_clipped ); + avalue = LPF.ar( asum3, 2000 ); + + aflute1 = DelayC.ar( avalue, note.reciprocal - block, afqc ); + + fdbckArray = [ aflute1 ]; + + LocalOut.ar( fdbckArray ); + + signalOut = avalue; + + signalOut = Pan2.ar(Mix(signalOut) * kenv2, pan); + + Out.ar( out_bus, signalOut * amp ); +}).writeDefFile("/Users/sam/Development/RPi/sonic-pi/etc/synthdefs/compiled/"); +) \ No newline at end of file diff --git a/etc/synthdefs/designs/supercollider/gated/flute_gated.scd b/etc/synthdefs/designs/supercollider/gated/flute_gated.scd new file mode 100644 index 0000000000..ebe18e6b2d --- /dev/null +++ b/etc/synthdefs/designs/supercollider/gated/flute_gated.scd @@ -0,0 +1,121 @@ +// Copyright notices + +// Perry Cook's Slide Flute: A Simple Flute Physical Model +// https://ccrma.stanford.edu/software/clm/compmus/clm-tutorials/pm.html#s-f + +// Source for SuperCollider adapation taken from: +// https://github.com/everythingwillbetakenaway/Synthdefs/blob/master/flute.scd + +// Originally found at http://ecmc.rochester.edu/ecmc/docs/supercollider/scbook/Ch21_Interface_Investigations/ixi%20SC%20tutorial/ixi_SC_tutorial_10.html +//by Wilson, Cottle and Collins +//also available at Bruno Ruviaro Collection https://github.com/brunoruviaro/SynthDefs-for-Patterns/blob/master/flute.scd + +( +SynthDef('sonic-pi-flute_gated', {| + note = 40, note_slide = 0, note_slide_shape = 1, note_slide_curve = 0, + amp = 0.4, amp_slide = 0, amp_slide_shape = 1, amp_slide_curve = 0, + pan = 0, pan_slide = 0, pan_slide_shape = 1, pan_slide_curve = 0, + attack = 0, decay = 0, sustain = 0, release = 1, + attack_level = 1, decay_level = -1, sustain_level = 1, + noise_attack = 0.06, noise_decay = 0.2, noise_sustain = 0, noise_release = 0.2, + noise_attack_level = 0.99, noise_decay_level = -1, noise_sustain_level = 0.9, + vibrato_attack = 0.5, vibrato_decay = 0.5, vibrato_sustain = 0, vibrato_release = 0.5, + vibrato_attack_level = 0, vibrato_decay_level = -1, vibrato_sustain_level = 1, + ibreath = 0.13, ibreath_slide = 0, ibreath_slide_shape = 1, ibreath_slide_curve = 0, + ifeedbk1 = 0.5, ifeedbk1_slide = 0, ifeedbk1_slide_shape = 1, ifeedbk1_slide_curve = 0, + ifeedbk2 = 0.57, ifeedbk2_slide = 0, ifeedbk2_slide_shape = 1, ifeedbk2_slide_curve = 0, + gate = 1, + out_bus = 0| + + var kenv1, kenv2, kenvibr, kvibr, sr, cr, block; + var poly, signalOut; + var aflow1, asum1, asum2, afqc, atemp1, ax, apoly, asum3, avalue, atemp2, aflute1; + var ifeedbk2_clipped, fdbckArray; + + sr = SampleRate.ir; + cr = ControlRate.ir; + block = cr.reciprocal; + + note = note.midicps; + note = note.varlag(note_slide, note_slide_curve, note_slide_shape); + decay_level = Select.kr(decay_level < 0, [decay_level, sustain_level]); + amp = amp.varlag(amp_slide, amp_slide_curve, amp_slide_shape); + pan = pan.varlag(pan_slide, pan_slide_curve, pan_slide_shape); + + decay_level = Select.kr(decay_level < 0, [decay_level, sustain_level]); + noise_decay_level = Select.kr(noise_decay_level < 0, [noise_decay_level, noise_sustain_level]); + vibrato_decay_level = Select.kr(vibrato_decay_level < 0, [vibrato_decay_level, vibrato_sustain_level]); + + ibreath = ibreath.varlag(ibreath_slide, ibreath_slide_curve, ibreath_slide_shape); + ifeedbk1 = ifeedbk1.varlag(ifeedbk1_slide, ifeedbk1_slide_curve, ifeedbk1_slide_shape); + ifeedbk2 = ifeedbk2.varlag(ifeedbk2_slide, ifeedbk2_slide_curve, ifeedbk2_slide_shape); + + ibreath = 0.7 * ibreath; + ifeedbk1 = 0.8 * ifeedbk1; + ifeedbk2 = 0.7 * ifeedbk2; + ifeedbk2_clipped = Select.kr((ifeedbk1 + ifeedbk2) > 1.1, [ifeedbk2, (1.1 - ifeedbk1)]); + + // Noise envelope + kenv1 = EnvGen.kr(Env.new( + [0, noise_attack_level, noise_decay_level, noise_sustain_level, 0], + [noise_attack,noise_decay,noise_sustain,noise_release], + \linear, + 3 + ), + gate + ); + + // Amp envelope + kenv2 = EnvGen.kr(Env.new( + [0, attack_level, decay_level, sustain_level, 0], + [attack,decay,sustain,release], + \sine, + 3 + ), + gate, + doneAction: 2 + ); + + // vibrato envelope + kenvibr = EnvGen.kr(Env.new( + [0, vibrato_attack_level, vibrato_decay_level, vibrato_sustain_level, 0], + [vibrato_attack,vibrato_decay,vibrato_sustain,vibrato_release], + \linear, + 3 + ), + gate + ); + + // Create air flow and vibrato + // The actual tone is created by feeding back this noise signal (see LocalOut below). + // Hence, kenv1 determines if one hears anything. + aflow1 = LFClipNoise.ar( sr, kenv1 ); + kvibr = SinOsc.ar( 5, 0, 0.1 * kenvibr ); + + asum1 = ( ibreath * aflow1 ) + kenv1 + kvibr; + afqc = note.reciprocal - ( asum1/20000 ) - ( 9/sr ) + ( note/12000000 ) - block; + + fdbckArray = LocalIn.ar( 1 ); + + aflute1 = fdbckArray; + asum2 = asum1 + ( aflute1 * ifeedbk1 ); + + ax = DelayC.ar( asum2, note.reciprocal - block * 0.5, afqc * 0.5 - ( asum1/note/cr ) + 0.001 ); + + apoly = ax - ( ax.cubed ); + asum3 = apoly + ( aflute1 * ifeedbk2_clipped ); + avalue = LPF.ar( asum3, 2000 ); + + aflute1 = DelayC.ar( avalue, note.reciprocal - block, afqc ); + + fdbckArray = [ aflute1 ]; + + LocalOut.ar( fdbckArray ); + + signalOut = avalue; + + signalOut = Pan2.ar(Mix(signalOut) * kenv2, pan); + + Out.ar( out_bus, signalOut * amp ); +}).writeDefFile("/Users/sam/Development/RPi/sonic-pi/etc/synthdefs/compiled/"); +) \ No newline at end of file