; PvsRecPlay.csd
; Written by Iain McCurdy, 2012
/*
Hi to all,
I modified PvsRecPlay.csd by Cabbage McCurdy collection, because I could used it.
This .csd is very nice but:
In downloded version PlayOnce didn't work correctly, because after 'else' was missing:
kPlayOnceNdx line 0,1,1
Moreover I insert this 'if' conditions:
if kLoopBeg < kLoopEnd then
kPlayNdx = (kPlayNdx*kLoopLen) + kLoopBeg
elseif kLoopBeg > kLoopEnd then
kPlayNdx = kLoopBeg-(kPlayNdx*kLoopLen) ;INVERT
else
kPlayNdx = kLoopBeg
endif
and I replaced:
kLoopBeg portk gkLoopBeg, kporttime
kLoopEnd portk gkLoopEnd, kporttime
with:
kLoopBeg = gkLoopBeg
kLoopEnd = gkLoopEnd
to avoid crash with begin and end point very close together.
I reduce stereo in to mono, but you can restore to stereo changing the lines with *** flags.
I added play midi instr and global envelope also.
'Play Once' button blink correctly now, when it turn off.
Maybe this version can be useful to someone else.
Unfortly there is an unwanted effect:
At loop, the first time the sound is better, then it's more metallic.
Does anyone know how to avoid this?
Greetings
Andrea S. 2019
*/
form caption("PVS Rec/Play") size(300, 600), pluginid("pvrp")
groupbox bounds( 0, 0,300, 95), text("Transport")
label bounds( 10, 75, 70, 14), text("Record")
label bounds( 80, 75, 70, 14), text("Pause")
label bounds(150, 75, 70, 14), text("Play Loop")
label bounds(220, 75, 70, 14), text("Play Once")
checkbox bounds( 10, 25, 70, 50), channel("Record"), value(0), shape("square"), colour("red")
checkbox bounds( 80, 25, 70, 50), channel("Pause"), value(0), shape("square"), colour("Blue")
checkbox bounds(150, 25, 70, 50), channel("PlayLoop"), value(0), shape("square")
checkbox bounds(220, 25, 70, 50), channel("PlayOnce"), value(0), shape("square"), colour("yellow")
hslider bounds( 0, 95, 300,50), channel("Speed"), range(-4.00, 4.00, 1) ;, text("Speed")
label bounds(100, 135, 100,13), text("Speed") , align(centre)
hslider bounds( 0, 145, 300,50), channel("Pitch"), range(0.25, 4.00, 1) ;, text("Pitch")
label bounds(100, 185, 100,13), text("Pitch") , align(centre)
hslider bounds( 0, 195, 300,50), channel("LoopBeg"), range(0, 1, 0) ;, text("Loop Begin")
label bounds(100, 235, 100,13), text("Loop Begin") , align(centre)
hslider bounds( 0, 245, 300,50), channel("LoopEnd"), range(0, 1, 1) ;, text("Loop End")
label bounds(100, 285, 100,13), text("Loop End") , align(centre)
hslider bounds( 0, 295, 300,50), channel("Attack"), range(.01, 1, .2) ;, text("Attack Time")
label bounds(100, 335, 100,13), text("Attack Time") , align(centre)
hslider bounds( 0, 345, 300,50), channel("Release"), range(.01, 1, .2) ;, text("Release Time")
label bounds(100, 385, 100,13), text("Release Time") , align(centre)
hslider bounds( 0, 395, 300,50), channel("InGain"), range(0, 1, 1) ;, text("Input Gain")
label bounds(100, 435, 100,13), text("Input Gain") , align(centre)
hslider bounds( 0, 445, 300,50), channel("OutGain"), range(0, 1, 1) ;, text("Output Gain")
label bounds(100, 485, 100,13), text("Output Gain") , align(centre)
keyboard bounds(8, 510, 285, 80)
;-d -n
-dm0 -n -+rtmidi=null -M0 ;flag copati da LiveLooper.
sr = 44100
ksmps = 32
nchnls = 2
0dbfs = 1
massign 0, 3
;Author: Iain McCurdy (2012)
;***PER ATTIVARE REGISTRAZIONE STEREO RIPRISTINA RIGHE COMMENTATE CON ***
gistorageL ftgen 0,0,1048576,-7,0 ;AUDIO DATA STORAGE SPACE (ABOUT 23 SECONDS)
;gistorageR ftgen 0,0,1048576,-7,0 ;AUDIO DATA STORAGE SPACE (ABOUT 23 SECONDS) ;***SE LA REGISTRAZIONE è MONO QUESTA RIGA NON SERVE
gkRecDur init 0 ;DURATION OF THE MOST RECENTLY RECORDED BUFFER
gibuflen init 60 ;PVS BUFFER LENGTH
instr 1 ;READ IN WIDGETS AND START AND STOP THE VARIOUS RECORDING AND PLAYBACK INSTRUMENTS
gitablelen = ftlen(gistorageL) ;DERIVE TABLE LENGTH
gkRecord chnget "Record" ;READ IN CABBAGE WIDGET CHANNELS
gkPause chnget "Pause"
gkPlayLoop chnget "PlayLoop"
gkPlayOnce chnget "PlayOnce"
gkPlayOnceTrig changed gkPlayOnce
gkSpeed chnget "Speed"
gkPitch chnget "Pitch"
gkLoopBeg chnget "LoopBeg"
gkLoopEnd chnget "LoopEnd"
gkInGain chnget "InGain"
gkOutGain chnget "OutGain"
gkAtkTime chnget "Attack"
gkRelTime chnget "Release"
;=============================feature to transform 'k' to 'i', valid for 'linsegr'
giAtkTime init i(gkAtkTime) ;trasforma variabile 'k' in variabile 'i' per essere compatibile con 'linsegr', che accetta solo 'i'
giRelTime init i(gkRelTime)
kRelChange changed gkAtkTime, gkRelTime
;printk2 kRelChange
if kRelChange = 1 then ;se muovi lo slider
reinit reset2
reset2:
giAtkTime = i(gkAtkTime)
giRelTime = i(gkRelTime) ;aggiorna i valori
;print giRelTime
rireturn
endif
;=============================
/*
;SOSTITUITO DAL 'DEFINE' SEGUENTE, PRESO DA TabRecFx.csd
#define TURN_ON(NAME)
#
i$NAME nstrnum "$NAME"
kOnTrig$NAME trigger gk$NAME,0.5,0
if kOnTrig$NAME==1 then ;IF BUTTON IS TURNED ON...
turnoff2 i$NAME,0,giRelTime ;UTILE NEL CASO IN CUI SI PREMA IL PLAY MENTRE ANCORA è IN ESECUZIONE IL RELEASE, PER NON FAR RIMANERE 'INCANTATO' IL BOTTONE
event "i",i$NAME,0,3600
endif
#
$TURN_ON(Record)
$TURN_ON(PlayOnce)
$TURN_ON(PlayLoop)
*/
;ALTERNATIVA A 'DEFINE'
#define TURN_ON_OFF(NAME) ;defines a macro with arguments. macro per accendere e spegnere gli strumenti: 'Record', 'PlayOnce', 'PlayLoop'
# ;l'argomento è 'NAME'
i$NAME nstrnum "$NAME" ;Returns the number of a named instrument. $NAME può essere lo strumento "Record' o 'PlayOnce' o 'PlayLoop', come stabilito sotto
;>>>>>>>>>>>>>>>> i$NAME cambia ad 'i' time, vedere oltre l'uso degli opcode 'event' e 'turnoff2' <<<<<<<<<<<<<<<<<<<<<<
;serve ad accendere e spegnere gli strumenti 'Record', 'PlayOnce', 'PlayLoop'
kOnTrig$NAME trigger gk$NAME,0.5,0 ;Informs when a krate signal crosses a threshold: ksig, kthreshold, kmode. Se è maggiore di .5 (kmode=0) o se è minore di .5 (kmode = 1)
kOffTrig$NAME trigger gk$NAME,0.5,1 ;>>>>>>>>>>>>>>>>>>>> gk$NAME sarà secondo i casi: gkRecord o gkPlayOnce o gkPlayLoop <<<<<<<<<<<<<<<<<<<<<
if kOnTrig$NAME==1 then ;IF BUTTON IS TURNED ON...
turnoff2 i$NAME,0,0 ;UTILE NEL CASO IN CUI SI PREMA IL PLAY MENTRE ANCORA è IN ESECUZIONE IL RELEASE, PER NON FAR RIMANERE 'INCANTATO' IL BOTTONE
event "i",i$NAME,0,3600 ;Generates a score event from an instrument. event_i "scorechar", iinsnum, idelay, idur, [, ip4] [, ip5] [, ...]
elseif kOffTrig$NAME==1 then ;IF BUTTON IS TURNED ON...
turnoff2 i$NAME,0,giRelTime ;spegne lo strumento generato da 'event', tempo di rilascio definito in gkRelTime
;PERCHE' c'era una VIRGOLA DOPO turnoff2 e funzionava lo stesso???
endif
#
$TURN_ON_OFF(Record) ;applica la stessa macro con tre diversi bottoni (nomi dei canali)
$TURN_ON_OFF(PlayOnce)
$TURN_ON_OFF(PlayLoop)
endin
instr Record
if gkRecord==0 then ;IF BUTTON IS TURNED ON...
turnoff
endif
if gkPause=1 goto SKIP_RECORD ;IF PAUSE BUTTON IS ACTIVATED TEMPORARILY SKIP RECORDING PROCESS
ainL,ainR ins ;READ AUDIO FROM LIVE INPUT CHANNEL 1
;MACRO THAT DEFINES THE CODED NEEDED TO RECORD A SINGLE CHANNEL PVS BUFFER
#define REC_BUF(CHAN)
#
iFFTsize = 1024
ioverlap = 256
iwinsize = 1024
iwintype = 1
;kPhOffset = 0
f_anal$CHAN pvsanal ain$CHAN, iFFTsize, ioverlap, iwinsize, iwintype ;ANALYSE THE LEFT CHANNEL AUDIO. OUTPUT AN F-SIGNAL.
ibuf$CHAN,ktime pvsbuffer f_anal$CHAN, gibuflen ;BUFFER FSIG
gkhandle$CHAN init ibuf$CHAN ;INITIALISE HANDLE TO BUFFER
#
;EXPAND BUFFER TWICE, ONCE FOR EACH STEREO CHANNEL
$REC_BUF(L)
;$REC_BUF(R) ;***NEL CASO DI REGISTRAZIONE MONO QUESTA RIGA NON SERVE
gkRecDur timeinsts ;DURATION OF CURRENT RECORDING
if gkRecDur>=gibuflen then ;IF BUFFER IS FULL (I.E. DO NOT OVERWRITE THE BEGINNING OF THE BUFFER
turnoff ;TURN OFF THIS INSTRUMENT
endif ;ENDO OF THIS CONDITIONAL BRANCH
SKIP_RECORD: ;JUMP TO HERE WHEN 'PAUSE' BUTTON IS ACTIVE
endin
instr PlayLoop
;if gkPlayLoop==0 then ;IF BUTTON IS TURNED ON...
;turnoff
;endif
if gkPlayLoop==0 then ;IF 'PLAY LOOPED' BUTTON IS INACTIVE...
turnoff2 "PlayLoop",0,giRelTime ;TURN THIS INSTRUMENT OFF
endif ;END OF THIS CONDITIONAL BRANCH
if gkPause=1 goto SKIP_PLAY_LOOP ;IF PAUSE BUTTON IS ACTIVATED SKIP PLAYBACK CODE
kporttime linseg 0,0.001,0.05 ;PORTAMENTO TIME RAMPS UP RAPIDLY TO A HELD VALUE
kLoopBeg = gkLoopBeg
kLoopEnd = gkLoopEnd
;portk fa imballare il processo quando kLoopBeg e kLoopEnd hanno valori uguali!!!!!! Oltre tutto sembra che non serva a nulla!!!!!!!!
;kLoopBeg portk gkLoopBeg, kporttime ;APPLY PORTAMENTO SMOOTHING TO CHANGES OF LOOP BEGIN SLIDER
;kLoopEnd portk gkLoopEnd, kporttime ;APPLY PORTAMENTO SMOOTHING TO CHANGES OF LOOP END SLIDER
kLoopBeg = kLoopBeg * gkRecDur ;RESCALE gkLoopBeg (RANGE 0-1) TO BE WITHIN THE RANGE 0-FILE_LENGTH.
kLoopEnd = kLoopEnd * gkRecDur ;RESCALE gkLoopEnd (RANGE 0-1) TO BE WITHIN THE RANGE 0-FILE_LENGTH.
kLoopLen = abs(kLoopEnd - kLoopBeg) ;DERIVE LOOP LENGTH FROM LOOP START AND END POINTS
kPlayPhasFrq divz gkSpeed, kLoopLen, 0.00001 ;SAFELY DIVIDE, PROVIDING ALTERNATIVE VALUE IN CASE DENOMINATOR IS ZERO
kPlayNdx phasor kPlayPhasFrq ;DEFINE PHASOR POINTER FOR BUFFER READ INDEX
;kLoopBeg = (kLoopBeg < kLoopEnd ? kLoopBeg : kLoopEnd) ;CHECK IF LOOP-BEGINNING AND LOOP-END SLIDERS HAVE BEEN REVERSED
;printk .2, kPlayNdx
if kLoopBeg < kLoopEnd then
kPlayNdx = (kPlayNdx*kLoopLen) + kLoopBeg ;RESCALE INDEX POINTER ACCORDING TO LOOP LENGTH AND LOOP BEGINING
elseif kLoopBeg > kLoopEnd then
kPlayNdx = kLoopBeg-(kPlayNdx*kLoopLen) ;INVERTE LA DIREZIONE
else
kPlayNdx = kLoopBeg
endif ;...IN TAL MODO SPEGNENDO E RIACCENDENDO IL BOTTONE LA LETTURA RIPARTE.
f_bufL pvsbufread kPlayNdx , gkhandleL ;READ BUFFER
f_scaleL pvscale f_bufL, gkPitch ;RESCALE FREQUENCIES
aL pvsynth f_scaleL ;RESYNTHESIZE THE f-SIGNAL AS AN AUDIO SIGNAL
;***SE LA REGISTRAZIONE è MONO LE TRE RIGHE SUCCESSIVE NON SERVONO
;f_bufR pvsbufread kPlayNdx , gkhandleR ;READ BUFFER - SE VUOI USARE INGRESSO STEREO RIPRISTINA gkhandleR!!!!!!
;f_scaleR pvscale f_bufR, gkPitch ;RESCALE FREQUENCIES
;aR pvsynth f_scaleR ;RESYNTHESIZE THE f-SIGNAL AS AN AUDIO SIGNAL
kEnv linsegr 0, giAtkTime, 1, giRelTime, 0 ;inviluppo globale. Tempo di rilascio in accordo con turnoff2
;***SE LA REGISTRAZIONE è MONO SCRIVERE ALL'USCITA DUE VOLTE aL, ALTRIMENTI aL POI aR
outs aL*gkOutGain*kEnv,aL*gkOutGain*kEnv ;SEND AUDIO TO OUTPUTS
SKIP_PLAY_LOOP: ;JUMP TO HERE WHEN 'PAUSE' BUTTON IS ACTIVE
endin
instr PlayOnce
;if gkPlayOnce==0 then ;IF BUTTON IS TURNED ON...
; turnoff ;2 "PlayOnce", 0, giRelTime
;endif
if gkPause=1 goto SKIP_PLAY_ONCE ;IF PAUSE BUTTON IS ACTIVATED SKIP PLAYBACK
kPlayOnceNdx init 0 ;INITIALISE PLAYBACK POINTER
if kPlayOnceNdx<=gkRecDur then ;IF PLAYBACK IS NOT YET COMPLETED THEN CONTINUE PLAYBACK
kLoopBeg = gkLoopBeg * gkRecDur ;RESCALE gkLoopBeg (RANGE 0-1) TO BE WITHIN THE RANGE 0-FILE_LENGTH.
kLoopEnd = gkLoopEnd * gkRecDur ;RESCALE gkLoopEnd (RANGE 0-1) TO BE WITHIN THE RANGE 0-FILE_LENGTH.
kPlayOnceNdx line 0,1,1 ;CREATE A MOVING POINTER
if kLoopEnd>kLoopBeg then ;IF LOOP END SLIDER IS AT A LATER POSITION TO LOOP BEGIN SLIDER...
kPlayOnceNdx = (kPlayOnceNdx*gkSpeed)+kLoopBeg ;RESCALE MOVING POINTER VALUE ACCORDING TO LOOP BEGIN POSITION AND SPEED SLIDER SETTING
if kPlayOnceNdx>=kLoopEnd then ;IF PLAY INDEX IS EQUAL TO OR GREATER THAN THE DURATION OF THE RECORDED BUFFER (STOP PLAYBACK)...
turnoff ;TURN THIS INSTRUMENT OFF
endif ;END OF CONDITIONAL BRANCH
else ;OTHERWISE (I.E. LOOP BEGIN SLIDER IS AT A LATER POSITION THAT LOOP END)
kPlayOnceNdx line 0,1,1 ;CREATE A MOVING POINTER ((((QUESTA RIGA MANCAVA E 'PLAY ONCE' NON FUNZIONAVA!!!!!!!!!!!!!!!!!!!!!!!)))))
kPlayOnceNdx = kLoopBeg-(kPlayOnceNdx*gkSpeed) ;RESCALE MOVING POINTER VALUE ACCORDING TO LOOP BEGIN POSITION AND SPEED SLIDER SETTING
if kPlayOnceNdx<=kLoopEnd then ;IF PLAY POINTER HAS REACHED THE BEGINNING OF THE PRESCRIBED CHUNK BETWEEN LOOP BEGIN AND LOOP END (STOP PLAYBACK)...
turnoff2 "PlayOnce", 0, giRelTime ;TURN THIS INSTRUMENT OFF
endif ;END OF CONDITIONAL BRANCH
endif ;END OF CONDITIONAL BRANCH
endif
f_bufL pvsbufread kPlayOnceNdx , gkhandleL ;READ BUFFER
f_scaleL pvscale f_bufL, gkPitch ;RESCALE FREQUENCIES
aL pvsynth f_scaleL ;RESYNTHESIZE THE f-SIGNAL AS AN AUDIO SIGNAL
;***SE LA REGISTRAZIONE è MONO LE TRE RIGHE SUCCESSIVE NON SERVONO
;f_bufR pvsbufread kPlayOnceNdx , gkhandleR ;READ BUFFER - SE VUOI USARE INGRESSO STEREO RIPRISTINA gkhandleR!!!!!!
;f_scaleR pvscale f_bufR, gkPitch ;RESCALE FREQUENCIES
;aR pvsynth f_scaleR ;RESYNTHESIZE THE f-SIGNAL AS AN AUDIO SIGNAL
kEnv linsegr 0, giAtkTime, 1, giRelTime, 0 ;inviluppo globale. Tempo di rilascio in accordo con turnoff2
;***SE LA REGISTRAZIONE è MONO SCRIVERE ALL'USCITA DUE VOLTE aL, ALTRIMENTI aL POI aR
outs aL*gkOutGain*kEnv,aL*gkOutGain*kEnv ;SEND AUDIO TO OUTPUT
;else
; turnoff
;endif ;END OF CONDITIONAL BRANCH
SKIP_PLAY_ONCE:
krelease release
if krelease==1 then
chnset 1-krelease,"PlayOnce"
endif
endin
instr 3 ; PLayMidi, molto simile allo strumento 'PlayLoop'
kpitch = cpsmidi()/cpsmidinn(60) ; DERIVE RATIO BASED ON NOTE NUMBER 60 AS THE POINT OF UNISON (IE. RATIO=1)
ival ampmidi 1
;if gkPlayLoop==0 then ;IF BUTTON IS TURNED ON...
;turnoff
;endif
;if gkPlayLoop==0 then ;IF 'PLAY LOOPED' BUTTON IS INACTIVE...
;turnoff ;TURN THIS INSTRUMENT OFF
;endif ;END OF THIS CONDITIONAL BRANCH
if gkPause=1 goto SKIP_PLAY_LOOP ;IF PAUSE BUTTON IS ACTIVATED SKIP PLAYBACK CODE
kporttime linseg 0,0.001,0.05 ;PORTAMENTO TIME RAMPS UP RAPIDLY TO A HELD VALUE
kLoopBeg = gkLoopBeg
kLoopEnd = gkLoopEnd
;portk fa imballare il processo quando kLoopBeg e kLoopEnd hanno valori uguali!!!!!! Oltre tutto sembra che non serva a nulla!!!!!!!!
;kLoopBeg portk gkLoopBeg, kporttime ;APPLY PORTAMENTO SMOOTHING TO CHANGES OF LOOP BEGIN SLIDER
;kLoopEnd portk gkLoopEnd, kporttime ;APPLY PORTAMENTO SMOOTHING TO CHANGES OF LOOP END SLIDER
kLoopBeg = kLoopBeg * gkRecDur ;RESCALE gkLoopBeg (RANGE 0-1) TO BE WITHIN THE RANGE 0-FILE_LENGTH.
kLoopEnd = kLoopEnd * gkRecDur ;RESCALE gkLoopEnd (RANGE 0-1) TO BE WITHIN THE RANGE 0-FILE_LENGTH.
kLoopLen = abs(kLoopEnd - kLoopBeg) ;DERIVE LOOP LENGTH FROM LOOP START AND END POINTS
kPlayPhasFrq divz gkSpeed, kLoopLen, 0.00001 ;SAFELY DIVIDE, PROVIDING ALTERNATIVE VALUE IN CASE DENOMINATOR IS ZERO
kPlayNdx phasor kPlayPhasFrq ;DEFINE PHASOR POINTER FOR BUFFER READ INDEX
;kLoopBeg = (kLoopBeg < kLoopEnd ? kLoopBeg : kLoopEnd) ;CHECK IF LOOP-BEGINNING AND LOOP-END SLIDERS HAVE BEEN REVERSED
if kLoopBeg < kLoopEnd then
kPlayNdx = (kPlayNdx*kLoopLen) + kLoopBeg ;RESCALE INDEX POINTER ACCORDING TO LOOP LENGTH AND LOOP BEGINING
elseif kLoopBeg > kLoopEnd then
kPlayNdx = kLoopBeg-(kPlayNdx*kLoopLen) ;INVERTE LA DIREZIONE
else
kLoopEnd = kLoopEnd-(kr/sr) ;EVITA CHE SI IMBALLI COMPLETAMENTE NEL CASO CHE I DUE VALORI COINCIDANO...
endif ;...IN TAL MODO SPEGNENDO E RIACCENDENDO IL BOTTONE LA LETTURA RIPARTE.
f_bufL pvsbufread kPlayNdx , gkhandleL ;READ BUFFER
f_scaleL pvscale f_bufL, gkPitch*kpitch ;RESCALE FREQUENCIES
aL pvsynth f_scaleL ;RESYNTHESIZE THE f-SIGNAL AS AN AUDIO SIGNAL
;***SE LA REGISTRAZIONE è MONO LE TRE RIGHE SUCCESSIVE NON SERVONO
;f_bufR pvsbufread kPlayNdx , gkhandleR ;READ BUFFER - SE VUOI USARE INGRESSO STEREO RIPRISTINA gkhandleR!!!!!!
;f_scaleR pvscale f_bufR, gkPitch*kpitch ;RESCALE FREQUENCIES
;aR pvsynth f_scaleR ;RESYNTHESIZE THE f-SIGNAL AS AN AUDIO SIGNAL
kEnv linsegr 0, giAtkTime, ival, giRelTime, 0 ;inviluppo globale. Tempo di rilascio in accordo con turnoff2
;***SE LA REGISTRAZIONE è MONO SCRIVERE ALL'USCITA DUE VOLTE aL, ALTRIMENTI aL POI aR
outs aL*gkOutGain*kEnv,aL*gkOutGain*kEnv ;SEND AUDIO TO OUTPUTS
SKIP_PLAY_LOOP: ;JUMP TO HERE WHEN 'PAUSE' BUTTON IS ACTIVE
endin
i 1 0 [3600*24*7]