diff options
author | IOhannes m zmölnig <zmoelnig@umlautQ.umlaeute.mur.at> | 2016-01-25 11:25:59 +0100 |
---|---|---|
committer | IOhannes m zmölnig <zmoelnig@umlautQ.umlaeute.mur.at> | 2016-01-25 11:25:59 +0100 |
commit | 110d59c341b8c50c04f30d90e85e9b8f6f329a0e (patch) | |
tree | 174afbe2ded41ae03923b93a0c4e6975e3163ad5 /examp.fs | |
parent | e5328e59987b90c4e98959510b810510e384650d (diff) |
Imported Upstream version 16.1
Diffstat (limited to 'examp.fs')
-rw-r--r-- | examp.fs | 4070 |
1 files changed, 2106 insertions, 1964 deletions
@@ -1,235 +1,253 @@ \ examp.fs -- examples from examp.scm|rb -\ Author: Michael Scholz <mi-scholz@users.sourceforge.net> -\ Created: Tue Jul 05 13:09:37 CEST 2005 -\ Changed: Sat Feb 19 17:25:03 CET 2011 - -\ Commentary: +\ Translator/Author: Michael Scholz <mi-scholz@users.sourceforge.net> +\ Created: 05/07/05 13:09:37 +\ Changed: 15/01/29 23:39:32 \ +\ @(#)examp.fs 1.68 1/29/15 + \ With original comments and doc strings from examp.scm. \ -\ all-chans ( -- array-of-lists ) -\ close-sound-extend ( snd -- ) -\ snd-snd ( :optional snd -- snd ) -\ snd-chn ( :optional chn -- chn ) -\ toggle-read-eval-loop ( -- ) -\ make-read-eval-loop ( -- ) -\ remove-read-eval-loop ( -- ) +\ all-chans ( -- array-of-lists ) +\ close-sound-extend ( snd -- ) +\ snd-snd ( :optional snd -- snd ) +\ snd-chn ( :optional chn -- chn ) +\ make-read-eval-loop ( -- ) +\ remove-read-eval-loop ( -- ) +\ toggle-read-eval-loop ( -- ) \ \ from frame.scm -\ insert-vct ( v :optional beg dur snd chn edpos -- samps ) +\ insert-vct ( v :optional beg dur snd chn edpos -- samps ) \ \ examp.(scm|rb) -\ selection-rms ( -- val ) -\ region-rms ( :optional n -- val ) -\ window-samples ( :optional snd chn -- vct ) -\ display-energy ( snd chn -- val ) -\ display-db ( snd chn -- val ) -\ window-rms ( -- val ) -\ fft-peak ( snd chn scaler -- pk ) -\ finfo ( file -- str ) -\ display-correlate ( snd chn y0 y1 -- val ) -\ zoom-spectrum ( snd chn y0 y1 -- val ) -\ superimpose-ffts ( snd chn y0 y1 -- val ) -\ locate-zero ( limit -- samp ) -\ mpg ( mpgfile rawfile -- ) -\ auto-dot ( snd chn y0 y1 -- val ) +\ selection-rms ( -- val ) +\ region-rms ( :optional n -- val ) +\ window-samples ( :optional snd chn -- vct ) +\ display-energy ( snd chn -- val ) +\ display-db ( snd chn -- val ) +\ window-rms ( -- val ) +\ fft-peak ( snd chn scaler -- pk ) +\ finfo ( file -- str ) +\ display-correlate ( snd chn y0 y1 -- val ) +\ zoom-spectrum ( snd chn y0 y1 -- val ) +\ superimpose-ffts ( snd chn y0 y1 -- val ) +\ locate-zero ( limit -- samp ) +\ mpg ( mpgfile rawfile -- ) +\ auto-dot ( snd chn y0 y1 -- val ) \ first-mark-in-window-at-left ( -- ) -\ flash-selected-data ( millisecs -- ) -\ mark-loops ( -- ) +\ flash-selected-data ( millisecs -- ) +\ mark-loops ( -- ) \ -\ do-all-chans ( func :optional origin -- ) -\ update-graphs ( -- ) -\ do-chans ( func :optional origin -- ) -\ do-sound-chans ( func :optional origin -- ) -\ every-sample? ( func -- f ) -\ sort-samples ( nbins -- ary ) -\ place-sound ( mono stereo pan -- res ) -\ fft-edit ( bottom top :optional snd chn -- vct ) -\ fft-squelch ( squelch :optional snd chn -- scl ) -\ fft-cancel ( lo-freq hi-freq :optional snd chn -- vct ) -\ make-ramp ( :optional size -- gen ) -\ ramp ( gen up -- val ) -\ squelch-vowels ( :optional snd chn -- val ) -\ fft-env-data ( fft-env :optional snd chn -- vct ) -\ fft-env-edit ( fft-env :optional snd chn -- vct ) -\ fft-env-interp ( env1 env2 interp :optional snd chn -- vct ) -\ filter-fft ( flt :optional normalize snd chn -- val ) -\ fft-smoother ( cutoff start samps :optional snd chn -- val ) -\ comb-filter ( scaler size -- prc; x self -- res ) -\ comb-chord ( scaler size amp -- prc; x self -- res ) -\ zcomb ( scaler size pm -- prc; x self -- val ) -\ notch-filter ( scaler size -- prc; x self -- val ) -\ formant-filter ( radius frequency -- prc; x self -- val ) -\ formants ( r1 f1 r2 f2 r3 f3 -- prc; x self -- val ) -\ moving-formant ( radius move -- prc; x self -- val ) -\ osc-formants ( radius bases amounts freqs -- prc; x self -- val ) +\ do-all-chans ( func :optional origin -- ) +\ update-graphs ( -- ) +\ do-chans ( func :optional origin -- ) +\ do-sound-chans ( func :optional origin -- ) +\ every-sample? ( func -- f ) +\ sort-samples ( nbins -- ary ) +\ place-sound ( mono stereo pan -- res ) +\ fft-edit ( bottom top :optional snd chn -- vct ) +\ fft-squelch ( squelch :optional snd chn -- scl ) +\ fft-cancel ( lo-freq hi-freq :optional snd chn -- vct ) +\ make-ramp ( :optional size -- gen ) +\ ramp ( gen up -- val ) +\ squelch-vowels ( :optional snd chn -- val ) +\ fft-env-data ( fft-env :optional snd chn -- vct ) +\ fft-env-edit ( fft-env :optional snd chn -- vct ) +\ fft-env-interp ( env1 env2 interp :optional snd chn -- vct ) +\ filter-fft ( flt :optional normalize snd chn -- val ) +\ fft-smoother ( cutoff start samps :optional snd chn -- val ) +\ comb-filter ( scaler size -- prc; x self -- res ) +\ comb-chord ( scaler size amp -- prc; x self -- res ) +\ zcomb ( scaler size pm -- prc; x self -- val ) +\ notch-filter ( scaler size -- prc; x self -- val ) +\ formant-filter ( radius frequency -- prc; x self -- val ) +\ formants ( r1 f1 r2 f2 r3 f3 -- prc; x self -- val ) +\ moving-formant ( radius move -- prc; x self -- val ) +\ osc-formants ( radius bases amounts freqs -- prc; x self -- val ) \ -\ echo ( scaler secs -- prc; y self -- val ) -\ zecho ( scaler secs freq amp -- prc; y self -- val ) -\ flecho ( scaler secs -- prc; y self -- val ) -\ ring-mod ( freq gliss-env -- prc; y self -- val ) -\ am ( freq -- prc; y self -- val ) -\ vibro ( speed depth -- prc; y self -- val ) +\ echo ( scaler secs -- prc; y self -- val ) +\ zecho ( scaler secs freq amp -- prc; y self -- val ) +\ flecho ( scaler secs -- prc; y self -- val ) +\ ring-mod ( freq gliss-env -- prc; y self -- val ) +\ am ( freq -- prc; y self -- val ) +\ vibro ( speed depth -- prc; y self -- val ) \ -\ hello-dentist ( frq amp :optional snd chn -- vct ) -\ fp ( sr osamp osfrq :optional snd chn -- vct ) -\ compand ( -- prc; y self -- val ) -\ compand-channel ( :optional beg dur snd chn edpos -- val ) -\ compand-sound ( :optional beg dur snd -- ) -\ expsrc ( rate :optional snd chn -- val ) -\ expsnd ( gr-env :optional snd chn -- vct ) -\ cross-synthesis ( cross-snd amp fftsize r -- prc; y self -- val ) -\ voiced->unvoiced ( amp fftsize r tempo :optional snd chn -- vct ) -\ pulse-voice ( cosines :optional freq amp fftsize r snd chn -- vct ) -\ cnvtest ( snd0 snd1 amp -- mx ) -\ swap-selection-channels ( -- ) -\ make-sound-interp ( start :optional snd chn -- prc; loc self -- val ) -\ sound-interp ( func loc -- val ) -\ env-sound-interp ( envelope :optional time-scale snd chn -- file-name ) -\ granulated-sound-interp ( e :optional tscale grain-len grain-env out-hop snd chn -- file-name ) -\ filtered-env ( e :optional snd chn -- val ) +\ hello-dentist ( frq amp :optional snd chn -- vct ) +\ fp ( sr osamp osfrq :optional snd chn -- vct ) +\ compand ( -- prc; y self -- val ) +\ expsrc ( rate :optional snd chn -- val ) +\ expsnd ( gr-env :optional snd chn -- vct ) +\ cross-synthesis ( cross-snd amp fftsize r -- prc; y self -- val ) +\ voiced->unvoiced ( amp fftsize r tempo :optional snd chn -- vct ) +\ pulse-voice ( cos :optional freq amp fftsize r snd chn -- vct ) +\ cnvtest ( snd0 snd1 amp -- mx ) +\ swap-selection-channels ( -- ) +\ make-sound-interp ( start :optional snd chn -- prc; loc self -- val ) +\ sound-interp ( func loc -- val ) +\ env-sound-interp ( env :optional time-scale snd chn -- vct ) +\ granulated-sound-interp ( e :optional tscl glen genv ohop snd chn -- vct ) +\ filtered-env ( e :optional snd chn -- val ) \ -\ switch-to-buffer ( -- val ) -\ find-click ( loc -- pos ) -\ remove-clicks ( -- ) -\ search-for-click ( -- pos ) -\ zero+ ( -- prc; n self -- val ) -\ next-peak ( -- prc; n self -- val ) -\ find-pitch ( pitch -- prc; y self -- val ) -\ file->vct ( file -- vct ) -\ add-notes ( notes :optional snd chn -- #f ) -\ region-play-list ( data -- ) -\ region-play-sequence ( data -- ) -\ replace-with-selection ( -- ) -\ explode-sf2 ( -- ) +\ find-click ( loc -- pos ) +\ remove-clicks ( -- ) +\ search-for-click ( -- pos ) +\ zero+ ( -- prc; n self -- val ) +\ next-peak ( -- prc; n self -- val ) +\ find-pitch ( pitch -- prc; y self -- val ) +\ file->vct ( file -- vct ) +\ add-notes ( notes :optional snd chn -- #f ) +\ region-play-list ( data -- ) +\ region-play-sequence ( data -- ) +\ replace-with-selection ( -- ) +\ explode-sf2 ( -- ) \ open-next-file-in-directory ( -- f ) \ click-middle-button-to-open-next-file-in-directory ( -- ) -\ chain-dsps ( start dur :optional dsps -- ) +\ chain-dsps ( start dur :optional dsps -- ) \ -\ smooth-channel-via-ptree ( :optional beg dur snd chn edpos -- val ) -\ ring-modulate-channel ( freq :optional beg dur snd chn edpos -- val ) -\ scramble-channels ( new-order -- ) -\ scramble-channel ( silence -- ) -\ reverse-by-blocks ( block-len :optional snd chn -- val ) -\ reverse-within-blocks ( block-len :optional snd chn -- val ) -\ channel-clipped? ( :optional snd chn -- val ) -\ sync-everything ( -- ) +\ scramble-channels ( new-order -- ) +\ scramble-channel ( silence -- ) +\ reverse-by-blocks ( block-len :optional snd chn -- val ) +\ reverse-within-blocks ( block-len :optional snd chn -- val ) +\ channel-clipped? ( :optional snd chn -- val ) +\ sync-everything ( -- ) \ -\ make-moog-filter ( freq Q -- gen ) -\ moog-frequecy@ ( gen -- frq ) -\ moog-frequecy! ( frq gen -- ) -\ moog-filter ( gen sig -- A ) +\ make-moog-filter ( freq Q -- gen ) +\ moog-frequecy@ ( gen -- frq ) +\ moog-frequecy! ( frq gen -- ) +\ moog-filter ( gen sig -- A ) 'snd-nogui provided? [if] - <'> noop alias show-widget \ ( a -- b ) - <'> noop alias hide-widget \ ( a -- b ) - <'> noop alias widget-size \ ( a -- b ) - : set-widget-size ( a b -- c ) drop ; - : window-property ( a b -- c ) drop ; - : set-window-property ( a b c -- d ) 2drop ; + <'> noop alias show-widget ( a -- a ) + <'> noop alias hide-widget ( a -- a ) + <'> noop alias widget-size ( a -- a ) + : set-widget-size ( a b -- a ) drop ; + : window-property ( a b -- a ) drop ; + : set-window-property ( a b c -- a ) 2drop ; [then] \ #( #( snd0 chn0 ) #( snd0 chn1 ) ... ) : all-chans ( -- array-of-lists ) - #() { ary } - sounds each { snd } - snd channels 0 ?do - ary #( snd i ) array-push drop - loop - end-each - ary + nil { snd } + #() ( ary ) + sounds each to snd + snd channels 0 ?do + ( ary ) #( snd i ) array-push + loop + end-each + ( ary ) ; \ 5 == notebook widget : close-sound-extend <{ snd -- }> - main-widgets 5 object-ref if - sounds snd object-index 0 max { idx } - snd close-sound drop - sounds empty? unless - sounds - idx sounds length < if idx else -1 then - object-ref set-selected-sound drop - then - else - snd close-sound drop - then + main-widgets 5 object-ref if + sounds snd object-index 0 max { idx } + snd close-sound drop + sounds empty? unless + sounds + idx sounds length < if + idx + else + -1 + then object-ref set-selected-sound drop + then + else + snd close-sound drop + then ; : snd-snd <{ :optional snd #f -- snd }> - snd integer? if - snd - else - selected-sound integer? if - selected-sound - else - sounds 0 array-ref - then - then + snd sound? if + snd + else + selected-sound to snd + snd sound? if + snd + else + sounds car + then + then ; + : snd-chn <{ :optional chn #f -- chn }> - chn integer? if - chn - else - #f selected-channel integer? if - #f selected-channel - else - 0 - then - then + chn integer? if + chn + else + #f selected-channel to chn + chn integer? if + chn + else + 0 + then + then ; \ === Traditional Forth Read-Eval-Loop for Snd's listener. === + +"ok " constant read-eval-loop-prompt + hide -#f value ficl-stack - -: read-eval-loop-cb <{ line -- result-string }> - #f clear-minibuffer drop - line empty? if - "ok\n" snd-print drop - else - ficl-stack restore-stack - $space snd-print drop - line <'> string-eval #t nil fth-catch false? if - save-stack to ficl-stack - $" ok\n" snd-print drop - else - stack-reset - $" %s in %s" - #( *last-exception* exception-name *last-exception* exception-last-message-ref ) - string-format snd-warning drop - *fth-verbose* if backtrace then - then - then - reset-listener-cursor drop - #t -; -: __toggle-read-eval-loop { key -- } - key 'on equal? - key #t equal? || if \ on - read-hook <'> read-eval-loop-cb hook-member? unless - read-hook <'> read-eval-loop-cb add-hook! - then - else - key 'off equal? - key #f equal? || if \ off - read-hook <'> read-eval-loop-cb remove-hook! - else \ toggle - read-hook <'> read-eval-loop-cb hook-member? if - read-hook <'> read-eval-loop-cb remove-hook! - else - read-hook <'> read-eval-loop-cb add-hook! - then - then - then - #f to ficl-stack - stack-reset - reset-listener-cursor drop +#f value rel-ficl-stack +#f value rel-old-listener-prompt +#f value rel-old-hooks + +: rel-cb <{ text -- flag }> + rel-ficl-stack restore-stack + $space snd-print drop + text string-eval save-stack to rel-ficl-stack + $cr snd-print drop + read-eval-loop-prompt snd-print drop + \ reset-listener-cursor returns #f + reset-listener-cursor not +; + +: rel-add ( -- ) + listener-prompt to rel-old-listener-prompt + read-eval-loop-prompt set-listener-prompt drop + read-hook hook->array to rel-old-hooks + read-hook reset-hook! + read-hook <'> rel-cb add-hook! +; + +: rel-remove ( -- ) + read-hook <'> rel-cb remove-hook! + nil { prc } + rel-old-hooks empty? unless + rel-old-hooks each to prc + read-hook prc add-hook! + end-each + then + rel-old-listener-prompt set-listener-prompt drop +; + +: rel-toggle ( key -- ) + ( key ) case + 'on of + read-hook <'> rel-cb hook-member? unless + rel-add + then + endof + 'off of + rel-remove + endof + 'toggle of + read-hook <'> rel-cb hook-member? if + rel-remove + else + rel-add + then + endof + endcase + #f to rel-ficl-stack + stack-reset + reset-listener-cursor drop ; set-current -: toggle-read-eval-loop ( -- ) nil __toggle-read-eval-loop ; -: make-read-eval-loop ( -- ) #t __toggle-read-eval-loop ; -: remove-read-eval-loop ( -- ) #f __toggle-read-eval-loop ; + +<'> noop alias ok + +: make-read-eval-loop ( -- ) 'on rel-toggle ; +: remove-read-eval-loop ( -- ) 'off rel-toggle ; +: toggle-read-eval-loop ( -- ) 'toggle rel-toggle ; previous require clm @@ -239,116 +257,138 @@ require rgb \ === from frame.scm \ : insert-vct <{ v :optional beg 0 dur #f snd #f chn #f edpos #f -- samps }> - doc" Inserts vct V's data into sound SND at BEG." - v vct? v 1 $" a vct" assert-type - dur v vct-length || { len } - beg len v snd chn edpos #f $" %S %s %s %s" #( v beg dur get-func-name ) format insert-samples + doc" Insert vct V's data into sound SND at BEG." + v vct? v 1 "a vct" assert-type + dur v vct-length || { len } + beg len v snd chn edpos #f "%S %s %s %s" + #( v beg dur get-func-name ) string-format insert-samples ; \ === examp.scm|rb \ \ this mainly involves keeping track of the current sound/channel : selection-rms ( -- val ) - doc" Returns rms of selection data using sample readers." - undef selection? if - selection-position #f #f 1 #f make-sampler { rd } - selection-frames { len } - 0.0 ( sum ) len 0 ?do rd next-sample dup f* f+ ( sum += ... ) loop - len f/ fsqrt - else - 'no-active-selection #( get-func-name ) fth-throw - then + doc" Return rms of selection data using sample readers." + undef selection? unless + 'no-active-selection #( get-func-name ) fth-throw + then + selection-position #f #f 1 #f make-sampler { rd } + selection-framples { len } + 0.0 ( sum ) len 0 ?do + rd next-sample dup f* f+ ( sum += ... ) + loop + len f/ fsqrt ; : region-rms <{ :optional reg 0 -- val }> - doc" Returns rms of region N's data (chan 0)." - reg region? if - reg 0 0 region->vct { data } - data dup dot-product data length f/ fsqrt - else - 'no-such-region #( get-func-name reg ) fth-throw - then + doc" Return rms of region N's data (chan 0)." + reg region? unless + 'no-such-region #( "%s: %s" get-func-name reg ) fth-throw + then + reg 0 0 region->vct { data } + data dup dot-product data length f/ fsqrt ; : window-samples <{ :optional snd #f chn #f -- vct }> - doc" Samples in SND channel CHN in current graph window." - - snd chn left-sample { wl } - snd chn right-sample { wr } - wl wr wl - 1+ snd chn #f channel->vct + doc" Sample in SND channel CHN in current graph window." + snd chn left-sample { wl } + snd chn right-sample { wr } + wl wr wl - 1+ snd chn #f channel->vct ; : display-energy <{ snd chn -- v }> - doc" A lisp-graph-hook function to display the time domain data as energy (squared).\n\ -list-graph-hook <'> display-energy add-hook!" - snd chn undef undef undef make-graph-data dup array? if 1 array-ref then { data } - data if - snd chn left-sample { ls } - snd chn right-sample { rs } - snd srate { sr } - snd chn y-zoom-slider { y-max } - data dup vct-multiply! $" energy" ls sr f/ rs sr f/ 0.0 y-max dup f* snd chn #t graph - else - #f - then + doc" A lisp-graph-hook function to display the time \ +domain data as energy (squared).\n\ +list-graph-hook <'> display-energy add-hook!." + snd chn undef undef undef make-graph-data dup array? if + 1 array-ref + then { data } + data if + snd chn left-sample { ls } + snd chn right-sample { rs } + snd srate { sr } + snd chn y-zoom-slider { y-max } + data data vct-multiply! "energy" ls sr f/ rs sr f/ + 0.0 y-max dup f* snd chn #t undef graph + else + #f + then ; \ lisp-graph-hook <'> display-energy add-hook! hide -: db-calc ( val -- r ) { val } val 0.001 f< if -60.0 else 20.0 val flog10 f* then ; +: db-calc { val -- r } + val 0.001 f< if + -60.0 + else + 20.0 val flog10 f* + then +; set-current + : display-db <{ snd chn -- v }> - doc" A lisp-graph-hook function to display the time domain data in dB.\n\ -list-graph-hook <'> display-db add-hook!" - snd chn undef undef undef make-graph-data dup array? if 1 array-ref then { data } - data if - snd chn left-sample { ls } - snd chn right-sample { rs } - snd srate { sr } - data map - *key* fabs db-calc 60.0 f+ - end-map $" dB" ls sr f/ rs sr f/ 0.0 60.0 snd chn #t graph - else - #f - then + doc" A lisp-graph-hook function to display \ +the time domain data in dB.\n\ +list-graph-hook <'> display-db add-hook!." + snd chn undef undef undef make-graph-data dup array? if + 1 array-ref + then { data } + data if + snd chn left-sample { ls } + snd chn right-sample { rs } + snd srate { sr } + data map + *key* fabs db-calc 60.0 f+ + end-map "dB" ls sr f/ rs sr f/ 0.0 60.0 snd chn #t undef graph + else + #f + then ; previous \ lisp-graph-hook <'> display-db add-hook! : window-rms ( -- val ) - doc" Returns rms of data in currently selected graph window." - #f #f left-sample { ls } - #f #f right-sample { rs } - ls rs ls - 1+ #f #f #f channel->vct { data } - data vct-length { len } - data data len dot-product len f/ fsqrt + doc" Return rms of data in currently selected graph window." + #f #f left-sample { ls } + #f #f right-sample { rs } + ls rs ls - 1+ #f #f #f channel->vct { data } + data vct-length { len } + data dup len dot-product len f/ fsqrt ; : fft-peak <{ snd chn scaler -- pk }> - doc" Returns the peak spectral magnitude" - snd chn transform-graph? - snd chn transform-graph-type graph-once = && if - snd chn #f transform->vct vct-peak f2* snd chn transform-size f/ - object->string snd #f report-in-minibuffer - else - #f - then + doc" print peak spectral magnitude\n\ +Returns the peak spectral magnitude. \ +It is intended for use with after-transform-hook." + snd chn transform-graph? + snd chn transform-graph-type graph-once = && if + snd chn #f transform->vct vct-peak f2* + snd chn transform-size f/ { pk } + pk number->string snd status-report drop + pk + else + #f + then ; \ after-transform-hook <'> fft-peak add-hook! \ ;;; -------- 'info' from extsnd.html using format -------- : finfo ( file -- str ) - doc" Returns description (as a string) of file." - find-file { file } - file false? if 'no-such-file #( get-func-name file ) fth-throw then - $" %s: chans: %d, srate: %d, %s, %s, len: %1.3f" - #( file - file mus-sound-chans - file mus-sound-srate - file mus-sound-header-type mus-header-type-name - file mus-sound-data-format mus-data-format-name - file mus-sound-samples file mus-sound-chans file mus-sound-srate f* f/ ) string-format + doc" Return description (as a string) of file." + find-file { file } + file unless + 'no-such-file #( "%s: %S" get-func-name file ) fth-throw + then + "%s: chans: %d, srate: %d, %s, %s, len: %1.3f" + #( file + file mus-sound-chans + file mus-sound-srate + file mus-sound-header-type mus-header-type-name + file mus-sound-sample-type mus-sample-type-name + file mus-sound-samples + file mus-sound-chans + file mus-sound-srate f* f/ ) string-format ; \ ;;; -------- Correlation -------- @@ -356,41 +396,43 @@ previous \ ;;; correlation of channels in a stereo sound : display-correlate <{ snd chn y0 y1 -- val }> - doc" Returns the correlation of SND's 2 channels (intended for use with graph-hook). \ + doc" Return the correlation of SND's 2 channels (intended \ +for use with graph-hook). \ y0 and y1 are ignored." - snd channels 2 = if - snd 0 #f frames 1 > - snd 1 #f frames 1 > && if - snd chn left-sample { ls } - snd chn right-sample { rs } - rs ls - 1+ { ilen } - ilen flog 2.0 flog f/ fround->s { pow2 } - 2.0 pow2 f** fround->s { fftlen } - fftlen 2/ { fftlen2 } - fftlen 1/f { fftscale } - ls fftlen snd 0 #f channel->vct { rl1 } - ls fftlen snd 1 #f channel->vct { rl2 } - fftlen 0.0 make-vct { im1 } - fftlen 0.0 make-vct { im2 } - rl1 im1 1 fft drop - rl2 im2 1 fft drop - rl1 vct-copy { tmprl } - im1 vct-copy { tmpim } - fftlen2 0.0 make-vct { data3 } - tmprl rl2 vct-multiply! drop - tmpim im2 vct-multiply! drop - im2 rl1 vct-multiply! drop - rl2 im1 vct-multiply! drop - tmprl tmpim vct-add! drop - im2 rl2 vct-subtract! drop - tmprl im2 -1 fft drop - data3 tmprl vct-add! drop - data3 fftscale vct-scale! drop - data3 $" lag time" 0 fftlen2 undef undef snd chn undef undef graph - then - else - $" %s wants stereo input" #( get-func-name ) string-format snd #f report-in-minibuffer - then + snd channels 2 = if + snd 0 #f framples 1 > + snd 1 #f framples 1 > && if + snd chn left-sample { ls } + snd chn right-sample { rs } + rs ls - 1+ { ilen } + ilen flog 2.0 flog f/ fround->s { pow2 } + 2.0 pow2 f** fround->s { fftlen } + fftlen 2/ { fftlen2 } + fftlen 1/f { fftscale } + ls fftlen snd 0 #f channel->vct { rl1 } + ls fftlen snd 1 #f channel->vct { rl2 } + fftlen 0.0 make-vct { im1 } + fftlen 0.0 make-vct { im2 } + rl1 im1 1 fft drop + rl2 im2 1 fft drop + rl1 vct-copy { tmprl } + im1 vct-copy { tmpim } + fftlen2 0.0 make-vct { data3 } + tmprl rl2 vct-multiply! drop + tmpim im2 vct-multiply! drop + im2 rl1 vct-multiply! drop + rl2 im1 vct-multiply! drop + tmprl tmpim 0 vct-add! drop + im2 rl2 vct-subtract! drop + tmprl im2 -1 fft drop + data3 tmprl 0 vct-add! drop + data3 fftscale vct-scale! drop + data3 "lag time" 0 fftlen2 + undef undef snd chn #t undef graph + then + else + get-func-name " wants stereo input" $+ snd status-report + then ; \ graph-hook <'> display-correlate add-hook! @@ -399,151 +441,206 @@ y0 and y1 are ignored." \ ;;; also zoom spectrum based on y-axis zoom slider : zoom-spectrum <{ snd chn y0 y1 -- val }> - doc" Sets the transform size to correspond to the time-domain window size (use with graph-hook)." - snd chn transform-graph? - snd chn transform-graph-type graph-once = && if - 2.0 snd chn right-sample snd chn left-sample f- flog 2.0 flog f/ fceil f** fround->s - snd chn set-transform-size drop - snd chn y-zoom-slider snd chn set-spectrum-end drop - then - #f + doc" Set the transform size to correspond to the \ +time-domain window size (use with graph-hook)." + snd chn transform-graph? + snd chn transform-graph-type graph-once = && if + 2.0 snd chn right-sample snd chn left-sample f- + flog 2.0 flog f/ fceil f** fround->s { val } + val 0> if + val snd chn set-transform-size drop + then + snd chn y-zoom-slider snd chn set-spectrum-end drop + then + #f ; \ graph-hook <'> zoom-spectrum add-hook! \ ;;; -------- superimpose spectra of sycn'd sounds : superimpose-ffts <{ snd chn y0 y1 -- val }> - doc" Superimposes ffts of multiple (syncd) sounds (use with graph-hook)." - 0 sounds each ( snd ) sync max end-each { maxsync } - 0 sounds each { n } n sync snd sync = if n else maxsync 1+ then min end-each { sndsync } - snd sync 0> snd sndsync = && if - snd chn left-sample { ls } - snd chn right-sample { rs } - rs ls f- flog 2.0 flog f/ fround->s { pow2 } - 2.0 pow2 f** fround->s { fftlen } - pow2 2 > if - nil { ffts } - sounds each { n } - n sync snd sync = n channels chn > && if - ls fftlen n chn #f channel->vct { fdr } - fftlen 0.0 make-vct { fdi } - fftlen 2/ 0.0 make-vct { spectr } - ffts #( spectr fdr fdi #f 2 spectrum 0 vct-add! ) array-append to ffts + doc" Superimpose ffts of multiple (syncd) sounds (use with graph-hook)." + 0 ( maxsync ) + sounds each ( snd ) + sync max + end-each { maxsync } + 0 sounds each { n } + n sync snd sync = if + n + else + maxsync 1+ + then min + end-each { sndsync } + snd chn left-sample { ls } + snd chn right-sample { rs } + snd sync 0> + rs ls > && + snd sndsync = && if + rs ls f- 1.0 fmax 2.0 flog fceil->s { pow2 } + 2.0 pow2 f** floor->s { fftlen } + pow2 2 > if + #() { ffts } + sounds each { n } + n sync snd sync = + n channels chn > && if + ls fftlen n chn #f channel->vct { fdr } + fftlen 0.0 make-vct { fdi } + fftlen 2/ 0.0 make-vct ( spectr ) + fdr fdi #f 2 spectrum + 0 vct-add! { val } + ffts #( val ) array-push drop + then + end-each + ffts "spectra" 0.0 0.5 y0 y1 + snd chn #t undef graph drop + then then - end-each - ffts "spectra" 0.0 0.5 undef undef snd chn undef undef graph drop - then - then - #f + #f ; \ graph-hook <'> superimpose-ffts add-hook! \ ;;; -------- c-g? example (Anders Vinjar) : locate-zero ( limit -- samp ) - doc" Looks for successive samples that sum to less than LIMIT, moving the cursor if successful." - { limit } - #f #f #f cursor { start } - start #f #f 1 #f make-sampler { sf } - sf next-sample fabs { val0 } - sf next-sample fabs { val1 } - begin - sf sampler-at-end? - \ c-g? || - val0 val1 f+ limit f< || not - while - start 1+ to start - val1 to val0 - sf next-sample fabs to val1 - repeat - sf free-sampler drop - start #f #f #f set-cursor + doc" Look for successive samples that sum to less than LIMIT, \ +moving the cursor if successful." + { limit } + #f #f #f cursor { start } + start #f #f 1 #f make-sampler { sf } + sf next-sample fabs { val0 } + sf next-sample fabs { val1 } + begin + sf sampler-at-end? + val0 val1 f+ limit f< || not + while + start 1+ to start + val1 to val0 + sf next-sample fabs to val1 + repeat + sf free-sampler drop + start #f #f #f set-cursor ; \ ;;; -------- translate mpeg input to 16-bit linear and read into Snd \ ;;; -\ ;;; mpg123 with the -s switch sends the 16-bit (mono or stereo) representation of -\ ;;; an mpeg file to stdout. There's also apparently a switch to write 'wave' output. +\ ;;; mpg123 with the -s switch sends the 16-bit (mono or stereo) +\ ;;; representation of an mpeg file to stdout. There's also +\ ;;; apparently a switch to write 'wave' output. : mpg ( mpgfile rawfile -- ) - doc" Converts file from MPEG to raw 16-bit samples using mpg123." - { mpgfile rawfile } - mpgfile io-open-read { io } - io io-getc { b0 } - io io-getc { b1 } - io io-getc { b2 } - io io-getc { b3 } - io io-close - b0 255 <> - b1 0b11100000 and 0b11100000 <> || if - $" %s is not an MPEG file (first 11 bytes: %b %b)" #( mpgfile b0 b1 0b11100000 and ) clm-message - else - b1 0b11000 and 3 rshift { id } - b1 0b110 and 1 rshift { layer } - b2 0b1100 and 2 rshift { srate-index } - b3 0b11000000 and 6 rshift { channel-mode } - id 1 = if - $" odd: %s is using a reserved Version ID" #( mpgfile ) clm-message - then - layer 0= if - $" odd: %s is using a reserved layer description" #( mpgfile ) clm-message - then - channel-mode 3 = if 1 else 2 then { chans } - id 0= if 4 else id 2 = if 2 else 1 then then { mpegnum } - layer 3 = if 1 else layer 2 = if 2 else 3 then then { mpeg-layer } - #( 44100 48000 32000 0 ) srate-index array-ref mpegnum / { srate } - $" %s: %s Hz, %s, MPEG-%s" - #( mpgfile srate chans 1 = if "mono" else "stereo" then mpeg-layer ) clm-message - $" mpg123 -s %s > %s" #( mpgfile rawfile ) string-format file-system if - rawfile chans srate little-endian? if mus-lshort else mus-bshort then open-raw-sound drop - else - $" system in %s: %s" #( get-func-name exit-status ) clm-message - then - then + doc" Convert file from MPEG to raw 16-bit samples using mpg123." + { mpgfile rawfile } + mpgfile io-open-read { io } + io io-getc { b0 } + io io-getc { b1 } + io io-getc { b2 } + io io-getc { b3 } + io io-close + b0 255 <> + b1 0b11100000 and 0b11100000 <> || if + "%s is not an MPEG file (first 11 bytes: %b %b)" + #( mpgfile b0 b1 0b11100000 and ) clm-print + exit + then + b1 0b11000 and 3 rshift { id } + b1 0b110 and 1 rshift { layer } + b2 0b1100 and 2 rshift { srate-index } + b3 0b11000000 and 6 rshift { channel-mode } + id 1 = if + "odd: %s is using a reserved Version ID" + #( mpgfile ) clm-print + then + layer 0= if + "odd: %s is using a reserved layer description" + #( mpgfile ) clm-print + then + channel-mode 3 = if + 1 + else + 2 + then { chans } + id 0= if + 4 + else + id 2 = if + 2 + else + 1 + then + then { mpegnum } + layer 3 = if + 1 + else + layer 2 = if + 2 + else + 3 + then + then { mpeg-layer } + #( 44100 48000 32000 0 ) srate-index array-ref mpegnum / { srate } + "%s: %s Hz, %s, MPEG-%s" + #( mpgfile + srate + chans 1 = if + "mono" + else + "stereo" + then mpeg-layer ) clm-print + "mpg123 -s %s > %s" #( mpgfile rawfile ) string-format file-system if + rawfile chans srate little-endian? if + mus-lshort + else + mus-bshort + then open-raw-sound drop + else + "system in %s: %s" #( get-func-name exit-status ) fth-throw + then ; \ "mpeg.mpg" "mpeg.raw" mpg \ ;;; -------- read ASCII files \ ;;; -\ ;;; these are used by Octave (WaveLab) -- each line has one integer, apparently a signed short. +\ ;;; these are used by Octave (WaveLab) -- each line has one integer, +\ ;;; apparently a signed short. hide : read-ascii-cb { fname snd -- prc; self -- } - 0 proc-create fname , snd , ( thunk ) - does> { self -- } - self @ ( fname ) readlines { in-buffer } - self cell+ @ { snd } - 512 { bufsize } - bufsize 0.0 make-vct { data } - 0 { loc } - 0 { frame } - 32768.0 1/f { short->float } - nil { val } - in-buffer each ( line ) nil string-split each ( str-val ) string->number ( val ) short->float f* - data loc rot vct-set! drop - loc 1+ to loc - loc bufsize = if - data frame bufsize snd 0 vct->channel drop - frame bufsize d+ to frame - 0 to loc - then - end-each - end-each - loc d0> if - data frame loc snd 0 vct->channel drop - then + 0 proc-create ( prc ) + fname , snd , + does> { self -- } + self @ ( fname ) readlines { in-buffer } + self cell+ @ { snd } + 512 { bufsize } + bufsize 0.0 make-vct { data } + 0 { loc } + 0 { frame } + 32768.0 1/f { short->float } + nil { val } + in-buffer each ( line ) + nil string-split each ( str-val ) + string->number ( val ) short->float f* + data loc rot vct-set! drop + loc 1+ to loc + loc bufsize = if + data frame bufsize snd 0 vct->channel drop + frame bufsize d+ to frame + 0 to loc + then + end-each + end-each + loc d0> if + data frame loc snd 0 vct->channel drop + then ; set-current -: read-ascii <{ in-filename :optional - out-filename "test.snd" - out-type mus-next - out-format mus-bshort - out-srate 44100 -- snd }> - doc" tries to read an ASCII sound file" - out-filename out-type out-format out-srate 1 $" created by read-ascii: " in-filename $+ new-sound { snd } - in-filename snd read-ascii-cb as-one-edit drop - snd +: read-ascii + <{ fl :optional sf "test.snd" st mus-next ht mus-bshort sr 44100 -- snd }> + doc" Try to read an ASCII sound file." + "created by %s: %s" #( get-func-name fl ) string-format { com } + sf 1 sr ht st com new-sound { snd } + fl snd read-ascii-cb as-one-edit drop + snd ; previous @@ -553,22 +650,22 @@ previous \ ;;; if many samples are displayed, etc : auto-dot <{ snd chn y0 y1 -- val }> - doc" Sets the dot size depending on the number of samples being displayed (use with graph-hook)." - snd chn right-sample snd chn left-sample - { dots } - dots 100 > if - 1 snd chn set-dot-size drop - else - dots 50 > if - 2 snd chn set-dot-size drop - else - dots 25 > if - 3 snd chn set-dot-size drop - else - 5 snd chn set-dot-size drop - then - then - then - #f + doc" Set the dot size depending on the number of \ +samples being displayed (use with graph-hook)." + snd chn right-sample snd chn left-sample - { dots } + dots 100 > if + 1 + else + dots 50 > if + 2 + else + dots 25 > if + 3 + else + 5 + then + then + then snd chn set-dot-size ; \ graph-hook <'> auto-dot add-hook! @@ -579,154 +676,184 @@ previous \ ;;; the desired left edge has a mark, and the 'm' key (without control) \ ;;; will move the window left edge to that mark. -: first-mark-in-window-at-left ( -- ) - doc" Moves the graph so that the leftmost visible mark is at the left edge." - #f snd-snd { keysnd } - #f snd-chn { keychn } - keysnd keychn left-sample { current-left-sample } - keysnd keychn #f marks { chan-marks } - chan-marks length 0= if - $" no marks" keysnd #f report-in-minibuffer drop - else - #f ( flag ) chan-marks map *key* undef mark-sample end-map each { samp } - samp current-left-sample > if drop ( #f ) samp leave then - end-each { leftmost } - leftmost if - leftmost keysnd keychn set-left-sample drop - keyboard-no-action - else - $" no mark in window" keysnd #f report-in-minibuffer drop - then - then -; -\ "m" 0 lambda: <{ -- val }> first-mark-in-window-at-left ; -\ #f "align window left edge with mark" "align window left edge with mark" bind-key drop +: first-mark-in-window-at-left <{ -- val }> + doc" Move the graph so that the leftmost \ +visible mark is at the left edge." + #f snd-snd { keysnd } + #f snd-chn { keychn } + keysnd keychn left-sample { current-left-sample } + keysnd keychn #f marks { chan-marks } + chan-marks length 0= if + "no marks" status-report + else + #f ( flag ) + chan-marks map + *key* undef mark-sample + end-map each { samp } + samp current-left-sample > if + drop \ drop #f + samp + leave + then + end-each { leftmost } + leftmost if + leftmost keysnd keychn set-left-sample + else + "no mark in window" status-report + then + then drop + keyboard-no-action +; +\ "m" 0 <'> first-mark-in-window-at-left +\ #f "align window left edge with mark" dup bind-key drop \ ;;; -------- flash selected data red and green -defer flash-selected-data ( millisecs -- ) hide -: fsd-cb { millisecs -- prc; self -- val } - 0 proc-create millisecs , ( prc ) - does> { self -- val } - self @ ( millisecs ) flash-selected-data - #f +: fsd-cb { ms xt -- prc; self -- val } + 0 proc-create ( prc ) + ms , xt , + does> { self -- val } + self @ ( ms ) self cell+ @ ( xt ) execute + #f ; user data-red? #t data-red? ! set-current -lambda: ( millisecs -- ) - doc" Causes the selected data to flash red and green." - { millisecs } - selected-sound sound? if - data-red? @ if green else red then set-selected-data-color drop - data-red? @ not data-red? ! - millisecs dup fsd-cb in drop - then -; is flash-selected-data + +: flash-selected-data ( millisecs -- ) + doc" Cause the selected data to flash red and green." + { ms } + selected-sound sound? if + data-red? @ if + green + else + red + then set-selected-data-color drop + data-red? @ not data-red? ! + ms ms running-word fsd-cb in drop + then +; previous \ ;;; -------- use loop info (if any) to set marks at loop points : mark-loops ( -- ) - doc" Places marks at loop points found in the selected sound's header." - #f snd-snd { snd } - #f snd-chn { chn } - snd sound-loop-info - snd file-name mus-sound-loop-info || { loops } - loops nil? if - $" %s has no loop info" #( snd short-file-name ) clm-message - else - loops 0 array-ref 0<> loops 1 array-ref 0<> || if - loops 0 array-ref snd chn undef undef add-mark drop - loops 1 array-ref snd chn undef undef add-mark drop - loops 2 array-ref 0<> loops 3 array-ref 0<> || if - loops 2 array-ref snd chn undef undef add-mark drop - loops 3 array-ref snd chn undef undef add-mark drop - then - then - then + doc" Place marks at loop points found in the selected sound's header." + #f snd-snd { snd } + #f snd-chn { chn } + snd sound-loop-info + snd file-name mus-sound-loop-info || { loops } + loops empty? if + "%s has no loop info" + #( snd short-file-name ) string-format + snd status-report drop + exit + then + loops 0 array-ref 0<> + loops 1 array-ref 0<> || if + loops 0 array-ref snd chn undef undef add-mark drop + loops 1 array-ref snd chn undef undef add-mark drop + loops 2 array-ref 0<> + loops 3 array-ref 0<> || if + loops 2 array-ref snd chn undef undef add-mark drop + loops 3 array-ref snd chn undef undef add-mark drop + then + then ; \ ;;; -------- mapping extensions \ ;;; (map arbitrary single-channel function over various channel collections) : do-all-chans <{ func :optional origin #f -- }> - doc" Applies FUNC to all active channels, using ORIGIN as the edit history indication:\n\ -lambda: <{ val -- val*2 }> val f2* ; \"double all samples\" do-all-chans" - all-chans each { lst } - lst 0 array-ref { snd } - lst 1 array-ref { chn } - func 0 #f snd chn #f origin map-channel drop - end-each + doc" Apply FUNC to all active channels, \ +using ORIGIN as the edit history indication:\n\ +lambda: <{ val -- val*2 }> val f2* ; \"double all samples\" do-all-chans." + all-chans each { lst } + lst 0 array-ref { snd } + lst 1 array-ref { chn } + func 0 #f snd chn #f origin map-channel drop + end-each ; : update-graphs ( -- ) - doc" Updates (redraws) all graphs." - all-chans each { lst } - lst 0 array-ref { snd } - lst 1 array-ref { chn } - snd chn update-time-graph drop - end-each + doc" Update (redraw) all graphs." + all-chans each { lst } + lst 0 array-ref { snd } + lst 1 array-ref { chn } + snd chn update-time-graph drop + end-each ; : do-chans <{ func :optional origin #f -- }> - doc" Applies FUNC to all sync'd channels using ORIGIN as the edit history indication." - #f snd-snd sync { snc } - snc 0> if - all-chans each { lst } - lst 0 array-ref { snd } - lst 1 array-ref { chn } - snd sync snc = if func 0 #f snd chn #f origin map-channel drop then - end-each - else - $" sync not set" snd-warning drop - then + doc" Apply FUNC to all sync'd channels \ +using ORIGIN as the edit history indication." + #f snd-snd sync { snc } + snc 0> if + all-chans each { lst } + lst 0 array-ref { snd } + lst 1 array-ref { chn } + snd sync snc = if + func 0 #f snd chn #f origin map-channel drop + then + end-each + else + "sync not set" snd status-report drop + then ; : do-sound-chans <{ func :optional origin #f -- }> - doc" applies FUNC to all selected channels using ORIGIN as the edit history indication." - selected-sound { snd } - snd sound? if - snd channels 0 ?do func 0 #f snd i ( chn ) #f origin map-channel drop loop - else - $" no selected sound" snd-warning drop - then + doc" Apply FUNC to all selected channels \ +using ORIGIN as the edit history indication." + selected-sound { snd } + snd sound? if + snd channels 0 ?do + func 0 #f snd i ( chn ) #f origin map-channel drop + loop + else + "no selected sound" snd status-report drop + then ; hide : everys-cb { func -- prc; y self -- f } - 1 proc-create func , ( prc ) - does> { y self -- f } - self @ ( func ) #( y ) run-proc not + 1 proc-create ( prc ) + func , + does> { y self -- f } + self @ ( func ) #( y ) run-proc not ; set-current + : every-sample? ( func -- f ) - doc" Returns #t if FUNC is not #f for all samples in the current channel, \ - otherwise it moves the cursor to the first offending sample." - { func } - func everys-cb 0 #f #f #f #f scan-channel { baddy } - baddy if baddy 1 array-ref #f #f #f set-cursor drop then - baddy not + doc" Return #t if FUNC is not #f for all samples in the \ +current channel, otherwise it moves the cursor to the first offending sample." + { func } + func everys-cb 0 #f #f #f #f scan-channel { baddy } + baddy if + baddy 1 array-ref #f #f #f set-cursor drop + then + baddy not ; previous hide : sorts-cb { bins -- prc; y self -- f } - 1 proc-create bins , ( prc ) - does> { y self -- f } - self @ { bins } - y fabs bins length f* fround->s { bin } - bins bin 1 object-set+! - #f + 1 proc-create ( prc ) + bins , + does> { y self -- f } + self @ { bins } + y fabs bins length f* fround->s { bin } + bins bin 1 object-set+! + #f ; set-current + : sort-samples ( nbins -- ary ) - doc" Provides a histogram in BINS bins." - { nbins } - nbins :initial-element 0 make-array { bins } - bins sorts-cb 0 #f #f #f #f scan-channel drop - bins + doc" Provide histogram in BINS bins." + { nbins } + nbins :initial-element 0 make-array { bins } + bins sorts-cb 0 #f #f #f #f scan-channel drop + bins ; previous @@ -734,294 +861,322 @@ previous hide : places1-cb { rd pos -- prc; y self -- val } - 1 proc-create rd , pos , ( prc ) - does> { y self -- val } - self @ { rd } - self cell+ @ { pos } - rd read-sample pos f* y f+ + 1 proc-create ( prc ) + rd , pos , + does> { y self -- val } + self @ { rd } + self cell+ @ { pos } + rd read-sample pos f* y f+ ; + : places0-cb { rd pos -- prc; y self -- val } - 1 proc-create rd , pos , ( prc ) - does> { y self -- val } - self @ { rd } - self cell+ @ { pos } - rd read-sample 1.0 pos f- f* y f+ + 1 proc-create ( prc ) + rd , pos , + does> { y self -- val } + self @ { rd } + self cell+ @ { pos } + rd read-sample 1.0 pos f- f* y f+ ; + : places3-cb { rd en -- prc; y self -- val } - 1 proc-create rd , en , ( prc ) - does> { y self -- val } - self @ { rd } - self cell+ @ { en } - rd read-sample en env f* y f+ + 1 proc-create ( prc ) + rd , en , + does> { y self -- val } + self @ { rd } + self cell+ @ { en } + rd read-sample en env f* y f+ ; + : places2-cb { rd en -- prc; y self -- val } - 1 proc-create rd , en , ( prc ) - does> { y self -- val } - self @ { rd } - self cell+ @ { en } - rd read-sample 1.0 en env f- f* y f+ + 1 proc-create ( prc ) + rd , en , + does> { y self -- val } + self @ { rd } + self cell+ @ { en } + rd read-sample 1.0 en env f- f* y f+ ; set-current + : place-sound ( mono stereo pan -- res ) - doc" Mixes a mono sound into a stereo sound, \ + doc" Mix a mono sound into a stereo sound, \ splitting it into two copies whose amplitudes depend on the envelope PAN-ENV. \ If PAN-ENV is a number, the sound is split such that 0 is all in channel 0 \ and 90 is all in channel 1." - { mono stereo pan } - mono #f #f #f frames { len } - pan number? if - pan 90.0 f/ { pos } - 0 mono #f 1 #f make-sampler { reader0 } - 0 mono #f 1 #f make-sampler { reader1 } - reader1 pos places1-cb 0 len stereo 1 #f #f map-channel drop - reader0 pos places0-cb 0 len stereo 0 #f #f map-channel drop - else - :envelope pan :length len make-env { e0 } - :envelope pan :length len make-env { e1 } - 0 mono #f 1 #f make-sampler { reader0 } - 0 mono #f 1 #f make-sampler { reader1 } - reader1 e1 places3-cb 0 len stereo 1 #f #f map-channel drop - reader0 e0 places2-cb 0 len stereo 0 #f #f map-channel drop - then + { mono stereo pan } + mono #f #f #f framples { len } + pan number? if + pan 90.0 f/ { pos } + 0 mono #f 1 #f make-sampler { reader0 } + 0 mono #f 1 #f make-sampler { reader1 } + reader1 pos places1-cb 0 len stereo 1 #f #f map-channel drop + reader0 pos places0-cb 0 len stereo 0 #f #f map-channel drop + else + :envelope pan :length len make-env { e0 } + :envelope pan :length len make-env { e1 } + 0 mono #f 1 #f make-sampler { reader0 } + 0 mono #f 1 #f make-sampler { reader1 } + reader1 e1 places3-cb 0 len stereo 1 #f #f map-channel drop + reader0 e0 places2-cb 0 len stereo 0 #f #f map-channel drop + then ; previous \ ;;; -------- FFT-based editing : fft-edit <{ bottom top :optional snd #f chn #f -- vct }> - doc" Ffts an entire sound, removes all energy below BOTTOM and all above TOP, then inverse ffts." - snd srate { sr } - snd chn #f frames { len } - 2.0 len flog 2.0 flog f/ fceil f** fround->s { fsize } - 0 fsize snd chn #f channel->vct { rdata } - fsize 0.0 make-vct { idata } - bottom sr fsize f/ f/ fround->s { lo } - top sr fsize f/ f/ fround->s { hi } - rdata idata 1 fft drop - lo 0> if - rdata 0 0.0 vct-set! drop - idata 0 0.0 vct-set! drop - fsize 1- { jj } - lo 1 ?do - rdata i 0.0 vct-set! drop - rdata jj 0.0 vct-set! drop - idata i 0.0 vct-set! drop - idata jj 0.0 vct-set! drop - jj 1- to jj - loop - then - hi fsize 2/ < if - fsize hi - { jj } - fsize 2/ hi ?do - rdata i 0.0 vct-set! drop - rdata jj 0.0 vct-set! drop - idata i 0.0 vct-set! drop - idata jj 0.0 vct-set! drop - jj 1- to jj - loop - then - rdata idata -1 fft drop - $" %s %s %s" #( bottom top get-func-name ) string-format { origin } - rdata fsize 1/f vct-scale! ( rdata) 0 len 1- snd chn #f origin vct->channel + doc" Fft an entire sound, remove all energy \ +below BOTTOM and all above TOP, then inverse fft." + snd srate { sr } + snd chn #f framples { len } + 2.0 len flog 2.0 flog f/ fceil f** fround->s { fsize } + 0 fsize snd chn #f channel->vct { rdata } + fsize 0.0 make-vct { idata } + bottom sr fsize f/ f/ fround->s { lo } + top sr fsize f/ f/ fround->s { hi } + rdata idata 1 fft drop + lo 0> if + rdata 0 0.0 vct-set! drop + idata 0 0.0 vct-set! drop + fsize 1- { jj } + lo 1 ?do + rdata i 0.0 vct-set! drop + rdata jj 0.0 vct-set! drop + idata i 0.0 vct-set! drop + idata jj 0.0 vct-set! drop + jj 1- to jj + loop + then + hi fsize 2/ < if + fsize hi - { jj } + fsize 2/ hi ?do + rdata i 0.0 vct-set! drop + rdata jj 0.0 vct-set! drop + idata i 0.0 vct-set! drop + idata jj 0.0 vct-set! drop + jj 1- to jj + loop + then + rdata idata -1 fft drop + "%s %s %s" #( bottom top get-func-name ) string-format { origin } + rdata fsize 1/f vct-scale! 0 len 1- snd chn #f origin vct->channel ; + : fft-squelch <{ squelch :optional snd #f chn #f -- scl }> - doc" Ffts an entire sound, sets all bins to 0.0 whose energy is below squelch, then inverse ffts." - snd chn #f frames { len } - 2.0 len flog 2.0 flog f/ fceil f** fround->s { fsize } - 0 fsize snd chn #f channel->vct { rdata } - fsize 0.0 make-vct { idata } - fsize 2/ { fsize2 } - rdata idata 1 fft drop - rdata vct-copy { vr } - idata vct-copy { vi } - vr vi rectangular->polar drop - vr vct-peak { scaler } - squelch scaler f* { scl-squelch } - rdata 0 vct-ref dup f* idata 0 vct-ref dup f* f+ fsqrt scl-squelch f< if - rdata 0 0.0 vct-set! drop - idata 0 0.0 vct-set! drop - then - fsize 1- { jj } - fsize 1 ?do - rdata i vct-ref dup f* idata i vct-ref dup f* f+ fsqrt scl-squelch f< if - rdata i 0.0 vct-set! drop - rdata jj 0.0 vct-set! drop - idata i 0.0 vct-set! drop - idata jj 0.0 vct-set! drop - then - jj 1- to jj - loop - rdata idata -1 fft drop - $" %s %s" #( squelch get-func-name ) string-format { origin } - rdata fsize 1/f vct-scale! ( rdata) 0 len 1- snd chn #f origin vct->channel drop - scaler + doc" Fft an entire sound, set all bins \ +to 0.0 whose energy is below squelch, then inverse fft." + snd chn #f framples { len } + 2.0 len flog 2.0 flog f/ fceil f** fround->s { fsize } + 0 fsize snd chn #f channel->vct { rdata } + fsize 0.0 make-vct { idata } + fsize 2/ { fsize2 } + rdata idata 1 fft drop + rdata vct-copy { vr } + idata vct-copy { vi } + vr vi rectangular->polar drop + vr vct-peak { scaler } + squelch scaler f* { scl-squelch } + rdata 0 vct-ref dup f* + idata 0 vct-ref dup f* f+ fsqrt scl-squelch f< if + rdata 0 0.0 vct-set! drop + idata 0 0.0 vct-set! drop + then + fsize 1- { jj } + fsize 1 ?do + rdata i vct-ref dup f* + idata i vct-ref dup f* f+ fsqrt scl-squelch f< if + rdata i 0.0 vct-set! drop + rdata jj 0.0 vct-set! drop + idata i 0.0 vct-set! drop + idata jj 0.0 vct-set! drop + then + jj 1- to jj + loop + rdata idata -1 fft drop + "%s %s" #( squelch get-func-name ) string-format { origin } + rdata fsize 1/f vct-scale! 0 len 1- snd chn #f origin vct->channel drop + scaler ; + : fft-cancel <{ lo-freq hi-freq :optional snd #f chn #f -- vct }> - doc" Ffts an entire sound, sets the bin(s) representing LO-FREQ to HI-FREQ to 0.0, \ -then inverse ffts" - snd srate { sr } - snd chn #f frames { len } - 2.0 len flog 2.0 flog f/ fceil f** fround->s { fsize } - 0 fsize snd chn #f channel->vct { rdata } - fsize 0.0 make-vct { idata } - rdata idata 1 fft drop - sr fsize f/ { hz-bin } - lo-freq hz-bin f/ fround->s { lo-bin } - hi-freq hz-bin f/ fround->s { hi-bin } - fsize lo-bin - { jj } - hi-bin 1+ lo-bin ?do - rdata i 0.0 vct-set! drop - rdata jj 0.0 vct-set! drop - idata i 0.0 vct-set! drop - idata jj 0.0 vct-set! drop - jj 1- to jj - loop - rdata idata -1 fft drop - $" %s %s %s" #( lo-freq hi-freq get-func-name ) string-format { origin } - rdata fsize 1/f vct-scale! ( rdata) 0 len 1- snd chn #f origin vct->channel + doc" Fft an entire sound, set the bin(s) \ +representing LO-FREQ to HI-FREQ to 0, then inverse fft." + snd srate { sr } + snd chn #f framples { len } + 2.0 len flog 2.0 flog f/ fceil f** fround->s { fsize } + 0 fsize snd chn #f channel->vct { rdata } + fsize 0.0 make-vct { idata } + rdata idata 1 fft drop + sr fsize f/ { hz-bin } + lo-freq hz-bin f/ fround->s { lo-bin } + hi-freq hz-bin f/ fround->s { hi-bin } + fsize lo-bin - { jj } + hi-bin 1+ lo-bin ?do + rdata i 0.0 vct-set! drop + rdata jj 0.0 vct-set! drop + idata i 0.0 vct-set! drop + idata jj 0.0 vct-set! drop + jj 1- to jj + loop + rdata idata -1 fft drop + "%s %s %s" #( lo-freq hi-freq get-func-name ) string-format { origin } + rdata fsize 1/f vct-scale! 0 len 1- snd chn #f origin vct->channel ; : make-ramp <{ :optional size 128 -- gen }> - doc" Returns a ramp generator." - vct( 0.0 size ) + doc" Return ramp generator." + vct( 0.0 size ) ; + : ramp <{ gen up -- val }> - doc" Is a kind of CLM generator that produces a ramp of a given length, \ -then sticks at 0.0 or 1.0 until the UP argument changes." - gen 0 vct-ref { ctr } - gen 1 vct-ref { size } - ctr size f/ { val } - gen 0 ctr up if 1 else -1 then f+ 0 fmax size fmin vct-set! drop - val + doc" Is a kind of CLM generator that produces a ramp of a \ +given length, then sticks at 0.0 or 1.0 until the UP argument changes." + gen 0 vct-ref { ctr } + gen 1 vct-ref { size } + ctr size f/ { val } + gen 0 ctr up if + 1 + else + -1 + then f+ 0 fmax size fmin vct-set! drop + val ; hide : squelch-vowels-cb { snd chn -- prc; y self -- val } - 32 { fft-size } - 0 snd chn 1 #f make-sampler { read-ahead } - 1 proc-create { prc } - fft-size 0.0 make-vct map! read-ahead #() apply end-map ( rl ) , - fft-size 0.0 make-vct ( im ) , - 256 make-ramp ( ramper ) , - snd chn #f maxamp fft-size f2/ f/ ( peak ) , - read-ahead , - #f ( in-vowel ) , - prc - does> { y self -- val } - self @ { rl } - self cell+ @ { im } - self 2 cells + @ { ramper } - self 3 cells + @ { peak } - self 4 cells + @ { read-ahead } - self 5 cells + @ { in-vowel } - rl read-ahead #() apply cycle-set! - rl cycle-start@ 0= if - rl im 1 fft drop - rl rl vct-multiply! drop - im im vct-multiply! drop - rl im vct-add! drop - rl 0 vct-ref rl 1 vct-ref f+ rl 2 vct-ref f+ rl 3 vct-ref f+ peak f> to in-vowel - in-vowel self 5 cells + ! - im 0.0 vct-fill! drop - then - 1.0 ramper in-vowel ramp f- ( rval ) y f* + 32 { fft-size } + 0 snd chn 1 #f make-sampler { read-ahead } + 1 proc-create { prc } + fft-size 0.0 make-vct map! + read-ahead #() apply + end-map ( rl ) , + fft-size 0.0 make-vct ( im ) , + 256 make-ramp ( ramper ) , + snd chn #f maxamp fft-size f2/ f/ ( peak ) , + read-ahead , + #f ( in-vowel ) , + prc + does> { y self -- val } + self @ { rl } + self cell+ @ { im } + self 2 cells + @ { ramper } + self 3 cells + @ { peak } + self 4 cells + @ { read-ahead } + self 5 cells + @ { in-vowel } + rl read-ahead #() apply cycle-set! + rl cycle-start@ 0= if + rl im 1 fft ( rl ) dup vct-multiply! drop + im im vct-multiply! drop + rl im 0 vct-add! drop + rl 0 vct-ref + rl 1 vct-ref f+ + rl 2 vct-ref f+ + rl 3 vct-ref f+ peak f> to in-vowel + in-vowel self 5 cells + ! ( in-vowel ) + im 0.0 vct-fill! drop + then + 1.0 ramper in-vowel ramp f- ( rval ) y f* ; set-current + : squelch-vowels <{ :optional snd #f chn #f -- val }> - doc" Suppresses portions of a sound that look like steady-state." - snd chn squelch-vowels-cb 0 #f snd chn #f get-func-name map-channel + doc" Suppress portions of a sound that look like steady-state." + snd chn squelch-vowels-cb 0 #f snd chn #f get-func-name map-channel ; previous : fft-env-data <{ fft-env :optional snd #f chn #f -- vct }> - doc" Applies FFT-ENV as spectral env to current sound, returning vct of new data." - snd chn #f frames { len } - 2.0 len flog 2.0 flog f/ fceil f** fround->s { fsize } - 0 fsize snd chn #f channel->vct { rdata } - fsize 0.0 make-vct { idata } - fsize 2/ { fsize2 } - :envelope fft-env :length fsize2 make-env { e } - rdata idata 1 fft drop - e env { val } - rdata 0 val object-set*! - idata 0 val object-set*! - fsize 1- { jj } - fsize2 1 ?do - e env to val - rdata i val object-set*! - rdata jj val object-set*! - idata i val object-set*! - idata jj val object-set*! - jj 1- to jj - loop - rdata idata -1 fft drop - rdata fsize 1/f vct-scale! + doc" Apply FFT-ENV as spectral env to current sound, \ +returning vct of new data." + snd chn #f framples { len } + 2.0 len flog 2.0 flog f/ fceil f** fround->s { fsize } + 0 fsize snd chn #f channel->vct { rdata } + fsize 0.0 make-vct { idata } + fsize 2/ { fsize2 } + :envelope fft-env :length fsize2 make-env { e } + rdata idata 1 fft drop + e env { val } + rdata 0 val object-set*! + idata 0 val object-set*! + fsize 1- { jj } + fsize2 1 ?do + e env to val + rdata i val object-set*! + rdata jj val object-set*! + idata i val object-set*! + idata jj val object-set*! + jj 1- to jj + loop + rdata idata -1 fft ( rdata ) fsize 1/f vct-scale! ; : fft-env-edit <{ fft-env :optional snd #f chn #f -- vct }> - doc" Edits (filters) current chan using FFT-ENV." - $" %s %s" #( fft-env get-func-name ) string-format { origin } - fft-env snd chn fft-env-data 0 snd chn #f frames 1- snd chn #f origin vct->channel + doc" Edit (filter) current chan using FFT-ENV." + "%s %s" #( fft-env get-func-name ) string-format { origin } + fft-env snd chn fft-env-data 0 snd chn #f framples 1- + snd chn #f origin vct->channel ; : fft-env-interp <{ env1 env2 interp :optional snd #f chn #f -- vct }> - doc" Interpolates between two fft-filtered versions (ENV1 and ENV2 are the spectral envelopes) \ + doc" Interpolate between two fft-filtered \ +versions (ENV1 and ENV2 are the spectral envelopes) \ following interp (an env between 0 and 1)." - env1 snd chn fft-env-data { data1 } - env2 snd chn fft-env-data { data2 } - snd chn #f frames { len } - :envelope interp :length len make-env { e } - $" %s %s %s %s" #( env1 env2 interp get-func-name ) string-format { origin } - len 0.0 make-vct map! - e env { pan } - 1.0 pan f- data1 i vct-ref f* data2 i vct-ref pan f* f+ - end-map ( new-data ) 0 len 1- snd chn #f origin vct->channel + env1 snd chn fft-env-data { data1 } + env2 snd chn fft-env-data { data2 } + snd chn #f framples { len } + :envelope interp :length len make-env { e } + "%s %s %s %s" #( env1 env2 interp get-func-name ) + string-format { origin } + len 0.0 make-vct map! + e env { pan } + 1.0 pan f- data1 i vct-ref f* data2 i vct-ref pan f* f+ + end-map ( new-data ) 0 len 1- snd chn #f origin vct->channel ; : filter-fft <{ flt :optional normalize #t snd #f chn #f -- val }> - doc" Gets the spectrum of all the data in the given channel, \ -applies the function FLT to it, then inverse ffts. \ + doc" Get the spectrum of all the data in the given channel, \ +apply the function FLT to it, then inverse fft. \ FLT should take one argument, the current spectrum value.\n\ lambda: <{ y -- val }> y 0.01 f< if 0.0 else y then ; filter-fft\n\ is like fft-squelch." - snd chn #f frames { len } - snd chn #f maxamp { mx } - 2.0 len flog 2.0 flog f/ fceil f** fround->s { fsize } - fsize 2/ { fsize2 } - 0 fsize snd chn #f channel->vct { rdata } - fsize 0.0 make-vct { idata } - rdata rectangular-window fsize #t 1.0 #f ( in-place == #f ) normalize snd-spectrum { spect } - rdata idata 1 fft drop - flt #( spect 0 vct-ref ) run-proc drop - fsize 1- { jj } - fsize2 1 ?do - spect i vct-ref { orig } - flt #( orig ) run-proc { cur } - orig fabs 0.000001 f> if - cur orig f/ { scl } - rdata i scl object-set*! - idata i scl object-set*! - rdata jj scl object-set*! - idata jj scl object-set*! - else - cur fabs 0.000001 f> if - cur 2.0 fsqrt f/ { scl } - rdata i scl vct-set! drop - idata i scl vct-set! drop - rdata jj scl vct-set! drop - idata jj scl fnegate vct-set! drop - then - then - jj 1- to jj - loop - rdata idata -1 fft drop - $" <'> %s %s" #( flt get-func-name ) string-format { origin } - mx f0<> if - rdata vct-peak { pk } - rdata mx pk f/ vct-scale! - else - rdata - then 0 len 1- snd chn #f origin vct->channel + snd chn #f framples { len } + snd chn #f maxamp { mx } + 2.0 len flog 2.0 flog f/ fceil f** fround->s { fsize } + fsize 2/ { fsize2 } + 0 fsize snd chn #f channel->vct { rdata } + fsize 0.0 make-vct { idata } + rdata rectangular-window fsize #t 1.0 + #f ( in-place == #f ) normalize snd-spectrum { spect } + rdata idata 1 fft drop + flt #( spect 0 vct-ref ) run-proc drop + fsize 1- { jj } + fsize2 1 ?do + spect i vct-ref { orig } + flt #( orig ) run-proc { cur } + orig fabs 0.000001 f> if + cur orig f/ { scl } + rdata i scl object-set*! + idata i scl object-set*! + rdata jj scl object-set*! + idata jj scl object-set*! + else + cur fabs 0.000001 f> if + cur 2.0 fsqrt f/ { scl } + rdata i scl vct-set! drop + idata i scl vct-set! drop + rdata jj scl vct-set! drop + idata jj scl fnegate vct-set! drop + then + then + jj 1- to jj + loop + rdata idata -1 fft drop + "<'> %s %s" #( flt get-func-name ) string-format { origin } + mx f0<> if + rdata vct-peak { pk } + rdata mx pk f/ vct-scale! + else + rdata + then 0 len 1- snd chn #f origin vct->channel ; \ 0.5 0.5 make-one-zero filter-fft \ 0.05 0.05 make-one-pole filter-fft @@ -1031,189 +1186,215 @@ is like fft-squelch." \ lambda: <{ y -- val }> y y f* y f* ; filter-fft : fft-smoother <{ cutoff start samps :optional snd #f chn #f -- val }> - doc" Uses fft-filtering to smooth a section:\n\ + doc" Use fft-filtering to smooth a section:\n\ #f #f #f cursor value curs -0.1 curs 400 #f #f fft-smoother curs 400 #f #f #f undef vct->channel" - 2.0 samps 1+ flog 2.0 flog f/ fceil f** fround->s { fftpts } - start fftpts snd chn #f channel->vct { rl } - fftpts 0.0 make-vct { im } - fftpts cutoff f* fround->s { top } - rl 0 vct-ref { old0 } - rl samps 1- vct-ref { old1 } - rl vct-peak { oldmax } - rl im 1 fft drop - fftpts top ?do - rl i 0.0 vct-set! drop - im i 0.0 vct-set! drop - loop - rl im -1 fft drop - rl fftpts 1/f vct-scale! drop - rl vct-peak { newmax } - newmax f0<> if - oldmax newmax f/ 1.5 f> if rl oldmax newmax f/ vct-scale! drop then - rl 0 vct-ref { new0 } - rl samps 1- vct-ref { new1 } - old0 new0 f- { offset0 } - old1 new1 f- { offset1 } - offset1 offset0 f= if 0.0 else offset1 offset0 f- samps f/ then { incr } - offset0 { trend } - samps 0 ?do - rl i trend object-set+! - trend incr f+ to trend - loop - then - rl +0.1 curs 400 #f #f fft-smoother curs 400 #f #f #f undef vct->channel." + 2.0 samps 1+ flog 2.0 flog f/ fceil f** fround->s { fftpts } + start fftpts snd chn #f channel->vct { rl } + fftpts 0.0 make-vct { im } + fftpts cutoff f* fround->s { top } + rl 0 vct-ref { old0 } + rl samps 1- vct-ref { old1 } + rl vct-peak { oldmax } + rl im 1 fft drop + fftpts top ?do + rl i 0.0 vct-set! drop + im i 0.0 vct-set! drop + loop + rl im -1 fft ( rl ) fftpts 1/f vct-scale! drop + rl vct-peak { newmax } + newmax f0<> if + oldmax newmax f/ 1.5 f> if + rl oldmax newmax f/ vct-scale! drop + then + rl 0 vct-ref { new0 } + rl samps 1- vct-ref { new1 } + old0 new0 f- { offset0 } + old1 new1 f- { offset1 } + offset1 offset0 f= if + 0.0 + else + offset1 offset0 f- samps f/ + then { incr } + offset0 { trend } + samps 0 ?do + rl i trend object-set+! + trend incr f+ to trend + loop + then + rl ; \ ;;; -------- comb-filter -: comb-filter ( scaler size -- prc; x self -- res ) - doc" Returns a comb-filter ready for map-channel etc: 0.8 32 comb-filter map-channel. \ +: comb-filter ( scaler size -- prc; x self -- val ) + doc" Return comb-filter ready for map-channel etc: \ +0.8 32 comb-filter map-channel. \ If you're in a hurry use: 0.8 32 make-comb clm-channel instead." - { scaler size } - scaler size make-comb { gen } - 1 proc-create gen , - does> ( x self -- res ) - { x self } - self @ ( cmb ) x 0.0 comb -; - -\ by using filters at harmonically related sizes, we can get chords: - -: comb-chord ( scaler size amp -- prc; x self -- res ) - doc" Returns a set of harmonically-related comb filters: 0.95 100 0.3 comb-chord map-channel" - { scaler size amp } - scaler size make-comb { c1 } - scaler size 0.75 f* f>s make-comb { c2 } - scaler size 1.2 f* f>s make-comb { c3 } - 1 proc-create amp , c1 , c2 , c3 , - does> ( x self -- res ) - { x self } - self @ { amp } - self 1 cells + @ { c1 } - self 2 cells + @ { c2 } - self 3 cells + @ { c3 } - c1 x 0.0 comb c2 x 0.0 comb c3 x 0.0 comb f+ f+ amp f* + { scaler size } + scaler size make-comb { gen } + 1 proc-create ( prc ) + gen , + does> { x self -- val } + self @ ( cmb ) x 0.0 comb +; + +\ ;;; by using filters at harmonically related sizes, we can get chords: + +: comb-chord ( scaler size amp -- prc; x self -- val ) + doc" Return set of harmonically-related comb \ +filters: 0.95 100 0.3 comb-chord map-channel." + { scaler size amp } + scaler size make-comb { c1 } + scaler size 0.75 f* f>s make-comb { c2 } + scaler size 1.2 f* f>s make-comb { c3 } + 1 proc-create ( prc ) + amp , c1 , c2 , c3 , + does> ( x self -- val ) + { x self } + self @ { amp } + self 1 cells + @ { c1 } + self 2 cells + @ { c2 } + self 3 cells + @ { c3 } + c1 x 0.0 comb c2 x 0.0 comb c3 x 0.0 comb f+ f+ amp f* ; \ ;;; or change the comb length via an envelope: : zcomb ( scaler size pm -- prc; x self -- val ) - doc" Returns a comb filter whose length varies according to an envelope:\n\ -0.8 32 #( 0 0 1 10 ) zcomb map-channel " - { scaler size pm } - :size size :max-size pm 0.0 max-envelope 1.0 f+ size f+ fround->s make-comb { cmb } - :envelope pm :length #f #f #f frames make-env { penv } - 1 proc-create cmb , penv , - does> { x self -- val } - self @ ( cmb ) x self cell+ @ ( penv ) env comb + doc" Return comb filter whose length varies according to an envelope:\n\ +0.8 32 #( 0 0 1 10 ) zcomb map-channel." + { scaler size pm } + :size size + :max-size pm 0.0 max-envelope 1.0 f+ size f+ fround->s + make-comb { gen } + :envelope pm :length #f #f #f framples make-env { penv } + 1 proc-create ( prc ) + gen , penv , + does> { x self -- val } + self @ ( gen ) x self cell+ @ ( penv ) env comb ; : notch-filter ( scaler size -- prc; x self -- val ) - doc" Returns a notch-filter: 0.8 32 notch-filter map-channel" - make-notch { gen } - 1 proc-create gen , - does> { x self -- val } - self @ ( cmd ) x 0.0 notch + doc" Return notch-filter: 0.8 32 notch-filter map-channel." + make-notch { gen } + 1 proc-create ( prc ) + gen , + does> { x self -- val } + self @ ( gen ) x 0.0 notch ; : formant-filter ( radius frequency -- prc; x self -- val ) - doc" Returns a formant generator: 2400 0.99 formant-filter map-channel. \ -Faster is: 2400 0.99 make-formant filter-sound" - make-formant { gen } - 1 proc-create gen , - does> { x self -- val } - self @ ( frm ) x formant + doc" Return formant generator: 2400 0.99 formant-filter map-channel. \ +Faster is: 2400 0.99 make-formant filter-sound." + make-formant { gen } + 1 proc-create ( prc ) + gen , + does> { x self -- val } + self @ ( gen ) x formant ; : formants ( r1 f1 r2 f2 r3 f3 -- prc; x self -- val ) - doc" Returns 3 formant filters in parallel: 0.99 900 0.98 1800 0.99 2700 formants map-channel" - { r1 f1 r2 f2 r3 f3 } - f1 r1 make-formant { fr1 } - f2 r2 make-formant { fr2 } - f3 r3 make-formant { fr3 } - 1 proc-create fr1 , fr2 , fr3 , - does> { x self -- val } - self @ x formant - self 1 cells + @ x formant f+ - self 2 cells + @ x formant f+ + doc" Return 3 formant filters in \ +parallel: 0.99 900 0.98 1800 0.99 2700 formants map-channel." + { r1 f1 r2 f2 r3 f3 } + f1 r1 make-formant { fr1 } + f2 r2 make-formant { fr2 } + f3 r3 make-formant { fr3 } + 1 proc-create ( prc ) + fr1 , fr2 , fr3 , + does> { x self -- val } + self @ ( fr1 ) x formant + self 1 cells + @ ( fr2 ) x formant f+ + self 2 cells + @ ( fr3 ) x formant f+ ; : moving-formant ( radius move -- prc; x self -- val ) - doc" Returns a time-varying (in frequency) formant filter:\n\ -0.99 #( 0 1200 1 2400 ) moving-formant map-channel" - { radius move } - move 1 array-ref radius make-formant { frm } - :envelope move :length #f #f #f frames make-env { menv } - 1 proc-create frm , menv , - does> { x self -- val } - self @ ( frm ) x formant ( ret ) - self @ ( frm ) self cell+ @ ( menv ) env set-mus-frequency drop - ( ret ) + doc" Return time-varying (in frequency) formant filter:\n\ +0.99 #( 0 1200 1 2400 ) moving-formant map-channel." + { radius move } + move 1 array-ref radius make-formant { frm } + :envelope move :length #f #f #f framples make-env { menv } + 1 proc-create ( prc ) + frm , menv , + does> { x self -- val } + self @ ( frm ) x formant ( val ) + self @ ( frm ) self cell+ @ ( menv ) env set-mus-frequency drop + ( val ) ; : osc-formants ( radius bases amounts freqs -- prc; x self -- val ) - doc" Returns a time-varying (in frequency) formant filter:\n\ -0.99 #( 0 1200 1 2400 ) moving-formant map-channel" - { radius bases amounts freqs } - bases vct-length { len } - len make-array map! bases i vct-ref radius make-formant end-map { frms } - len make-array map! freqs i vct-ref make-oscil end-map { oscs } - 1 proc-create frms , amounts , oscs , bases , - does> { x self -- val } - self @ { frms } - self 1 cells + @ { amounts } - self 2 cells + @ { oscs } - self 3 cells + @ { bases } - 0.0 ( val ) - frms each { frm } - frm x formant f+ ( val += ... ) - frm bases i vct-ref - amounts i vct-ref oscs i array-ref 0.0 0.0 oscil f* f+ - set-mus-frequency drop - end-each - ( val ) + doc" Return time-varying (in frequency) formant filter:\n\ +0.99 #( 0 1200 1 2400 ) moving-formant map-channel." + { radius bases amounts freqs } + bases vct-length { len } + len make-array map! + bases i vct-ref radius make-formant + end-map { frms } + len make-array map! + freqs i vct-ref make-oscil + end-map { oscs } + 1 proc-create ( prc ) + frms , amounts , oscs , bases , + does> { x self -- val } + self @ { frms } + self 1 cells + @ { amounts } + self 2 cells + @ { oscs } + self 3 cells + @ { bases } + 0.0 ( val ) + frms each { frm } + frm x formant f+ ( val += ... ) + frm bases i vct-ref + amounts i vct-ref + oscs i array-ref 0.0 0.0 oscil f* f+ set-mus-frequency drop + end-each + ( val ) ; \ ;;; -------- echo : echo ( scaler secs -- prc; y self -- val ) - doc" Returns an echo maker: 0.5 0.5 echo 0 44100 map-channel" - { scaler secs } - secs #f srate f* fround->s make-delay { del } - 1 proc-create del , scaler , ( prc ) - does> { y self -- val } - self @ { del } - self cell+ @ { scaler } - del del 0.0 tap y f+ scaler f* 0.0 delay y f+ + doc" Return echo maker: 0.5 0.5 echo 0 44100 map-channel." + { scaler secs } + secs #f srate f* fround->s make-delay { del } + 1 proc-create ( prc ) + del , scaler , + does> { y self -- val } + self @ { del } + self cell+ @ { scaler } + del del 0.0 tap y f+ scaler f* 0.0 delay y f+ ; : zecho ( scaler secs freq amp -- prc; y self -- val ) - doc" Returns a modulated echo maker: 0.5 0.75 6 10.0 zecho 0 65000 map-channel" - { scaler secs freq amp } - freq make-oscil { os } - secs #f srate f* fround->s { len } - :size len :max-size len amp f+ fround->s 1+ make-delay { del } - 1 proc-create del , scaler , os , amp , ( prc ) - does> { y self -- val } - self @ { del } - self cell+ @ { scaler } - self 2 cells + @ { os } - self 3 cells + @ { amp } - del del 0.0 tap y f+ scaler f* os 0.0 0.0 oscil amp f* delay y f+ + doc" Return modulated echo maker: \ +0.5 0.75 6 10.0 zecho 0 65000 map-channel." + { scaler secs freq amp } + freq make-oscil { os } + secs #f srate f* fround->s { len } + :size len :max-size len amp f+ fround->s 1+ make-delay { del } + 1 proc-create ( prc ) + del , scaler , os , amp , + does> { y self -- val } + self @ { del } + self cell+ @ { scaler } + self 2 cells + @ { os } + self 3 cells + @ { amp } + del del 0.0 tap y f+ scaler f* os 0.0 0.0 oscil amp f* delay y f+ ; : flecho ( scaler secs -- prc; y self -- val ) - doc" Returns a low-pass filtered echo maker: 0.5 0.9 flecho 0 75000 map-channel" - { scaler secs } - :order 4 :xcoeffs vct( 0.125 0.25 0.25 0.125 ) make-fir-filter { flt } - secs #f srate f* fround->s make-delay { del } - 1 proc-create del , scaler , flt , ( prc ) - does> { y self -- val } - self @ { del } - self cell+ @ { scaler } - self 2 cells + @ { flt } - del flt del 0.0 tap y f+ scaler f* fir-filter 0.0 delay y f+ + doc" Return low-pass filtered echo maker: \ +0.5 0.9 flecho 0 75000 map-channel." + { scaler secs } + :order 4 :xcoeffs vct( 0.125 0.25 0.25 0.125 ) make-fir-filter { flt } + secs #f srate f* fround->s make-delay { del } + 1 proc-create ( prc ) + del , scaler , flt , + does> { y self -- val } + self @ { del } + self cell+ @ { scaler } + self 2 cells + @ { flt } + del flt del 0.0 tap y f+ scaler f* fir-filter 0.0 delay y f+ ; \ ;;; -------- ring-mod and am @@ -1221,38 +1402,41 @@ Faster is: 2400 0.99 make-formant filter-sound" \ ;;; CLM instrument is ring-modulate.ins : ring-mod ( freq gliss-env -- prc; y self -- val ) - doc" Returns a time-varying ring-modulation filter:\n\ -10 #( 0 0 1 100 hz->radians ) ring-mod map-channel" - { freq gliss-env } - :frequency freq make-oscil { os } - :envelope gliss-env :length #f #f #f frames make-env { genv } - 1 proc-create os , genv , ( prc ) - does> { y self -- val } - self @ { os } - self cell+ @ { genv } - os genv env 0.0 oscil y f* + doc" Return time-varying ring-modulation filter:\n\ +10 #( 0 0 1 100 hz->radians ) ring-mod map-channel." + { freq gliss-env } + :frequency freq make-oscil { os } + :envelope gliss-env :length #f #f #f framples make-env { genv } + 1 proc-create ( prc ) + os , genv , + does> { y self -- val } + self @ { os } + self cell+ @ { genv } + os genv env 0.0 oscil y f* ; : am ( freq -- prc; y self -- val ) - doc" Returns an amplitude-modulator: 440 am map-channel" - make-oscil { os } - 1 proc-create os , ( prc ) - does> { y self -- val } - 1.0 y self @ ( os ) 0.0 0.0 oscil amplitude-modulate + doc" Return amplitude-modulator: 440 am map-channel." + make-oscil { os } + 1 proc-create ( prc ) + os , + does> { y self -- val } + 1.0 y self @ ( os ) 0.0 0.0 oscil amplitude-modulate ; \ ;;; this taken from sox (vibro.c) : vibro ( speed depth -- prc; y self -- val ) - { speed depth } - speed make-oscil { sine } - depth f2/ { scl } - 1.0 scl f- { offset } - 1 proc-create sine , scl , offset , ( prc ) - does> { y self -- val } - self @ { sine } - self cell+ @ { scl } - self 2 cells + @ { offset } - sine 0.0 0.0 oscil scl f* offset f+ y f* + { speed depth } + speed make-oscil { sine } + depth f2/ { scl } + 1.0 scl f- { offset } + 1 proc-create ( prc ) + sine , scl , offset , + does> { y self -- val } + self @ { sine } + self cell+ @ { scl } + self 2 cells + @ { offset } + sine 0.0 0.0 oscil scl f* offset f+ y f* ; \ ;;; -------- hello-dentist @@ -1261,29 +1445,32 @@ Faster is: 2400 0.99 make-formant filter-sound" hide : hd-input-cb { in-data -- prc; dir self -- val } - 1 proc-create { prc } - in-data , - 0 ( idx ) , - prc - does> { dir self -- val } - self @ { in-data } - self cell+ @ { idx } - in-data idx object-range? if in-data idx vct-ref else 0.0 then ( val ) - idx dir + self cell+ ! - ( val ) + 1 proc-create ( prc ) + in-data , 0 ( idx ) , + does> { dir self -- val } + self @ { in-data } + self cell+ @ { idx } + in-data idx object-range? if + in-data idx vct-ref + else + 0.0 + then ( val ) + idx dir + self cell+ ! ( idx ) + ( val ) ; set-current + : hello-dentist <{ frq amp :optional snd #f chn #f -- vct }> - doc" Varies the sampling rate randomly, making a voice sound quavery:\n\ -40.0 0.1 #f #f hello-dentist drop" - :frequency frq :amplitude amp make-rand-interp { rn } - snd chn #f frames { len } - 0 len snd chn #f channel->vct { in-data } - :srate 1.0 :input in-data hd-input-cb make-src { rd } - $" %s %s %s" #( frq amp get-func-name ) string-format { origin } - amp f2* 1.0 f+ len f* fround->s 0.0 make-vct map! - rd rn 0.0 rand-interp #f src - end-map ( out-data ) 0 len snd chn #f origin vct->channel + doc" Vary the sampling rate randomly, making a voice sound quavery:\n\ +40.0 0.1 #f #f hello-dentist drop." + :frequency frq :amplitude amp make-rand-interp { rn } + snd chn #f framples { len } + 0 len snd chn #f channel->vct { in-data } + :srate 1.0 :input in-data hd-input-cb make-src { rd } + "%s %s %s" #( frq amp get-func-name ) string-format { origin } + amp f2* 1.0 f+ len f* fround->s 0.0 make-vct map! + rd rn 0.0 rand-interp #f src + end-map ( out-data ) 0 len snd chn #f origin vct->channel ; previous @@ -1292,121 +1479,122 @@ previous hide : fp-input-cb { sf -- prc; dir self -- val } - 1 proc-create sf , - does> { dir self -- val } - self @ ( sf ) dir 0> if next-sample else previous-sample then + 1 proc-create ( prc ) + sf , + does> { dir self -- val } + self @ ( sf ) dir 0> if + next-sample + else + previous-sample + then ; set-current + : fp <{ sr osamp osfrq :optional snd #f chn #f -- vct }> - doc" Varies the sampling rate via an oscil: 1.0 0.3 20 #f #f fp" - osfrq make-oscil { os } - 0 snd chn 1 #f make-sampler { sf } - :srate sr :input sf fp-input-cb make-src { s } - snd chn #f frames { len } - $" %s %s %s %s" #( sr osamp osfrq get-func-name ) string-format { origin } - len 0.0 make-vct map! - s os 0.0 0.0 oscil osamp f* #f src - end-map ( out-data ) 0 len snd chn #f origin vct->channel - sf free-sampler drop + doc" Vary the sampling rate via an oscil: 1.0 0.3 20 #f #f fp." + osfrq make-oscil { os } + 0 snd chn 1 #f make-sampler { sf } + :srate sr :input sf fp-input-cb make-src { s } + snd chn #f framples { len } + "%s %s %s %s" #( sr osamp osfrq get-func-name ) string-format { origin } + len 0.0 make-vct map! + s os 0.0 0.0 oscil osamp f* #f src + end-map ( out-data ) 0 len snd chn #f origin vct->channel ( vct ) + sf free-sampler drop ; previous -\ ;;; -------- compand, compand-channel - -: compand <{ -- prc; y self -- val }> - doc" Returns a compander: compand map-channel" - 1 proc-create { prc } - vct( -1.000 -0.960 -0.900 -0.820 -0.720 -0.600 -0.450 -0.250 - 0.000 0.250 0.450 0.600 0.720 0.820 0.900 0.960 1.000 ) , - prc - does> { inval self -- val } - self @ ( tbl ) inval 8.0 f* 8.0 f+ ( index ) 17 array-interp -; - -: compand-channel <{ :optional beg 0 dur #f snd #f chn #f edpos #f -- val }> - doc" Applies a standard compander to sound." - compand beg dur snd chn edpos #t #f $" %s %s %s" #( beg dur get-func-name ) format ptree-channel -; +vct( -1.000 -0.960 -0.900 -0.820 + -0.720 -0.600 -0.450 -0.250 + 0.000 0.250 0.450 0.600 + 0.720 0.820 0.900 0.960 1.000 ) constant compand-table -: compand-sound <{ :optional beg 0 dur #f snd #f -- }> - doc" Applies companding to every channel of SND." - snd snd-snd to snd - snd sound? if - snd channels 0 ?do - beg dur snd i ( chn ) #f compand-channel drop - loop - else - 'no-such-sound #( get-func-name snd ) fth-throw - then +: compand ( -- prc; y self -- val ) + doc" Return compander: compand map-channel." + 1 proc-create ( prc ) + does> { y self -- val } + compand-table y 8.0 f* 8.0 f+ compand-table vct-length array-interp ; \ ;;; -------- shift pitch keeping duration constant \ ;;; -\ ;;; both src and granulate take a function argument to get input whenever it is needed. -\ ;;; in this case, src calls granulate which reads the currently selected file. -\ ;;; CLM version is in expsrc.ins +\ ;;; both src and granulate take a function argument to get input +\ ;;; whenever it is needed. +\ ;;; In this case, src calls granulate which reads the currently +\ ;;; selected file. CLM version is in expsrc.ins hide : expgr-cb { v snd chn -- prc; dir self -- val } - 1 proc-create v , snd , chn , 0 , ( prc ) - does> { dir self -- val } - self @ { v } - v cycle-ref ( val ) - v cycle-start@ 0= if - self cell+ @ { snd } - self 2 cells + @ { chn } - self 3 cells + @ { vbeg } - vbeg v vct-length + dup self 3 cells + ! ( vbeg += v-len ) - vbeg v vct-length snd chn #f channel->vct drop - then - ( val ) + 1 proc-create ( prc ) + v , snd , chn , 0 , + does> { dir self -- val } + self @ { v } + v cycle-ref ( val ) + v cycle-start@ 0= if + self cell+ @ { snd } + self 2 cells + @ { chn } + self 3 cells + @ { vbeg } + vbeg v vct-length + dup self 3 cells + ! ( vbeg += v-len ) + vbeg v vct-length snd chn #f channel->vct drop + then + ( val ) ; + : expsr-cb { gr -- prc; dir self -- val } - 1 proc-create gr , ( prc ) - does> { dir self -- val } - self @ ( gr ) #f #f granulate + 1 proc-create ( prc ) + gr , + does> { dir self -- val } + self @ ( gr ) #f #f granulate ; set-current + : expsrc <{ rate :optional snd #f chn #f -- val }> - doc" Uses sampling-rate conversion and granular synthesis to produce a sound \ -at a new pitch but at the original tempo. \ + doc" Use sampling-rate conversion and granular synthesis to \ +produce a sound at a new pitch but at the original tempo. \ It returns a function for map-channel." - 0 1024 snd chn #f channel->vct { v } - :input v snd chn expgr-cb :expansion rate make-granulate { gr } - :input gr expsr-cb :srate rate make-src { sr } - 1 proc-create sr , ( prc ) - does> { y self -- val } - self @ ( sr ) 0.0 #f src + 0 1024 snd chn #f channel->vct { v } + :input v snd chn expgr-cb :expansion rate make-granulate { gr } + :input gr expsr-cb :srate rate make-src { sr } + 1 proc-create ( prc ) + sr , + does> { y self -- val } + self @ ( sr ) 0.0 #f src ; previous -\ ;;; the next (expsnd) changes the tempo according to an envelope; the new duration -\ ;;; will depend on the expansion envelope -- we integrate it to get +\ ;;; the next (expsnd) changes the tempo according to an envelope; the new +\ ;;; duration will depend on the expansion envelope -- we integrate it to get \ ;;; the overall expansion, then use that to decide the new length. hide : es-input-cb { sf -- prc; dir self -- val } - 1 proc-create sf , - does> { dir self -- val } - self @ ( sf ) next-sample + 1 proc-create ( prc ) + sf , + does> { dir self -- val } + self @ ( sf ) next-sample ; set-current + : expsnd <{ gr-env :optional snd #f chn #f -- vct }> - doc" Uses the granulate generator to change tempo according to an envelope:\n\ -#( 0 0.5 2 2.0 ) #f #f expsnd" - snd chn #f frames { len } - len snd srate f/ gr-env integrate-envelope f* gr-env envelope-last-x f/ { dur } - 0 snd chn 1 #f make-sampler { sf } - :expansion gr-env 1 array-ref :jitter 0 :input sf es-input-cb make-granulate { gr } - :envelope gr-env :duration dur make-env { ge } - snd srate dur f* fround->s { sound-len } - sound-len len max to len - $" %s %s" #( gr-env get-func-name ) string-format { origin } - len 0.0 make-vct map! - gr #f granulate ( val ) - gr ge env set-mus-increment drop - end-map ( out-data ) 0 len snd chn #f origin vct->channel - sf free-sampler drop + doc" Use the granulate generator to change tempo \ +according to an envelope:\n\ +#( 0 0.5 2 2.0 ) #f #f expsnd." + snd chn #f framples { len } + len snd srate f/ gr-env integrate-envelope f* + gr-env envelope-last-x f/ { dur } + 0 snd chn 1 #f make-sampler { sf } + :expansion gr-env 1 array-ref + :jitter 0 + :input sf es-input-cb make-granulate { gr } + :envelope gr-env :duration dur make-env { ge } + snd srate dur f* fround->s { sound-len } + sound-len len max to len + "%s %s" #( gr-env get-func-name ) string-format { origin } + len 0.0 make-vct map! + gr #f granulate ( val ) + gr ge env set-mus-increment drop + end-map ( out-data ) 0 len snd chn #f origin vct->channel ( vct ) + sf free-sampler drop ; previous @@ -1415,92 +1603,117 @@ previous \ ;;; CLM version is in sndclm.html : cross-synthesis ( cross-snd amp fftsize r -- prc; y self -- val ) - doc" Does cross-synthesis between CROSS-SND (a sound index) and the currently selected sound:\n\ -1 0.5 128 6.0 cross-synthesis map-channel" - { cross-snd amp fftsize r } - fftsize 2/ { freq-inc } - fftsize 0.0 make-vct { fdr } - fftsize 0.0 make-vct { fdi } - freq-inc 0.0 make-vct { spectr } - 1.0 r fftsize f/ f- { radius } - #f srate fftsize / { bin } - freq-inc make-array map! i bin * radius make-formant end-map { formants } - 1 proc-create fdr , fdi , spectr , formants , amp , freq-inc , cross-snd , fftsize , 0 , ( prc ) - does> { y self -- val } - self @ { fdr } - self cell+ @ { fdi } - self 2 cells + @ { spectr } - self 3 cells + @ { formants } - self 4 cells + @ { amp } - self 5 cells + @ { ctr } - ctr formants length = if - self 6 cells + @ { cross-snd } - self 7 cells + @ { fftsize } - self 8 cells + @ { inctr } - inctr fftsize cross-snd 0 #f channel->vct dup self ! to fdr - inctr fftsize 2/ + self 8 cells + ! ( inctr += freq-inc ) - fdr fdi #f 2 spectrum ( fdr ) spectr vct-subtract! ( fdr ) fftsize 2/ 1/f vct-scale! drop - 0 self 5 cells + ! ( ctr = 0 ) - then - 1 self 5 cells + +! ( ctr++ ) - formants cycle-ref drop - spectr fdr vct-add! ( spectr ) formants y formant-bank amp f* + doc" Do cross-synthesis between CROSS-SND (a sound index) \ +and the currently selected sound:\n\ +1 0.5 128 6.0 cross-synthesis map-channel." + { cross-snd amp fftsize r } + fftsize 2/ { freq-inc } + fftsize 0.0 make-vct { fdr } + fftsize 0.0 make-vct { fdi } + freq-inc 0.0 make-vct { spectr } + 1.0 r fftsize f/ f- { radius } + #f srate fftsize / { bin } + freq-inc make-array map! + i bin * radius make-formant + end-map spectr make-formant-bank { formants } + 1 proc-create ( prc ) + fdr , fdi , spectr , formants , + amp , freq-inc , cross-snd , fftsize , 0 , + does> { y self -- val } + self @ { fdr } + self cell+ @ { fdi } + self 2 cells + @ { spectr } + self 3 cells + @ { formants } + self 4 cells + @ { amp } + self 5 cells + @ { ctr } + ctr formants length = if + self 6 cells + @ { cross-snd } + self 7 cells + @ { fftsize } + self 8 cells + @ { inctr } + inctr fftsize cross-snd 0 #f channel->vct dup self ! to fdr + inctr fftsize 2/ + self 8 cells + ! ( inctr += freq-inc ) + fdr fdi #f 2 spectrum ( fdr ) + spectr vct-subtract! ( fdr ) fftsize 2/ 1/f + vct-scale! drop + 0 self 5 cells + ! ( ctr = 0 ) + then + 1 self 5 cells + +! ( ctr++ ) + spectr fdr 0 vct-add! drop + formants y formant-bank amp f* ; : voiced->unvoiced <{ amp fftsize r tempo :optional snd #f chn #f -- vct }> - doc" Turns a vocal sound into whispering: 1.0 256 2.0 2.0 #f #f voiced->unvoiced" - fftsize 2/ { freq-inc } - nil { fdr } - fftsize 0.0 make-vct { fdi } - freq-inc 0.0 make-vct { spectr } - snd srate 3.0 f/ make-rand { noi } - 0 { inctr } - 1.0 r fftsize f/ f- { radius } - snd srate fftsize / { bin } - snd chn #f frames { len } - len tempo f/ fround->s len max { out-len } - freq-inc tempo f* fround->s { hop } - 0.0 0.0 { old-peak-amp new-peak-amp } - $" %s %s %s %s %s" #( amp fftsize r tempo get-func-name ) string-format { origin } - freq-inc make-array map! i bin * radius make-formant end-map { formants } - out-len 0.0 make-vct map! - i freq-inc mod 0= if - inctr fftsize snd chn #f channel->vct to fdr - fdr vct-peak old-peak-amp fmax to old-peak-amp - fdr fdi #f 2 spectrum ( fdr ) spectr vct-subtract! ( fdr ) freq-inc 1/f vct-scale! drop - hop inctr + to inctr - then - spectr fdr vct? if fdr vct-add! then formants noi 0.0 rand formant-bank ( outval ) - dup fabs new-peak-amp fmax to new-peak-amp - ( outval ) - end-map old-peak-amp new-peak-amp f/ amp f* vct-scale! 0 out-len snd chn #f origin vct->channel -; - -: pulse-voice <{ cosines :optional freq 440.0 amp 1.0 fftsize 256 r 2.0 snd #f chn #f -- vct }> - doc" Uses sum-of-cosines to manipulate speech sounds." - fftsize 2/ { freq-inc } - fftsize 0.0 make-vct { fdr } - fftsize 0.0 make-vct { fdi } - freq-inc 0.0 make-vct { spectr } - :cosines cosines :frequency freq make-sum-of-cosines { pulse } - 0 { inctr } - 1.0 r fftsize f/ f- { radius } - snd srate fftsize / { bin } - snd chn #f frames { len } - 0.0 0.0 { old-peak-amp new-peak-amp } - $" %s %s %s %s %s %s" #( cosines freq amp fftsize r get-func-name ) string-format { origin } - freq-inc make-array map! i bin * radius make-formant end-map { formants } - len 0.0 make-vct map! - i freq-inc mod 0= if - inctr fftsize snd chn #f channel->vct to fdr - fdr vct-peak old-peak-amp fmax to old-peak-amp - fdr fdi #f 2 spectrum ( fdr ) spectr vct-subtract! ( fdr ) freq-inc 1/f vct-scale! drop - freq-inc inctr + to inctr - then - spectr fdr vct-add! ( spectr ) formants pulse 0.0 sum-of-cosines formant-bank ( outval ) - dup fabs new-peak-amp fmax to new-peak-amp - ( outval ) - end-map old-peak-amp new-peak-amp f/ amp f* vct-scale! 0 len snd chn #f origin vct->channel + doc" Turn vocal sound into \ +whispering: 1.0 256 2.0 2.0 #f #f voiced->unvoiced." + fftsize 2/ { freq-inc } + nil { fdr } + fftsize 0.0 make-vct { fdi } + freq-inc 0.0 make-vct { spectr } + snd srate 3.0 f/ make-rand { noi } + 0 { inctr } + 1.0 r fftsize f/ f- { radius } + snd srate fftsize / { bin } + snd chn #f framples { len } + len tempo f/ fround->s len max { out-len } + freq-inc tempo f* fround->s { hop } + 0.0 0.0 { old-peak-amp new-peak-amp } + "%s %s %s %s %s" + #( amp fftsize r tempo get-func-name ) string-format { origin } + freq-inc make-array map! + i bin * radius make-formant + end-map ( formants ) spectr make-formant-bank { formants } + freq-inc 1/f { 1/freq-inc } + + out-len 0.0 make-vct map! + i freq-inc mod 0= if + inctr fftsize snd chn #f channel->vct to fdr + fdr vct-peak old-peak-amp fmax to old-peak-amp + fdr fdi #f 2 spectrum ( fdr ) + spectr vct-subtract! ( fdr ) + 1/freq-inc vct-scale! drop + hop inctr + to inctr + then + spectr fdr 0 vct-add! drop + formants noi 0.0 rand formant-bank ( outval ) + end-map { out-data } + out-data old-peak-amp out-data vct-peak f/ amp f* vct-scale! ( odata ) + 0 out-len snd chn #f origin vct->channel ( odata ) +; + +: pulse-voice + <{ co :optional freq 440.0 amp 1.0 fftsize 256 r 2.0 snd #f chn #f -- vct }> + doc" Use sum-of-cosines to manipulate speech sounds." + fftsize 2/ { freq-inc } + fftsize 0.0 make-vct { fdr } + fftsize 0.0 make-vct { fdi } + freq-inc 0.0 make-vct { spectr } + :cosines co :frequency freq make-sum-of-cosines { pulse } + 0 { inctr } + 1.0 r fftsize f/ f- { radius } + snd srate fftsize / { bin } + snd chn #f framples { len } + 0.0 0.0 { old-peak-amp new-peak-amp } + "%s %s %s %s %s %s" + #( co freq amp fftsize r get-func-name ) string-format { origin } + freq-inc make-array map! + i bin * radius make-formant + end-map spectr make-formant-bank { formants } + len 0.0 make-vct map! + i freq-inc mod 0= if + inctr fftsize snd chn #f channel->vct to fdr + fdr vct-peak old-peak-amp fmax to old-peak-amp + fdr fdi #f 2 spectrum ( fdr ) + spectr vct-subtract! ( fdr ) + freq-inc 1/f vct-scale! drop + freq-inc inctr + to inctr + then + spectr fdr 0 vct-add! drop + formants pulse 0.0 sum-of-cosines formant-bank ( outval ) + dup fabs new-peak-amp fmax to new-peak-amp + ( outval ) + end-map + old-peak-amp new-peak-amp f/ amp f* vct-scale! + 0 len snd chn #f origin vct->channel ( vct ) ; \ 20.0 1.0 1024 0.01 pulse-voice \ 120.0 1.0 1024 0.2 pulse-voice @@ -1509,200 +1722,175 @@ previous \ 1000.0 1.0 512 pulse-voice 'snd-nogui provided? [unless] - \ ;;; -------- convolution example - - hide - : cnv-cb { sf -- prc; dir self -- val } - 1 proc-create sf , ( prc ) - does> { dir self -- val } - self @ ( sf ) next-sample - ; - set-current - : cnvtest ( snd0 snd1 amp -- mx ) - doc" Convolves SND0 and SND1, scaling by AMP, returns new max amp: 0 1 0.1 cnvtest" - { snd0 snd1 amp } - snd0 #f #f frames { flt-len } - snd1 #f #f frames flt-len + { total-len } - 0 snd1 0 1 #f make-sampler { sf } - :input sf cnv-cb :filter 0 flt-len snd0 #f #f channel->vct make-convolve { cnv } - total-len 0.0 make-vct map! cnv #f convolve end-map amp vct-scale! ( out-data ) - 0 total-len snd1 #f #f get-func-name vct->channel vct-peak { max-samp } - sf free-sampler drop - max-samp 1.0 f> if #( max-samp fnegate max-samp ) snd1 #f set-y-bounds drop then - max-samp - ; - previous + \ ;;; -------- convolution example + + hide + : cnv-cb { sf -- prc; dir self -- val } + 1 proc-create ( prc ) + sf , + does> { dir self -- val } + self @ ( sf ) next-sample + ; + set-current + + : cnvtest ( snd0 snd1 amp -- mx ) + doc" Convolve SND0 and SND1, scaling by AMP, \ +returns new max amp: 0 1 0.1 cnvtest." + { snd0 snd1 amp } + snd0 #f #f framples { flt-len } + snd1 #f #f framples flt-len + { total-len } + 0 snd1 0 1 #f make-sampler { sf } + :input sf cnv-cb :filter 0 flt-len + snd0 #f #f channel->vct make-convolve { cnv } + total-len 0.0 make-vct map! + cnv #f convolve + end-map amp vct-scale! ( out-data ) + 0 total-len snd1 #f #f get-func-name + vct->channel vct-peak { max-samp } + sf free-sampler drop + max-samp 1.0 f> if + #( max-samp fnegate max-samp ) snd1 #f set-y-bounds drop + then + max-samp + ; + previous [then] \ ;;; -------- swap selection chans : swap-selection-channels ( -- ) - doc" Swaps the currently selected data's channels." - undef selection? if - selection-chans 2 = if - selection-position { beg } - selection-frames { len } - #f { snd-chn0 } - #f { snd-chn1 } - all-chans each { lst } - lst 0 array-ref lst 1 array-ref selection-member? if - snd-chn0 false? if - lst to snd-chn0 - else - snd-chn1 false? if - lst to snd-chn1 - leave - then - then + doc" Swap the currently selected data's channels." + undef selection? unless + 'no-active-selection #( get-func-name ) fth-throw + then + selection-chans 2 = if + selection-position { beg } + selection-framples { len } + #f { snd-chn0 } + #f { snd-chn1 } + all-chans each { lst } + lst car lst cadr selection-member? if + snd-chn0 false? if + lst to snd-chn0 + else + snd-chn1 false? if + lst to snd-chn1 + leave + then + then + then + end-each + snd-chn1 if + snd-chn0 car snd-chn0 cadr snd-chn1 car snd-chn1 cadr + beg len #f #f swap-channels drop + else + 'wrong-number-of-channels + #( "%s: needs two channels to swap" + get-func-name ) fth-throw + then + else + 'wrong-number-of-channels + #( "%s: needs a stereo selection (not %s chans)" + get-func-name + selection-chans ) fth-throw then - end-each - snd-chn1 if - snd-chn0 0 array-ref - snd-chn0 1 array-ref - snd-chn1 0 array-ref - snd-chn1 1 array-ref - beg len #f #f swap-channels drop - else - 'wrong-number-of-channels #( get-func-name $" needs two channels to swap" ) fth-throw - then - else - 'wrong-number-of-channels - #( get-func-name $" needs a stereo selection (not %s chans)" selection-chans ) fth-throw - then - else - 'no-active-selection #( get-func-name ) fth-throw - then ; \ ;;; -------- sound interp \ ;;; -\ ;;; make-sound-interp sets up a sound reader that reads a channel at an arbitary location, -\ ;;; interpolating between samples if necessary, the corresponding "generator" is sound-interp +\ ;;; make-sound-interp sets up a sound reader that reads a channel at an +\ ;;; arbitary location, interpolating between samples if necessary, +\ ;;; the corresponding "generator" is sound-interp : make-sound-interp <{ start :optional snd #f chn #f -- prc; loc self -- val }> - doc" Return an interpolating reader for SND's channel CHN." - 2048 { bufsize } - 1 proc-create { prc } - start bufsize snd chn #f channel->vct ( data ) , - start ( curbeg ) , - start bufsize + ( curend ) , - snd ( snd ) , - chn ( chn ) , - prc - does> { loc self -- val } - self @ { data } - self cell+ @ { curbeg } - self 2 cells + @ { curend } - self 3 cells + @ { snd } - self 4 cells + @ { chn } - 2048 { bufsize } - 128 { buf4size } - loc fround->s to loc - loc curbeg < if - \ get previous buffer - loc bufsize - buf4size + 0 max to curbeg - curbeg bufsize + to curend - curbeg bufsize snd chn #f channel->vct to data - else - loc curend > if - \ get next buffer - loc buf4size - 0 max to curbeg - curbeg bufsize + to curend - curbeg bufsize snd chn #f channel->vct to data - then - then - data self ! - curbeg self cell+ ! - curend self 2 cells + ! - data loc curbeg - bufsize array-interp + doc" Return an interpolating reader for SND's channel CHN." + 0 #f snd chn #f channel->vct { data } + 1 proc-create ( prc ) + data , data vct-length , + does> { loc self -- val } + self @ ( data ) loc self cell+ @ ( size ) array-interp ; : sound-interp { func loc -- val } - doc" Return sample at LOC (interpolated if necessary) from FUNC created by make-sound-interp." - loc func execute -; - -\ ;; env-sound-interp takes an envelope that goes between 0 and 1 (y-axis), and a time-scaler -\ ;; (1.0 = original length) and returns a new version of the data in the specified channel -\ ;; that follows that envelope (that is, when the envelope is 0 we get sample 0, when the -\ ;; envelope is 1 we get the last sample, envelope = .5 we get the middle sample of the -\ ;; sound and so on. (env-sound-interp #(0 0 1 1)) will return a copy of the -\ ;; current sound; (env-sound-interp #(0 0 1 1 2 0) 2.0) will return a new sound -\ ;; with the sound copied first in normal order, then reversed. src-sound with an -\ ;; envelope could be used for this effect, but it is much more direct to apply the -\ ;; envelope to sound sample positions. - -: env-sound-interp <{ envelope :optional time-scale 1.0 snd #f chn #f -- file-name }> - doc" Reads SND's channel CHN according to ENVELOPE and TIME-SCALE." - snd chn #f frames { len } - time-scale len f* fround->s { newlen } - 0 snd chn make-sound-interp { reader } - :envelope envelope :length newlen :scaler len make-env { read-env } - snd-tempnam { tempfilename } - tempfilename snd srate 1 #f mus-next get-func-name mus-sound-open-output { fil } - 8192 { bufsize } - 1 bufsize make-sound-data { data } - newlen 0 ?do - bufsize 0 do data 0 i reader read-env env sound-interp sound-data-set! drop loop - fil 0 bufsize 1- 1 data mus-sound-write drop - bufsize +loop - newlen bufsize mod ?dup-if fil 0 rot 1- 1 data mus-sound-write drop then - fil newlen 4 * mus-sound-close-output drop - $" %s %s %s" #( envelope time-scale get-func-name ) string-format { origin } - 0 newlen tempfilename snd chn #t ( truncate ) origin set-samples ( file-name ) - tempfilename file-delete -; - -: granulated-sound-interp <{ envelope - :optional time-scale 1.0 grain-length 0.1 grain-envelope #( 0 0 1 1 2 1 3 0 ) output-hop 0.05 - snd #f chn #f -- file-name }> - snd chn #f frames { len } - time-scale len f* fround->s { newlen } - :envelope envelope :length newlen :scaler len make-env { read-env } - snd-tempnam { tempfilename } - \ ;; #f as data-format -> format compatible with sndlib (so no data translation is needed) - tempfilename snd srate 1 #f mus-next get-func-name mus-sound-open-output { fil } - grain-length snd srate f* fround->s { grain-frames } - output-hop snd srate f* fround->s { hop-frames } - grain-length output-hop f/ fround->s 1+ { num-reader } - num-reader make-array { readers } - num-reader make-array map! - :envelope grain-envelope :length grain-frames make-env - end-map { grain-envs } - 0 { next-reader-start-at } - 8192 { bufsize } - 1 bufsize make-sound-data { data } - 0 { data-ctr } - snd srate 0.005 f* { jitter } - newlen 0 ?do - bufsize 0 do - read-env env { position-in-original } - j next-reader-start-at >= if - readers cycle-start@ { next-reader } - readers - position-in-original jitter mus-random f+ fround->s 0 max snd chn 1 #f make-sampler - cycle-set! - grain-envs next-reader array-ref mus-reset drop - hop-frames next-reader-start-at + to next-reader-start-at - then - 0.0 ( sum ) - readers each { rd } - rd sampler? if - grain-envs i array-ref env rd next-sample f* f+ ( sum += ... ) + doc" Return sample at LOC (interpolated if necessary) \ +from FUNC created by make-sound-interp." + loc func execute +; + +\ ;; env-sound-interp takes an envelope that goes between 0 and 1 +\ ;; (y-axis), and a time-scaler (1.0 = original length) and returns a +\ ;; new version of the data in the specified channel that follows that +\ ;; envelope (that is, when the envelope is 0 we get sample 0, when the +\ ;; envelope is 1 we get the last sample, envelope = .5 we get the middle +\ ;; sample of the sound and so on. (env-sound-interp #(0 0 1 1)) will +\ ;; return a copy of the current sound; (env-sound-interp #(0 0 1 1 2 0) +\ ;; 2.0) will return a new sound with the sound copied first in normal +\ ;; order, then reversed. src-sound with an envelope could be used +\ ;; for this effect, but it is much more direct to apply the envelope +\ ;; to sound sample positions. + +: env-sound-interp <{ en :optional time-scale 1.0 snd #f chn #f -- vct }> + doc" Read SND's channel CHN according to EN and TIME-SCALE." + snd chn #f framples { len } + time-scale len f* fround->s { newlen } + :envelope en :length newlen 1+ :scaler len make-env { read-env } + 0 #f snd chn #f channel->vct { data } + newlen 0.0 make-vct map! + data read-env env len array-interp + end-map { new-snd } + "%s %s %s" #( en time-scale get-func-name ) string-format { origin } + 0 newlen new-snd snd chn #t origin + 0 current-edit-position #t set-samples ( vct ) +; +\ #( 0 0 1 1 2 0 ) 2.0 env-sound-interp + +: granulated-sound-interp + <{ en :optional tscl 1.0 grlen 0.1 gren #f ohop 0.05 snd #f chn #f -- vct }> + doc" Read the given channel following EN (as env-sound-interp), \ +using grains to create the re-tempo'd read." + gren empty? if + #( 0 0 1 1 2 1 3 0 ) to gren then - end-each { sum } - data 0 i sum sound-data-set! drop - loop - fil 0 bufsize 1- 1 data mus-sound-write drop - bufsize +loop - newlen bufsize mod ?dup-if fil 0 rot 1- 1 data mus-sound-write drop then - fil newlen 4 * mus-sound-close-output drop - $" %s %s %s %s %s %s" - #( envelope time-scale grain-length grain-envelope output-hop get-func-name ) - string-format { origin } - 0 newlen tempfilename snd chn #t ( truncate ) origin set-samples ( file-name ) - tempfilename file-delete + snd chn #f framples { len } + tscl len f* fround->s { newlen } + :envelope en :length newlen :scaler len make-env { read-env } + snd srate { sr } + grlen sr f* fround->s { grain-framples } + ohop sr f* fround->s { hop-framples } + grlen ohop f/ fceil->s { num-readers } + 0 0 { cur-readers next-reader } + sr 0.005 f* { jitter } + num-readers make-array { readers } + num-readers make-array map! + :envelope gren :length grain-framples make-env + end-map { grain-envs } + newlen 0.0 make-vct { new-snd } + newlen 0 ?do + read-env i set-mus-location drop + read-env env { position-in-original } + position-in-original jitter mus-random f+ fround->s 0 max { mx } + mx snd chn 1 #f make-sampler { srd } + readers srd cycle-set! + grain-envs next-reader array-ref mus-reset drop + readers cycle-start@ to next-reader + cur-readers next-reader < if + next-reader to cur-readers + then + cur-readers 0 ?do + grain-envs i array-ref { e } + readers i array-ref { rd } + \ j is index from outer loop + newlen hop-framples j + min j ?do + new-snd i e env rd next-sample f* vct-set! drop + loop + loop + hop-framples +loop + "%s %s %s %s %s %s" + #( en tscl grlen gren ohop get-func-name ) string-format { origin } + 0 newlen new-snd snd chn #t origin + 0 current-edit-position #t set-samples ( vct ) ; + \ #( 0 0 1 .1 2 1 ) 1.0 0.2 #( 0 0 1 1 2 0 ) granulated-sound-interp \ #( 0 0 1 1 ) 2.0 granulated-sound-interp \ #( 0 0 1 .1 2 1 ) 1.0 0.2 #( 0 0 1 1 2 0 ) 0.02 granulated-sound-interp @@ -1711,387 +1899,341 @@ previous hide : fe-cb { flt amp-env -- prc; y self -- val } - 1 proc-create flt , amp-env , - does> { y self -- val } - self @ { flt } - self cell+ @ ( amp-env ) env { env-val } - flt 0 env-val set-mus-xcoeff drop - flt 1 env-val 1.0 f- set-mus-xcoeff drop - flt env-val y f* one-pole + 1 proc-create ( prc ) + flt , amp-env , + does> { y self -- val } + self @ { flt } + self cell+ @ ( amp-env ) env { env-val } + flt 0 env-val set-mus-xcoeff drop + flt 1 env-val 1.0 f- set-mus-xcoeff drop + flt env-val y f* one-pole ; set-current + : filtered-env <{ e :optional snd #f chn #f -- val }> - doc" It's a time-varying one-pole filter: \ -when env is at 1.0, no filtering, as env moves to 0.0, low-pass gets more intense; \ + doc" It's a time-varying one-pole filter: \ +when env is at 1.0, no filtering, as env moves to 0.0, \ +low-pass gets more intense; \ amplitude and low-pass amount move together." - 1.0 0.0 make-one-pole { flt } - :envelope e :length snd chn #f frames make-env { amp-env } - flt amp-env fe-cb 0 #f snd chn #f $" %s %s" #( e get-func-name ) string-format map-channel + 1.0 0.0 make-one-pole { flt } + :envelope e :length snd chn #f framples make-env { amp-env } + flt amp-env fe-cb 0 #f snd chn #f + "%s %s" #( e get-func-name ) string-format map-channel ; previous -\ ;;; -------- C-x b support: hide all but one of the current sounds (more like Emacs) +\ ;;; -------- C-x b support: hide all but one of the current sounds +\ ;;; (more like Emacs) hide #f value xb-last-buffer #f value xb-current-buffer 0 value xb-last-width 0 value xb-last-height +set-current + : open-current-buffer { width heigth -- } - width to xb-last-width - heigth to xb-last-height - xb-current-buffer 0 array-ref sound-widgets 0 array-ref { sound-pane } - sound-pane if - sound-pane show-widget drop - sound-pane #( width heigth ) set-widget-size drop - xb-current-buffer 0 array-ref select-sound drop - xb-current-buffer 1 array-ref select-channel drop - then -; -: close-all-buffers ( -- ) sounds each ( s ) sound-widgets 0 array-ref hide-widget drop end-each ; -: stb-cb <{ response -- f }> - xb-current-buffer 0 array-ref - sound-widgets 0 array-ref - widget-size dup 0 array-ref - swap 1 array-ref { width height } - response string? not response empty? || if - xb-current-buffer { temp } - xb-last-buffer if - xb-last-buffer to xb-current-buffer - else - :file undef - :header-type undef - :data-format undef - :srate undef - :channels undef - :comment undef - :size undef new-sound { index } - #( index 0 ) to xb-current-buffer - then - temp to xb-last-buffer - else - response find-file dup false? if drop "" then 0 find-sound { index } - index sound? if - xb-current-buffer to xb-last-buffer - #( index 0 ) to xb-current-buffer - else - $" can't find %s" #( response ) string-format #f #f report-in-minibuffer drop - 1 sleep - then - then - close-all-buffers - "" #f #f report-in-minibuffer drop - width height open-current-buffer - #f + width to xb-last-width + heigth to xb-last-height + xb-current-buffer 0 array-ref sound-widgets 0 array-ref { sound-pane } + sound-pane if + sound-pane show-widget drop + sound-pane #( width heigth ) set-widget-size drop + xb-current-buffer 0 array-ref select-sound drop + xb-current-buffer 1 array-ref select-channel drop + then ; -set-current -: switch-to-buffer <{ -- val }> - "" { default } - xb-last-buffer array? if - xb-last-buffer 0 array-ref short-file-name to default - $" switch to buffer: " - else - $" (make new sound) " - then { msg } - default #f undef report-in-minibuffer drop - msg <'> stb-cb #f #t prompt-in-minibuffer -; -: xb-close <{ snd -- val }> - xb-current-buffer array? - xb-current-buffer 0 array-ref snd = && if - xb-current-buffer 0 array-ref { closer } - close-all-buffers - xb-last-buffer if - xb-last-buffer - else - sounds if #f else #( sounds 0 array-ref 0 ) then - then to xb-current-buffer - #f sounds each { n } - n closer = - xb-current-buffer false? - xb-current-buffer 0 array-ref n = || && if - drop ( #f ) - #( n 0 ) - leave - then - end-each to xb-last-buffer - xb-current-buffer if xb-last-width xb-last-height open-current-buffer then - then - #f -; -: xb-open <{ snd -- val }> - close-all-buffers - xb-current-buffer to xb-last-buffer - #( snd 0 ) to xb-current-buffer - xb-last-width 0= if window-width else xb-last-width then - xb-last-height 0= if window-height 10 - else xb-last-height then open-current-buffer +: close-all-buffers ( -- ) + sounds each ( s ) + sound-widgets 0 array-ref hide-widget drop + end-each ; previous -\ C-x b -\ "b" 0 <'> switch-to-buffer #t "Switch to buffer" "switch-to-buffer" bind-key drop -\ after-open-hook <'> xb-open add-hook! -\ close-hook <'> xb-close add-hook! - \ ;;; -------- remove-clicks : find-click ( loc -- pos ) - doc" Finds the next click starting at LOC." - { loc } - loc #f #f 1 #f make-sampler { rd } - 0.0 0.0 0.0 { samp0 samp1 samp2 } - 10 0.0 make-vct { samps } - #f \ flag - #f #f #f frames loc ?do - samp1 to samp0 - samp2 to samp1 - rd next-sample to samp2 - samps samp0 cycle-set! - samps vct-peak 0.1 fmax { local-max } - samp0 samp1 f- fabs local-max f> - samp1 samp2 f- fabs local-max f> && - samp0 samp2 f- fabs local-max f2/ f< && if drop ( flag ) i leave then - loop + doc" Find the next click starting at LOC." + { loc } + loc #f #f 1 #f make-sampler { rd } + 0.0 0.0 0.0 { samp0 samp1 samp2 } + 10 0.0 make-vct { samps } + #f \ flag + #f #f #f framples loc ?do + samp1 to samp0 + samp2 to samp1 + rd next-sample to samp2 + samps samp0 cycle-set! + samps vct-peak 0.1 fmax { local-max } + samp0 samp1 f- fabs local-max f> + samp1 samp2 f- fabs local-max f> && + samp0 samp2 f- fabs local-max f2/ f< && if + drop ( flag ) + i + leave + then + loop ; : remove-clicks ( -- ) - doc" Tries to find and smooth-over clicks." - -2 { click } - begin - click 2+ find-click to click - click - while - click 2- 4 #f #f smooth-sound drop - repeat -; - -: search-for-click ( -- pos ) - doc" Looks for the next click (for use with C-s)" - 1 proc-create 10 0.0 make-vct , 0.0 , 0.0 , 0.0 , ( prc ) - does> { val self -- f } - self @ { samps } - self cell+ @ { samp0 } - self 2 cells + @ { samp1 } - self 3 cells + @ { samp2 } - samp1 to samp0 - samp2 to samp1 - val to samp2 - samp0 self cell+ ! - samp1 self 2 cells + ! - samp2 self 3 cells + ! - samps samp0 cycle-set! - samps vct-peak 0.1 fmax { local-max } - samp0 samp1 f- fabs local-max f>= - samp1 samp2 f- fabs local-max f>= && - samp0 samp2 f- fabs local-max f2/ f<= && if - -1 - else - #f - then + doc" Try to find and smooth-over clicks." + -2 { click } + begin + click 2+ find-click to click + click + while + click 2- 4 #f #f smooth-sound drop + repeat +; + +: search-for-click ( -- prc; val self -- pos ) + doc" Look for the next click (for use with C-s)." + 1 proc-create ( prc ) + 10 0.0 make-vct , 0.0 , 0.0 , 0.0 , + does> { val self -- pos } + self @ { samps } + self cell+ @ { samp0 } + self 2 cells + @ { samp1 } + self 3 cells + @ { samp2 } + samp1 to samp0 + samp2 to samp1 + val to samp2 + samp0 self cell+ ! + samp1 self 2 cells + ! + samp2 self 3 cells + ! + samps samp0 cycle-set! + samps vct-peak 0.1 fmax { local-max } + samp0 samp1 f- fabs local-max f>= + samp1 samp2 f- fabs local-max f>= && + samp0 samp2 f- fabs local-max f2/ f<= && if + -1 + else + #f + then ; : zero+ ( -- prc; n self -- val ) - doc" Finds the next positive-going zero crossing (if searching forward) (for use with C-s)" - 1 proc-create 0.0 ( lastn ) , - does> { n self -- val } - self @ ( lastn ) f0< n f0>= && -1 && ( rtn ) - n self ! ( lastn = n ) - ( rtn ) + doc" Find the next positive-going zero crossing (if \ +searching forward) (for use with C-s)." + 1 proc-create ( prc ) + 0.0 ( lastn ) , + does> { n self -- val } + self @ ( lastn ) f0< n f0>= && -1 && ( val ) + n self ! ( lastn = n ) + ( val ) ; : next-peak ( -- prc; n self -- val ) - doc" Finds the next max or min point in the time-domain waveform (for use with C-s)" - 1 proc-create ( last0 ) #f , ( last1 ) #f , - does> { n self -- val } - self @ { last0 } - self cell+ @ { last1 } - last0 number? - last0 last1 f< last1 n f> && - last0 last1 f> last1 n f< && || && - -1 && ( rtn ) - last1 self ! ( last0 = last1 ) - n self cell+ ! ( last1 = n ) - ( rtn ) + doc" Find the next max or min point in the \ +time-domain waveform (for use with C-s)." + 1 proc-create ( prc ) + ( last0 ) #f , ( last1 ) #f , + does> { n self -- val } + self @ { last0 } + self cell+ @ { last1 } + last0 number? + last0 last1 f< last1 n f> && + last0 last1 f> last1 n f< && || && + -1 && ( val ) + last1 self ! ( last0 = last1 ) + n self cell+ ! ( last1 = n ) + ( val ) ; : find-pitch ( pitch -- prc; y self -- val ) - doc" Finds the point in the current sound where PITCH (in Hz) predominates:\n\ + doc" Find the point in the current sound \ +where PITCH (in Hz) predominates:\n\ C-s 300 find-pitch\n\ -In most cases, this will be slightly offset from the true beginning of the note." - { pitch } - 1 proc-create #f #f transform-size 0.0 make-vct , pitch , ( prc ) - does> { n self -- val } - self @ { data } - self cell+ @ { pitch } - data n cycle-set! - data cycle-start@ 0= if - data vct-peak 0.001 f> if - data rectangular-window data length #t 0.0 undef #t snd-spectrum { spectr } - 10.0 flog { log10 } - 0.0 0 { pk pkloc } - data length 2/ 0 ?do - spectr i vct-ref dup pk f> if - ( val ) to pk - i to pkloc +In most cases, this will be slightly offset from the true \ +beginning of the note." + { pitch } + 1 proc-create ( prc ) + #f #f transform-size 0.0 make-vct , pitch , + does> { y self -- val } + self @ { data } + self cell+ @ { pitch } + data y cycle-set! + data cycle-start@ 0= if + data vct-peak 0.001 f> if + data rectangular-window data length + #t 0.0 undef #t snd-spectrum { spectr } + 10.0 flog { log10 } + 0.0 0 { pk pkloc } + data length 2/ 0 ?do + spectr i vct-ref dup pk f> if + ( val ) to pk + i to pkloc + else + ( val ) drop + then + loop + pkloc 0> if + spectr pkloc 1- vct-ref { la } + spectr pkloc vct-ref { ca } + spectr pkloc 1+ vct-ref { ra } + la ca fmax ra fmax 0.001 f* { pk1 } + la 0.0000001 fmax pk1 f/ flog log10 f/ { logla } + ca 0.0000001 fmax pk1 f/ flog log10 f/ { logca } + ra 0.0000001 fmax pk1 f/ flog log10 f/ { logra } + logla logra f- f2/ + logla logra f+ logca f2* f- f/ + else + 0.0 + then pkloc f+ #f srate f* data length f/ { pit } + pitch pit f- fabs #f srate data length f2* f/ f< if + data length 2/ negate + else + #f + then ( val ) + then + data 0.0 vct-fill! drop else - ( val ) drop + #f then - loop - pkloc 0> if - spectr pkloc 1- vct-ref { la } - spectr pkloc vct-ref { ca } - spectr pkloc 1+ vct-ref { ra } - la ca fmax ra fmax 0.001 f* { pk1 } - la 0.0000001 fmax pk1 f/ flog log10 f/ { logla } - ca 0.0000001 fmax pk1 f/ flog log10 f/ { logca } - ra 0.0000001 fmax pk1 f/ flog log10 f/ { logra } - logla logra f- f2/ logla logra f+ logca f2* f- f/ - else - 0.0 - then pkloc f+ #f srate f* data length f/ { pit } - pitch pit f- fabs #f srate data length f2* f/ f< if - data length 2/ negate - else - #f - then ( rtn ) - then - data 0.0 vct-fill! drop - else - #f - then ; \ ;;; -------- file->vct and a sort of cue-list, I think [ifundef] file->vct - : file->vct ( file -- vct ) - doc" Returns a vct with FILE's data." - { file } - file find-file to file - file false? if 'no-such-file #( get-func-name file ) fth-throw then - 0 file undef 1 #f make-sampler { reader } - file mus-sound-frames 0.0 make-vct map! reader next-sample end-map ( data ) - reader free-sampler drop - ; + : file->vct ( file -- vct ) + doc" Return a vct with FILE's data." + { file } + file find-file to file + file unless + 'no-such-file #( "%s: %S" get-func-name file ) fth-throw + then + 0 file undef 1 #f make-sampler { reader } + file mus-sound-framples 0.0 make-vct map! + reader next-sample + end-map ( data ) + reader free-sampler drop + ; [then] hide : an-cb ( notes snd chn -- prc; self -- #f ) - { notes snd chn } - 0 proc-create { prc } - notes , - snd , - chn , - prc - does> { self -- #f } - self @ { notes } - self cell+ @ { snd } - self 2 cells + @ { chn } - snd chn #f cursor { start } - notes each { note } - note 0 array-ref find-file { file } - file false? if 'no-such-file #( "add-notes" file ) fth-throw then - note length 1 > if - note 1 array-ref - else - 0.0 - then { offset } - note length 2 > if - note 2 array-ref - else - 1.0 - then { amp } - snd srate offset f* fround->s start + { beg } - amp 1.0 f<> if - file file->vct amp vct-scale! beg snd chn #f "add-notes" mix-vct drop - else - file beg 0 snd chn #f undef mix drop - then - end-each - #f + { notes snd chn } + 0 proc-create ( prc ) + notes , snd , chn , + does> { self -- #f } + self @ { notes } + self cell+ @ { snd } + self 2 cells + @ { chn } + snd chn #f cursor { start } + notes each { note } + note 0 array-ref find-file { file } + file unless + 'no-such-file #( "add-notes: %S" file ) fth-throw + then + note length 1 > if + note 1 array-ref + else + 0.0 + then { offset } + note length 2 > if + note 2 array-ref + else + 1.0 + then { amp } + snd srate offset f* fround->s start + { beg } + amp 1.0 f<> if + file file->vct amp vct-scale! + beg snd chn #f "add-notes" mix-vct + else + file beg 0 snd chn #f undef mix + then drop + end-each + #f ; set-current + : add-notes <{ notes :optional snd #f chn #f -- #f }> - doc" Adds (mixes) NOTES which is a list of lists of the form:\n\ + doc" Add (mix) NOTES which is a list of lists of the form:\n\ file :optional offset 0.0 amp 1.0\n\ starting at the cursor in the currently selected channel:\n\ -#( #( \"oboe.snd\" ) #( \"pistol.snd\" 1.0 2.0 ) ) add-notes" - notes snd chn an-cb $" %s %s" #( notes get-func-name ) string-format as-one-edit +#( #( \"oboe.snd\" ) #( \"pistol.snd\" 1.0 2.0 ) ) add-notes." + notes snd chn an-cb + "%s %s" #( notes get-func-name ) string-format as-one-edit ; previous hide : rpl-cb { reg -- prc; self -- val } - 0 proc-create reg , ( prc ) - does> { self -- val } - self @ ( reg ) play + 0 proc-create ( prc ) + reg , + does> { self -- val } + self @ ( reg ) play ; set-current + : region-play-list ( data -- ) - doc" DATA is list of lists #( #( time reg ) ... ), TIME in secs, \ + doc" DATA is list of lists #( #( time reg ) ... ), TIME in secs, \ setting up a sort of play list:\n\ -#( #( 0.0 0 ) #( 0.5 1 ) #( 1.0 2 ) #( 1.0 0 ) ) region-play-list" - ( data ) each { tone } - tone 0 array-ref 1000.0 f* fround->s { time } - tone 1 array-ref { region } - region region? if - time region rpl-cb in drop - then - end-each +#( #( 0.0 0 ) #( 0.5 1 ) #( 1.0 2 ) #( 1.0 0 ) ) region-play-list." + ( data ) each { tone } + tone 0 array-ref 1000.0 f* fround->s { time } + tone 1 array-ref { region } + region region? if + time region rpl-cb in drop + then + end-each ; previous : region-play-sequence ( data -- ) - doc" DATA is list of region ids which will be played one after the other:\n\ -#( 0 2 1 ) region-play-sequence" - 0.0 { time } - ( data ) map - *key* { id } - time { cur } - id 0 region-frames id region-srate f/ time f+ to time - #( cur id ) - end-map region-play-list + doc" DATA is list of region ids which will be played \ +one after the other:\n\ +#( 0 2 1 ) region-play-sequence." + 0.0 { time } + ( data ) map + *key* { id } + time { cur } + id 0 region-framples id region-srate f/ time f+ to time + #( cur id ) + end-map region-play-list ; \ ;;; -------- replace-with-selection : replace-with-selection ( -- ) - doc" Replaces the samples from the cursor with the current selection." - #f #f #f cursor { beg } - #f #f selection-frames { len } - beg #f #f insert-selection drop - beg len + len #f #f #f delete-samples drop + doc" Replace the samples from the cursor with the current selection." + #f #f #f cursor { beg } + #f #f selection-framples { len } + beg #f #f insert-selection drop + beg len + len #f #f #f delete-samples drop ; \ ;;; -------- explode-sf2 : explode-sf2 ( -- ) - doc" turns the currently selected soundfont file \ -into a bunch of files of the form sample-name.aif." - #f soundfont-info { lst } - lst length 1- { last } - lst each { vals } - \ #( name start loop-start loop-end ) - vals 0 array-ref { name } - vals 1 array-ref { start } - i last < if - lst i 1+ array-ref 1 array-ref - else - #f #f #f frames - then { end } - vals 2 array-ref start d- { loop-start } - vals 3 array-ref start d- { loop-end } - name ".aif" $+ { filename } - undef selection? if #f #t #f set-selection-member? drop then - #t #f #f set-selection-member? drop - start #f #f set-selection-position drop - end start d- #f #f set-selection-frames drop - :file filename :header-type mus-aifc save-selection drop - filename open-sound { temp } - temp #( loop-start loop-end ) set-sound-loop-info drop - temp close-sound drop - end-each + doc" Turn the currently selected soundfont file into \ +a bunch of files of the form sample-name.aif." + #f soundfont-info { lst } + lst length 1- { last } + lst each { vals } + \ #( name start loop-start loop-end ) + vals 0 array-ref { name } + vals 1 array-ref { start } + i last < if + lst i 1+ array-ref 1 array-ref + else + #f #f #f framples + then { end } + vals 2 array-ref start d- { loop-start } + vals 3 array-ref start d- { loop-end } + name ".aif" $+ { filename } + undef selection? if + #f #t #f set-selection-member? drop + then + #t #f #f set-selection-member? drop + start #f #f set-selection-position drop + end start d- #f #f set-selection-framples drop + :file filename :header-type mus-aifc save-selection drop + filename open-sound { temp } + temp #( loop-start loop-end ) set-sound-loop-info drop + temp close-sound drop + end-each ; \ ;;; -------- open-next-file-in-directory @@ -2100,264 +2242,247 @@ hide #f value nd-last-file-opened \ string #f value nd-current-directory \ string #f value nd-current-sorted-files \ array + : gcf-sort-cb <{ a b -- n }> - a b string< if - -1 - else - a b string> if - 1 - else - 0 - then - then -; -: get-current-files ( dir -- ) - { dir } - dir to nd-current-directory - dir sound-files-in-directory <'> gcf-sort-cb sort to nd-current-sorted-files + a b string< if + -1 + else + a b string> if + 1 + else + 0 + then + then ; + +: get-current-files { dir -- } + dir to nd-current-directory + dir sound-files-in-directory + <'> gcf-sort-cb sort to nd-current-sorted-files +; + : get-current-directory <{ filename -- filename }> - filename to nd-last-file-opened - filename mus-expand-filename file-dirname { new-path } - nd-current-directory string? not - nd-current-directory new-path string= not || if new-path get-current-files then - filename + filename to nd-last-file-opened + filename mus-expand-filename file-dirname { new-path } + nd-current-directory new-path string<> if + new-path get-current-files + then + filename ; set-current + : open-next-file-in-directory ( -- f ) - \ open-hook <'> get-current-directory hook-member? unless - \ open-hook <'> get-current-directory add-hook! - \ then - nd-last-file-opened string? not - sounds nil? not && if - #f snd-snd file-name to nd-last-file-opened - then - nd-current-directory string? unless - sounds nil? if file-pwd else nd-last-file-opened file-dirname then get-current-files - then - nd-current-sorted-files empty? if - 'no-such-file #( get-func-name nd-current-directory ) fth-throw - else - nd-current-sorted-files cycle-ref { next-file } - next-file 0 find-sound if - 'file-already-open #( get-func-name next-file ) fth-throw - else - sounds nil? unless #f snd-snd close-sound drop then - next-file find-file dup if open-sound then drop - then - then - #t + \ open-hook <'> get-current-directory hook-member? unless + \ open-hook <'> get-current-directory add-hook! + \ then + nd-last-file-opened string? not + sounds nil? not && if + #f snd-snd file-name to nd-last-file-opened + then + nd-current-directory string? unless + sounds nil? if + file-pwd + else + nd-last-file-opened file-dirname + then get-current-files + then + nd-current-sorted-files empty? if + 'no-such-file + #( "%s: %s" get-func-name nd-current-directory ) fth-throw + else + nd-current-sorted-files cycle-ref { next-file } + next-file 0 find-sound if + 'file-already-open + #( "%s: %s" get-func-name next-file ) fth-throw + else + sounds nil? unless + #f snd-snd close-sound drop + then + next-file find-file dup if + open-sound + then drop + then + then + #t ; previous hide : mouse-click-to-open-cb <{ snd chn button state x y axis -- f }> - button 2 = if open-next-file-in-directory else #f then + button 2 = if + open-next-file-in-directory + else + #f + then ; set-current + : click-middle-button-to-open-next-file-in-directory ( -- ) - mouse-click-hook <'> mouse-click-to-open-cb add-hook! - open-hook <'> get-current-directory add-hook! + mouse-click-hook <'> mouse-click-to-open-cb add-hook! + open-hook <'> get-current-directory add-hook! ; previous \ ;;; -------- chain-dsps instrument: chain-dsps <{ start dur :optional dsps #() -- }> - dsps map - *key* array? if - :envelope *key* :duration dur make-env - else - *key* - then - end-map { dsp-chain } - start dur nil run-instrument - 0.0 { val } - dsp-chain each { gen } - gen env? if - gen env val f* - else - gen readin? if - gen readin val f+ - else - gen mus-generator? if - gen val 0.0 mus-apply - else - gen #( val ) run-proc - then - then - then to val - end-each - val - end-run + dsps map + *key* array? if + :envelope *key* :duration dur make-env + else + *key* + then + end-map { dsp-chain } + start dur nil run-instrument + 0.0 { val } + dsp-chain each { gen } + gen env? if + gen env val f* + else + gen readin? if + gen readin val f+ + else + gen mus-generator? if + gen val 0.0 mus-apply + else + gen #( val ) run-proc + then + then + then to val + end-each + val + end-run ;instrument hide : cdsps-cb { os1 os2 -- prc; val self -- r } - 1 proc-create os1 , os2 , ( prc ) - does> { val self -- r } - self @ ( osc1 ) val 0.0 oscil - self cell+ @ ( osc2 ) val f2* 0.0 oscil f+ + 1 proc-create ( prc ) + os1 , os2 , + does> { val self -- r } + self @ ( osc1 ) val 0.0 oscil + self cell+ @ ( osc2 ) val f2* 0.0 oscil f+ ; set-current + 0 [if] -lambda: ( -- ) - 440.0 make-oscil { os1 } - 0 1.0 #( #( 0 0 1 1 2 0 ) os1 ) chain-dsps - 0.5 make-one-zero { oz } - "oboe.snd" find-file make-readin { rd } - 0 1.0 #( #( 0 0 1 1 2 0 ) oz rd ) chain-dsps - 220 make-oscil { osc1 } - 440 make-oscil { osc2 } - osc1 osc2 cdsps-cb { cb } - 0 1.0 #( #( 0 0 1 1 2 0 ) cb ) chain-dsps -; with-sound + lambda: ( -- ) + 440.0 make-oscil { os1 } + 0 1.0 #( #( 0 0 1 1 2 0 ) os1 ) chain-dsps + 0.5 make-one-zero { oz } + "oboe.snd" find-file make-readin { rd } + 0 1.0 #( #( 0 0 1 1 2 0 ) oz rd ) chain-dsps + 220 make-oscil { osc1 } + 440 make-oscil { osc2 } + osc1 osc2 cdsps-cb { cb } + 0 1.0 #( #( 0 0 1 1 2 0 ) cb ) chain-dsps + ; with-sound [then] previous -\ ;;; -------- smooth-channel as virtual op - -hide -: scvp3-cb <{ y data forward -- val }> - data 0 vct-ref { angle } - data 1 vct-ref { incr } - data 3 vct-ref data 4 vct-ref data 2 vct-ref angle f+ fcos f* f+ ( val ) - data 0 angle incr forward if f+ else f- then vct-set! drop ( val ) -; -: scvp1-cb { data -- prc1; frag-beg frag-dur self -- vct } - 2 proc-create { prc } data , prc - does> { frag-beg frag-dur self -- vct } - self @ { data } - pi frag-dur f/ { incr } - data 1 incr vct-set! drop - data 0 frag-beg incr f* vct-set! drop - data -; -set-current -: smooth-channel-via-ptree <{ :optional beg 0 dur #f snd #f chn #f edpos #f -- val }> - beg snd chn edpos sample { y0 } - beg dur snd chn #f frames 1- || snd chn edpos sample { y1 } - y1 y0 f> if pi else 0.0 then { init-angle } - y0 y1 f+ f2/ { off } - y1 y0 f- fabs f2/ { scale } - vct( 0.0 0.0 init-angle off scale ) { data } - $" %s %s %s" #( beg dur get-func-name ) string-format { origin } - <'> scvp3-cb beg dur snd chn edpos #t data scvp1-cb origin ptree-channel -; -previous - -\ ;;; -------- ring-modulate-channel (ring-mod as virtual op) - -hide -: rmc-cb3 <{ y data forward -- val }> - data 0 vct-ref { angle } - data 1 vct-ref { incr } - angle fsin y f* ( val ) - data 0 angle incr forward if f+ else f- then vct-set! drop ( val ) -; -: rmc-cb2 { freq snd -- prc; frag-beg frag-dur self -- vct } - 2 proc-create { prc } freq , snd , prc - does> { frag-beg frag-dur self -- vct } - two-pi self @ ( freq ) f* self cell+ @ ( snd ) srate f/ { incr } - vct( frag-beg incr f* two-pi fmod incr ) -; -set-current -: ring-modulate-channel <{ freq :optional beg 0 dur #f snd #f chn #f edpos #f -- val }> - $" %s %s %s %s" #( freq beg dur get-func-name ) string-format { origin } - <'> rmc-cb3 beg dur snd chn edpos #f freq snd rmc-cb2 origin ptree-channel -; -previous - \ ;;; -------- re-order channels : scramble-channels ( new-order -- ) - \ ;; (scramble-channels 3 2 0 1) means chan 3 goes to 0, etc - { end-chans } - end-chans length { len } - len 1 > if - end-chans map i end-map { cur-chans } - end-chans each { end-chan } - cur-chans i array-ref { cur-chan } - end-chan cur-chan <> if - #f cur-chans each { chn } chn end-chan = if drop ( #f ) i leave then end-each { end-loc } - #f end-loc #f i 0 len #f swap-channels drop - cur-chans end-loc cur-chan array-set! - cur-chans i end-chan array-set! - then - end-each - then + \ ;; (scramble-channels 3 2 0 1) means chan 3 goes to 0, etc + { end-chans } + end-chans length { len } + len 1 > if + end-chans map + i + end-map { cur-chans } + end-chans each { end-chan } + cur-chans i array-ref { cur-chan } + end-chan cur-chan <> if + #f cur-chans each { chn } + chn end-chan = if + drop ( #f ) + i + leave + then + end-each { end-loc } + #f end-loc #f i 0 len #f swap-channels drop + cur-chans end-loc cur-chan array-set! + cur-chans i end-chan array-set! + then + end-each + then ; hide : sc-scan-cb { buffer silence in-silence edges samp -- prc; y self -- #f } - 1 proc-create buffer , silence , in-silence , edges , samp , ( prc ) - does> { y self -- #f } - self @ ( buffer ) y y f* moving-average { sum-of-squares } - sum-of-squares self cell+ @ ( silence ) f< { now-silent } - self 2 cells + @ ( in-silence ) now-silent equal? unless - self 3 cells + @ ( edges ) self 4 cells + @ ( samp ) array-push drop - then - now-silent self 2 cells + ! ( in-silence = now-silent ) - 1 self 4 cells + +! ( samp++ ) - #f + 1 proc-create ( prc ) + buffer , silence , in-silence , edges , samp , + does> { y self -- #f } + self @ ( buffer ) y y f* moving-average { sum-of-squares } + sum-of-squares self cell+ @ ( silence ) f< { now-silent } + self 2 cells + @ ( in-silence ) now-silent equal? unless + self 3 cells + @ ( edges ) + self 4 cells + @ ( samp ) array-push drop + then + now-silent self 2 cells + ! ( in-silence = now-silent ) + 1 self 4 cells + +! ( samp++ ) + #f ; + : sc-edit-cb { pieces -- prc; self -- val } - 0 proc-create pieces , 0 ( start ) , ( prc ) - does> { self -- val } - self @ { pieces } - self cell+ @ { start } - 0.0 { scale-by } - pieces length { len } - len 0 ?do - len random fround->s { this } - pieces this array-ref { reg } - pieces this #f array-set! - reg unless - len this 1+ ?do - pieces i array-ref dup if - to reg - pieces i #f array-set! - leave - then - loop - reg unless - 0 this 1- ?do - pieces i array-ref dup if - to reg - pieces i #f array-set! - leave - then - -1 +loop - then - then - reg start #f #f 0 mix-region drop - reg 0 region-frames start + to start - reg forget-region drop - loop - pieces + 0 proc-create ( prc ) + pieces , 0 ( start ) , + does> { self -- val } + self @ { pieces } + self cell+ @ { start } + 0.0 { scale-by } + pieces length { len } + len 0 ?do + len random fround->s { this } + pieces this array-ref { reg } + pieces this #f array-set! + reg unless + len this 1+ ?do + pieces i array-ref dup if + to reg + pieces i #f array-set! + leave + then + loop + reg unless + 0 this 1- ?do + pieces i array-ref dup if + to reg + pieces i #f array-set! + leave + then + -1 +loop + then + then + reg start #f #f 0 mix-region drop + reg 0 region-framples start + to start + reg forget-region drop + loop + pieces ; set-current + : scramble-channel ( silence -- ) - \ ;; (scramble-channel .01) - { silence } - 128 make-moving-average { buffer } - silence 128 f/ to silence - #() { edges } - 0 { samp } - #t { in-silence } - max-regions { old-max } - with-mix-tags { old-tags } - 1024 set-max-regions drop - #f set-with-mix-tags drop - buffer silence in-silence edges samp sc-scan-cb 0 #f #f #f #f scan-channel drop - edges #f #f #f frames array-push drop - 0 0 { start end } - edges map - start *key* #f #f make-region - *key* to start - end-map ( pieces ) sc-edit-cb get-func-name as-one-edit drop - old-max set-max-regions drop - old-tags set-with-mix-tags drop + \ ;; (scramble-channel .01) + { silence } + 128 make-moving-average { buffer } + silence 128 f/ to silence + #() { edges } + 0 { samp } + #t { in-silence } + max-regions { old-max } + with-mix-tags { old-tags } + 1024 set-max-regions drop + #f set-with-mix-tags drop + buffer silence in-silence edges samp sc-scan-cb + 0 #f #f #f #f scan-channel drop + edges #f #f #f framples array-push drop + 0 0 { start end } + edges map + start *key* #f #f make-region + *key* to start + end-map ( pieces ) sc-edit-cb get-func-name as-one-edit drop + old-max set-max-regions drop + old-tags set-with-mix-tags drop ; previous @@ -2365,77 +2490,86 @@ previous hide : rbb-cb { rd beg ctr actual-block-len len snd chn -- prc; y self -- val } - 1 proc-create rd , beg , ctr , actual-block-len , len , snd , chn , ( prc ) - does> { y self -- val } - self @ { rd } - self cell+ @ { beg } - self 2 cells + @ { ctr } - self 3 cells + @ { actual-block-len } - self 4 cells + @ { len } - self 5 cells + @ { snd } - self 6 cells + @ { chn } - rd read-sample { val } - beg 10 < if - val beg f* 0.1 f* to val - else - beg actual-block-len 10 - > if - val actual-block-len beg - f* 0.1 f* to val - then - then - beg 1+ to beg - beg actual-block-len = if - 1 self 2 cells + +! ( ctr++ ) - 0 self 1 cells + ! ( beg = 0 ) - len self 2 cells + @ ( ctr ) actual-block-len * - 0 max snd chn 1 #f make-sampler self ! - then - val + 1 proc-create ( prc ) + rd , beg , ctr , actual-block-len , len , snd , chn , + does> { y self -- val } + self @ { rd } + self cell+ @ { beg } + self 2 cells + @ { ctr } + self 3 cells + @ { actual-block-len } + self 4 cells + @ { len } + self 5 cells + @ { snd } + self 6 cells + @ { chn } + rd read-sample { val } + beg 10 < if + val beg f* 0.1 f* to val + else + beg actual-block-len 10 - > if + val actual-block-len beg - f* 0.1 f* to val + then + then + beg 1+ to beg + beg actual-block-len = if + 1 self 2 cells + +! ( ctr++ ) + 0 self 1 cells + ! ( beg = 0 ) + len self 2 cells + @ ( ctr ) actual-block-len * - 0 max + snd chn 1 #f make-sampler self ! + then + val ; set-current + : reverse-by-blocks <{ block-len :optional snd #f chn #f -- val }> - doc" Divide sound into block-len blocks, recombine blocks in reverse order." - snd chn #f frames { len } - len snd srate block-len f* f/ fround->s { num-blocks } - num-blocks 1 > if - len num-blocks f/ fceil f>s { actual-block-len } - len actual-block-len - snd chn 1 #f make-sampler { rd } - 0 { beg } - 1 { ctr } - $" %s %s" #( block-len get-func-name ) string-format { origin } - rd beg ctr actual-block-len len snd chn rbb-cb 0 #f snd chn #f origin map-channel - else - #f - then + doc" Divide sound into block-len blocks, \ +recombine blocks in reverse order." + snd chn #f framples { len } + len snd srate block-len f* f/ fround->s { num-blocks } + num-blocks 1 > if + len num-blocks f/ fceil f>s { actual-block-len } + len actual-block-len - snd chn 1 #f make-sampler { rd } + 0 { beg } + 1 { ctr } + "%s %s" #( block-len get-func-name ) string-format { origin } + rd beg ctr actual-block-len len snd chn rbb-cb + 0 #f snd chn #f origin map-channel + else + #f + then ; previous hide : rwb-cb { len actual-block-len no-clicks-env snd chn -- prc; self -- val } - 0 proc-create len , actual-block-len , no-clicks-env , snd , chn , ( prc ) - does> { self -- val } - self @ { len } - self cell+ @ { actual-block-len } - self 2 cells + @ { no-clicks-env } - self 3 cells + @ { snd } - self 4 cells + @ { chn } - len 0 ?do - i actual-block-len snd chn #f reverse-channel drop - no-clicks-env i actual-block-len snd chn #f env-channel drop - actual-block-len +loop - #t + 0 proc-create ( prc ) + len , actual-block-len , no-clicks-env , snd , chn , + does> { self -- val } + self @ { len } + self cell+ @ { actual-block-len } + self 2 cells + @ { no-clicks-env } + self 3 cells + @ { snd } + self 4 cells + @ { chn } + len 0 ?do + i actual-block-len snd chn #f reverse-channel drop + no-clicks-env i actual-block-len snd chn #f env-channel drop + actual-block-len +loop + #t ; set-current + : reverse-within-blocks <{ block-len :optional snd #f chn #f -- val }> - doc" Divide sound into blocks, recombine in order, but each block internally reversed." - snd chn #f frames { len } - len snd srate block-len f* f/ fround->s { num-blocks } - num-blocks 1 > if - len num-blocks f/ fceil f>s { actual-block-len } - #( 0.0 0.0 0.01 1.0 0.99 1.0 1.0 0.0 ) { no-clicks-env } - $" %s %s" #( block-len get-func-name ) string-format { origin } - len actual-block-len no-clicks-env snd chn rwb-cb origin as-one-edit - else - 0 #f snd chn #f reverse-channel - then + doc" Divide sound into blocks, recombine in order, \ +but each block internally reversed." + snd chn #f framples { len } + len snd srate block-len f* f/ fround->s { num-blocks } + num-blocks 1 > if + len num-blocks f/ fceil f>s { actual-block-len } + #( 0.0 0.0 0.01 1.0 0.99 1.0 1.0 0.0 ) { no-clicks-env } + "%s %s" #( block-len get-func-name ) string-format { origin } + len actual-block-len no-clicks-env snd chn rwb-cb + origin as-one-edit + else + 0 #f snd chn #f reverse-channel + then ; previous @@ -2443,53 +2577,63 @@ previous hide : cc-cb ( -- prc; y self -- f ) - 1 proc-create 0.0 ( last-y ) , ( prc ) - does> { y self -- f } - self @ { last-y } - y fabs 0.9999 f>= - last-y fabs 0.9999 f>= && ( result ) - y self ! ( last-y = y ) - ( result ) + 1 proc-create ( prc ) + 0.0 ( last-y ) , + does> { y self -- f } + self @ { last-y } + y fabs 0.9999 f>= + last-y fabs 0.9999 f>= && ( flag ) + y self ! ( last-y = y ) + ( flag ) ; set-current + : channel-clipped? <{ :optional snd #f chn #f -- val }> - doc" Returns #t and a sample number if it finds clipping." - cc-cb 0 #f snd chn #f scan-channel + doc" Return #t and a sample number if it finds clipping." + cc-cb 0 #f snd chn #f scan-channel ; previous \ ;;; -------- sync-everything : sync-everything ( -- ) - doc" Sets the sync fields of all currently open sounds to the same, unique value." - sync-max 1+ { new-sync } - sounds each ( snd ) new-sync swap set-sync drop end-each + doc" Set the sync fields of all currently open sounds \ +to the same, unique value." + sync-max 1+ { new-sync } + sounds each ( snd ) + new-sync swap set-sync drop + end-each ; \ === Moog Filter === hide -vct( 0.999969 0.990082 0.980347 0.970764 0.961304 0.951996 0.94281 0.933777 0.924866 0.916077 - 0.90741 0.898865 0.890442 0.882141 0.873962 0.865906 0.857941 0.850067 0.842346 0.834686 - 0.827148 0.819733 0.812378 0.805145 0.798004 0.790955 0.783997 0.77713 0.770355 0.763672 - 0.75708 0.75058 0.744141 0.737793 0.731537 0.725342 0.719238 0.713196 0.707245 0.701355 - 0.695557 0.689819 0.684174 0.678558 0.673035 0.667572 0.66217 0.65686 0.651581 0.646393 - 0.641235 0.636169 0.631134 0.62619 0.621277 0.616425 0.611633 0.606903 0.602234 0.597626 - 0.593048 0.588531 0.584045 0.579651 0.575287 0.570953 0.566681 0.562469 0.558289 0.554169 - 0.550079 0.546051 0.542053 0.538116 0.53421 0.530334 0.52652 0.522736 0.518982 0.515289 - 0.511627 0.507996 0.504425 0.500885 0.497375 0.493896 0.490448 0.487061 0.483704 0.480377 - 0.477081 0.473816 0.470581 0.467377 0.464203 0.46109 0.457977 0.454926 0.451874 0.448883 - 0.445892 0.442932 0.440033 0.437134 0.434265 0.431427 0.428619 0.425842 0.423096 0.42038 - 0.417664 0.415009 0.412354 0.409729 0.407135 0.404572 0.402008 0.399506 0.397003 0.394501 - 0.392059 0.389618 0.387207 0.384827 0.382477 0.380127 0.377808 0.375488 0.37323 0.370972 - 0.368713 0.366516 0.364319 0.362122 0.359985 0.357849 0.355713 0.353607 0.351532 0.349457 - 0.347412 0.345398 0.343384 0.34137 0.339417 0.337463 0.33551 0.333588 0.331665 0.329773 - 0.327911 0.32605 0.324188 0.322357 0.320557 0.318756 0.316986 0.315216 0.313446 0.311707 - 0.309998 0.308289 0.30658 0.304901 0.303223 0.301575 0.299927 0.298309 0.296692 0.295074 - 0.293488 0.291931 0.290375 0.288818 0.287262 0.285736 0.284241 0.282715 0.28125 0.279755 - 0.27829 0.276825 0.275391 0.273956 0.272552 0.271118 0.269745 0.268341 0.266968 0.265594 - 0.264252 0.262909 0.261566 0.260223 0.258911 0.257599 0.256317 - 0.255035 0.25375 ) constant moog-gaintable +vct( 0.999969 0.990082 0.980347 0.970764 0.961304 0.951996 0.94281 0.933777 + 0.924866 0.916077 0.90741 0.898865 0.890442 0.882141 0.873962 0.865906 + 0.857941 0.850067 0.842346 0.834686 0.827148 0.819733 0.812378 0.805145 + 0.798004 0.790955 0.783997 0.77713 0.770355 0.763672 0.75708 0.75058 + 0.744141 0.737793 0.731537 0.725342 0.719238 0.713196 0.707245 0.701355 + 0.695557 0.689819 0.684174 0.678558 0.673035 0.667572 0.66217 0.65686 + 0.651581 0.646393 0.641235 0.636169 0.631134 0.62619 0.621277 0.616425 + 0.611633 0.606903 0.602234 0.597626 0.593048 0.588531 0.584045 0.579651 + 0.575287 0.570953 0.566681 0.562469 0.558289 0.554169 0.550079 0.546051 + 0.542053 0.538116 0.53421 0.530334 0.52652 0.522736 0.518982 0.515289 + 0.511627 0.507996 0.504425 0.500885 0.497375 0.493896 0.490448 0.487061 + 0.483704 0.480377 0.477081 0.473816 0.470581 0.467377 0.464203 0.46109 + 0.457977 0.454926 0.451874 0.448883 0.445892 0.442932 0.440033 0.437134 + 0.434265 0.431427 0.428619 0.425842 0.423096 0.42038 0.417664 0.415009 + 0.412354 0.409729 0.407135 0.404572 0.402008 0.399506 0.397003 0.394501 + 0.392059 0.389618 0.387207 0.384827 0.382477 0.380127 0.377808 0.375488 + 0.37323 0.370972 0.368713 0.366516 0.364319 0.362122 0.359985 0.357849 + 0.355713 0.353607 0.351532 0.349457 0.347412 0.345398 0.343384 0.34137 + 0.339417 0.337463 0.33551 0.333588 0.331665 0.329773 0.327911 0.32605 + 0.324188 0.322357 0.320557 0.318756 0.316986 0.315216 0.313446 0.311707 + 0.309998 0.308289 0.30658 0.304901 0.303223 0.301575 0.299927 0.298309 + 0.296692 0.295074 0.293488 0.291931 0.290375 0.288818 0.287262 0.285736 + 0.284241 0.282715 0.28125 0.279755 0.27829 0.276825 0.275391 0.273956 + 0.272552 0.271118 0.269745 0.268341 0.266968 0.265594 0.264252 0.262909 + 0.261566 0.260223 0.258911 0.257599 0.256317 0.255035 + 0.25375 ) constant moog-gaintable #( 0.0 -1.0 0.03311111 -0.9 @@ -2515,55 +2659,53 @@ vct( 0.999969 0.990082 0.980347 0.970764 0.961304 0.951996 0.94281 0.933777 0.92 0.9933787 1.0 1.0 1.0 ) constant moog-freqtable -struct - cell% field moog-freq - cell% field moog-Q - cell% field moog-s - cell% field moog-y - cell% field moog-fc -end-struct moog-filter% +#( "moog-freq" + "moog-Q" + "moog-s" + "moog-y" + "moog-fc" ) create-struct make-moog-filter-struct set-current -: moog-frequecy@ ( gen -- frq ) moog-freq @ ; -: moog-frequecy! ( frq gen -- ) - { frq gen } - frq gen moog-freq ! - frq mus-srate f2/ f/ moog-freqtable 1.0 envelope-interp gen moog-fc ! +<'> moog-freq@ alias moog-frequecy@ ( gen -- frq ) +: moog-frequency! { gen frq -- } + gen frq moog-freq! + gen frq mus-srate f2/ f/ moog-freqtable 1.0 envelope-interp moog-fc! ; : make-moog-filter ( freq Q -- gen ) - doc" Makes a new moog-filter generator. \ -FREQ is the cutoff in Hz, Q sets the resonance: 0 = no resonance, 1: oscillates at FREQUENCY." - { freq Q } - moog-filter% %alloc { gen } - freq gen moog-freq ! - Q gen moog-Q ! - 4 0.0 make-vct gen moog-s ! - 0.0 gen moog-y ! - freq mus-srate f2/ f/ moog-freqtable 1.0 envelope-interp gen moog-fc ! - gen + doc" Make a new moog-filter generator. \ +FREQ is the cutoff in Hz, Q sets the resonance: \ +0 = no resonance, 1: oscillates at FREQUENCY." + { freq Q } + make-moog-filter-struct { gen } + gen freq moog-frequency! + gen Q moog-Q! + gen vct( 0.0 0.0 0.0 0.0 ) moog-s! + gen 0.0 moog-y! + gen ; : moog-filter ( gen sig -- A ) - { gen sig } - 0.25 sig gen moog-y @ f- f* { A } - gen moog-s @ each { st } - gen moog-fc @ A st f- f* A f+ -0.95 fmax 0.95 fmin to A - gen moog-s @ i A vct-set! drop - A st f+ -0.95 fmax 0.95 fmin to A - end-each - gen moog-fc @ 99.0 f* { ix } - ix fround->s { ixint } - ix ixint f- { ixfrac } - A gen moog-Q @ f* - 1.0 ixfrac f- moog-gaintable ixint 99 + vct-ref f* - ixfrac moog-gaintable ixint 100 + vct-ref f* f+ f* gen moog-y ! - A + { gen sig } + 0.25 sig gen moog-y@ f- f* { A } + gen moog-s@ each { st } + gen moog-fc@ A st f- f* A f+ -0.95 fmax 0.95 fmin to A + gen moog-s@ i A vct-set! drop + A st f+ -0.95 fmax 0.95 fmin to A + end-each + gen moog-fc@ 99.0 f* { ix } + ix fround->s { ixint } + ix ixint f- { ixfrac } + A gen moog-Q@ f* + 1.0 ixfrac f- moog-gaintable ixint 99 + vct-ref + f* ixfrac moog-gaintable ixint 100 + vct-ref f* f+ f* + gen swap moog-y! + A ; previous \ 500.0 0.1 make-moog-filter value gen -\ lambda: <{ y }> gen swap moog-filter ; map-channel +\ lambda: <{ y -- val }> gen y moog-filter ; map-channel \ gen 1.0 moog-filter \ examp.fs ends here |