Welcome to %s forums

BrainModular Users Forum

Login Register

[script] data delays

I need help on a Patch
Post Reply
woodslanding
Member
Posts: 1327
Contact:

Unread post by woodslanding » 18 Jan 2010, 09:57

Somewhere in my old setup I was using a data delay. i sure as heck didn't write it as it's all greek to me.

Just wondering what it entails to delay some data with the new script. I don't need precision.... well, here's what I need:

I use mr ray22 which is an excellent rhodes emulation. Problem is, when you change patches, you get nasty clicks on every note until all 24 have been played at least once. The easy way to do this is to lay your arm on the keyboard with the volume down. Now it plays pretty!

So I created in V4 a 'virtual arm' that, when it sees a patch change, sends out a 0 to control the volume, spits out 24 different note ons, spits out another 24 about 200 ms. later, and then about 500 ms after that (so the sound can decay) sends a 1 on the volume out.

Works great, but I used three data delay modules, and three midi scripts. Now I think I could do it all with one script pretty easily if I understood delays better.

Obviously, I'd also like to make it as efficient as possible--shouldn't use any cpu except when it sees a program change.

Thoughts??

Thanks in advance!
-e
Custom Ryzen 5900x MATX build, Win10, Fireface UFX, touchscreen
Custom 2 manual midi keyboard
Usine, Kontakt, Reaktor, Synthmaster, Byome, Arturia, Soundtoys, Unify

bsork
Site Admin
Posts: 1334
Location: Asker, Norway
Contact:

Unread post by bsork » 18 Jan 2010, 12:00

Something like this?

Code: Select all

var onPM : tParameter;
var startTime : Single;
var doChange, noteOffsSent : Boolean;
...
PROCEDURE Callback (n : Integer);
begin
   startTime := TimeMs;
   doChange := TRUE;
   noteOffsSent := FALSE;
   SendVol0AndNoteOns;
end;

PROCEDURE Process;
BEGIN
   IF (doChange) THEN BEGIN
      IF ((TimeMs - startTime) >= 700.0) THEN BEGIN
         doChange := FALSE;
         SendVol1;
      END
      ELSE IF (((TimeMs - startTime) >= 200.0) AND (NOT noteOffsSent)) THEN BEGIN
         SendNoteOffs;
         noteOffsSent := TRUE;
      END;
   END;
END;
Just a tiny bit of CPU to check the doChange variable in Process.

I can't think of any way of doing something like this where somethings are going to happen after the initial trigger event that wouldn't use any CPU to check whatever needs checking (in this case the elapsed). Well, you could always put the script inside a separate (sub-)patch and turn activation on and off with some similar to keep track of the proceedings, but as long as you can use something as simple as a boolean, I doubt you'll gain anything. For all that I know, the patching needed could even use a bit more CPU.
Bjørn S

woodslanding
Member
Posts: 1327
Contact:

Unread post by woodslanding » 18 Jan 2010, 23:46

Thanks, Bsork!! I will start with this, should be easy!
Custom Ryzen 5900x MATX build, Win10, Fireface UFX, touchscreen
Custom 2 manual midi keyboard
Usine, Kontakt, Reaktor, Synthmaster, Byome, Arturia, Soundtoys, Unify

woodslanding
Member
Posts: 1327
Contact:

Unread post by woodslanding » 18 Jan 2010, 23:53

BTW, Bsork, I'm digging your variable naming.

The code reads like butter!

I've realized it's not so easy in Delphi when you don't have capitalization to set things apart.....
Custom Ryzen 5900x MATX build, Win10, Fireface UFX, touchscreen
Custom 2 manual midi keyboard
Usine, Kontakt, Reaktor, Synthmaster, Byome, Arturia, Soundtoys, Unify

woodslanding
Member
Posts: 1327
Contact:

Unread post by woodslanding » 19 Jan 2010, 00:01

So, I'm wondering if I shouldn't call sendnoteons from the process thread, as it creates midi?? Still a little fuzzy on this.

I'll try it both ways and see what works.....
Custom Ryzen 5900x MATX build, Win10, Fireface UFX, touchscreen
Custom 2 manual midi keyboard
Usine, Kontakt, Reaktor, Synthmaster, Byome, Arturia, Soundtoys, Unify

woodslanding
Member
Posts: 1327
Contact:

Unread post by woodslanding » 19 Jan 2010, 00:39

well, here's my first attempt. This sends out noteONs constantly, and never sends a noteOff. Not sure what I'm doing wrong....

I'm not sure why you are checking whether noteoffs are sent. I'll look into that.

EDIT: Actually this version sends one noteOFF, but if I move the noteON method to process, I lose even that one.

Code: Select all

// resets Mr ray's oscillators on a patch change


var startTime : Single;
var doChange, noteOffsSent : Boolean;
var i            : integer;
var noteOn       : TMidi;
var noteOff      : TMidi;

var output  : Tparameter;
var volOut  : Tparameter;
var trigger : Tparameter;

// initialisation : create parameters
procedure init;
begin  
 Output := CreateParam('Midi Out',ptMidi);                   SetIsInput(Output,false);
 VolOut := CreateParam('Vol Out',ptDataField);               SetIsInput(volOut,false);
 trigger := CreateParam('trig',ptDataField);                    SetIsOutput(Trigger,false); 


end;

Procedure SendNoteOns;
begin
    SetLength(outPut,24);      // set the number of output codes
    for i := 1 to 24         // loop for all input codes, for polyphonic data (chords)
    do begin
      noteOn.data1 := i + 12;
      noteOn.data2 := 1;
      noteOn.msg := 144; 
      noteOn.channel := 1;
      SetMidiArrayValue(output,i,noteOn); // set output value
    end
end;

Procedure SendNoteOffs;
begin               
    SetLength(outPut,24);      // set the number of output codes
    for i := 1 to 24 do begin               
        noteOff.data1 := i - 12;
        noteOff.data2 := 0;
        noteOff.msg := 128;
        noteOff.channel := 1;
        SetMidiArrayValue(output,i,noteOff); // set output value 
    end;
end;


PROCEDURE Callback (n : Integer);  // only 1 input, trigger....
begin
    
   if n = trigger then begin  
       startTime := TimeMs;
       doChange :=  TRUE;
       noteOffsSent := FALSE;
       SetValue(volOut, 0);
       SendNoteOns;       
   end;
end;

PROCEDURE Process;
BEGIN
   IF (doChange) THEN BEGIN
      IF ((TimeMs - startTime) >= 7000.0) THEN BEGIN
         doChange := FALSE;
         SetValue(volOut, 1);
      END
      ELSE IF (((TimeMs - startTime) >= 200.0) AND (NOT noteOffsSent)) THEN BEGIN
         SendNoteOffs;
         noteOffsSent := TRUE;
      END;
   END;
END;
Custom Ryzen 5900x MATX build, Win10, Fireface UFX, touchscreen
Custom 2 manual midi keyboard
Usine, Kontakt, Reaktor, Synthmaster, Byome, Arturia, Soundtoys, Unify

woodslanding
Member
Posts: 1327
Contact:

Unread post by woodslanding » 19 Jan 2010, 00:47

okay, this sends note ons and note offs, but still does it constantly....

Code: Select all

// resets Mr ray's oscillators on a patch change


var startTime : Single;
var doChange, noteOffsSent : Boolean;
var i            : integer;
var noteOn       : TMidi;
var noteOff      : TMidi;




var output  : Tparameter;
var volOut  : Tparameter;
var trigger : Tparameter;

// initialisation : create parameters
procedure init;
begin  
 Output := CreateParam('Midi Out',ptMidi);                   SetIsInput(Output,false);
 VolOut := CreateParam('Vol Out',ptDataField);               SetIsInput(volOut,false);
 trigger := CreateParam('trig',ptDataField);                    SetIsOutput(Trigger,false); 


end;

Procedure SendNoteOns;
begin
    SetLength(outPut,48);      // set the number of output codes
    for i := 0 to 23         // loop for all input codes, for polyphonic data (chords)
    do begin
      noteOn.data1 := i + 12;
      noteOn.data2 := 1;
      noteOn.msg := 144; 
      noteOn.channel := 1;
      SetMidiArrayValue(output,i,noteOn); // set output value
    end
end;

Procedure SendNoteOffs;
begin               
    //SetLength(outPut,24);      // set the number of output codes
    for i := 24 to 47 do begin               
        noteOff.data1 := i - 12;
        noteOff.data2 := 0;
        noteOff.msg := 128;
        noteOff.channel := 1;
        SetMidiArrayValue(output,i,noteOff); // set output value 
    end;
end;


PROCEDURE Callback (n : Integer);  // only 1 input, trigger....
begin
    
   if n = trigger then begin  
       startTime := TimeMs;
       doChange :=  TRUE;
       noteOffsSent := FALSE;
       SetValue(volOut, 0);
       SendNoteOns;       
   end;
end;

PROCEDURE Process;
BEGIN
   IF (doChange) THEN BEGIN
      IF ((TimeMs - startTime) >= 7000.0) THEN BEGIN
         doChange := FALSE;
         SetValue(volOut, 1);
      END
      ELSE IF (((TimeMs - startTime) >= 200.0) AND (NOT noteOffsSent)) THEN BEGIN
         SendNoteOffs;
         noteOffsSent := TRUE;
      END;
   END;
END;
Custom Ryzen 5900x MATX build, Win10, Fireface UFX, touchscreen
Custom 2 manual midi keyboard
Usine, Kontakt, Reaktor, Synthmaster, Byome, Arturia, Soundtoys, Unify

woodslanding
Member
Posts: 1327
Contact:

Unread post by woodslanding » 19 Jan 2010, 00:50

I want to try making this contingent on the value of trigger, but I cannot test for

getValue(trigger) = 1

without getting a compile error. What am I doing wrong? Doesn't getValue() return a SINGLE??
Custom Ryzen 5900x MATX build, Win10, Fireface UFX, touchscreen
Custom 2 manual midi keyboard
Usine, Kontakt, Reaktor, Synthmaster, Byome, Arturia, Soundtoys, Unify

martignasse
Site Admin
Posts: 611
Location: Lyon, FRANCE
Contact:

Unread post by martignasse » 19 Jan 2010, 01:03

hi woodlanding,
woodslanding wrote:I want to try making this contingent on the value of trigger, but I cannot test for

getValue(trigger) = 1

without getting a compile error. What am I doing wrong? Doesn't getValue() return a SINGLE??
try

Code: Select all

round(getValue(trigger)) = 1;
because you are comparing a single against an integer.

hope it help
Martin FLEURENT - Usine Developer - SDK maintainer

martignasse
Site Admin
Posts: 611
Location: Lyon, FRANCE
Contact:

Unread post by martignasse » 19 Jan 2010, 01:10

and for your problem of notes constantly sent,

i believe you have to set the length of outpout parameter to 0 at the end of the IF (doChange) block, or the midi message is fired each time Process is executed.

Code: Select all

BEGIN
   IF (doChange) THEN BEGIN
      IF ((TimeMs - startTime) >= 7000.0) THEN BEGIN
         doChange := FALSE;
         SetValue(volOut, 1);
      END
      ELSE IF (((TimeMs - startTime) >= 200.0) AND (NOT noteOffsSent)) THEN BEGIN
         SendNoteOffs;
         noteOffsSent := TRUE;
      END;
      SetLength(outPut,48);
   END;
END;
Martin FLEURENT - Usine Developer - SDK maintainer

bsork
Site Admin
Posts: 1334
Location: Asker, Norway
Contact:

Unread post by bsork » 19 Jan 2010, 01:29

Sorry, I (once again) forgot to take into account that the midi output has to zeroed or else it just keeps getting sent. My bad :(

It's getting late, so this is probably not the most elegant code, but it works:

Code: Select all

// resets Mr ray's oscillators on a patch change


var startTime : Single;
var doChange, noteOnsSent, setZeroLength, noteOffsSent : Boolean;
var i            : integer;
var noteOn       : TMidi;
var noteOff      : TMidi;

var output  : Tparameter;
var volOut  : Tparameter;
var trigger : Tparameter;

// initialisation : create parameters
procedure init;
begin  
 Output := CreateParam('Midi Out',ptMidi);                   SetIsInput(Output,false);
 VolOut := CreateParam('Vol Out',ptDataField);               SetIsInput(volOut,false);
 trigger := CreateParam('trig',ptButton);                    SetIsOutput(Trigger,false); 


end;

Procedure SendNoteOns;
begin
    SetLength(outPut,24);      // set the number of output codes
    for i := 0 to 23         // loop for all input codes, for polyphonic data (chords)
    do begin
      noteOn.data1 := i;
      noteOn.data2 := 1;
      noteOn.msg := 144; 
      noteOn.channel := 1;
      SetMidiArrayValue(output,i,noteOn); // set output value
    end
end;

Procedure SendNoteOffs;
begin               
    SetLength(outPut,24);      // set the number of output codes
    for i := 0 to 23 do begin               
        noteOff.data1 := i;
        noteOff.data2 := 0;
        noteOff.msg := 128;
        noteOff.channel := 1;
        SetMidiArrayValue(output,i,noteOff); // set output value 
    end;
end;


PROCEDURE Callback (n : Integer);  // only 1 input, trigger....
begin
    
   if n = trigger then begin  
       startTime := TimeMs;
       doChange :=  TRUE;
       noteOffsSent := FALSE;
       SetValue(volOut, 0);
       SendNoteOns;
       noteOnsSent := TRUE;
       setZeroLength := FALSE;
   end;
end;

PROCEDURE Process;
BEGIN
   IF (doChange) THEN BEGIN
      IF ((TimeMs - startTime) >= 700.0) THEN BEGIN
         doChange := FALSE;
         SetValue(volOut, 1);
      END
      ELSE IF (((TimeMs - startTime) >= 200.0) AND (NOT noteOffsSent)) THEN BEGIN
         SendNoteOffs;
         noteOffsSent := TRUE;
      END
      ELSE IF (noteOnsSent AND NOT setZeroLength) THEN BEGIN
         setZeroLength := TRUE;
      END
      ELSE IF (setZeroLength) THEN BEGIN
         SetLength(outPut, 0);
      END;
   END;
END;
Doesn't ray22 recognize the lowest octave, or why else do you start at NoteNo 12? If that's the case, you can use the loop construction 0..23 with 12 added to data1 for both Ons and Offs.There's no need to "keep counting" from where you left off.
Bjørn S

bsork
Site Admin
Posts: 1334
Location: Asker, Norway
Contact:

Unread post by bsork » 19 Jan 2010, 12:05

Some comments about Callback and Process:

Callback is called once for each updated input parameter in a execution before Process is called. Setting the length or value of a parameter can be done from either procedure, but any assignments done in Process will override anything called from Callback.

BTW, I really welcome the new structure: It makes for much cleaner coding. For a small script like yours, there's not much too gain, but with a lot parameters things get a lot simpler when one can fetch all or most of the data in one place, for instance:

Code: Select all

VAR pMidiIn, pMidiOut, pSwitch, pButton : tParameter;
VAR numIn : Integer;
VAR switchOn : Boolean;
...
PROCEDURE Init;
BEGIN
    pMidiIn := CreateParam('midi in', ptMidi); SetIsOutput(pMidiIn, FALSE);
    pMidiOut := CreateParam('midi out', ptMidi); SetIsInput(pMidiIn, FALSE);
    pSwitch := CreateParam('switch', ptSwitch); SetIsOutput(pSwitch, FALSE);
    pButton := CreateParam('switch', ptButton); SetIsOutput(pButton, FALSE);
   ...
END;
...
PROCEDURE Callback(n : Integer);
BEGIN
   CASE n OF
     pMidiIn : numIn := trunc(GetValue(n));
     pSwitch : switchOn := (GetValue(n) > 0.0);
     pButton : DoSomeThing;
     ...
   END;
END;

PROCEDURE Process;
   VAR i : Integer;
   VAR m : tMidi;
BEGIN
   IF (switchOn) THEN 
      ...
   FOR i := 0 TO (numIn - 1) THEN DO
      GetMidiArrayValue(pMidiIn, i, m);
      ...
END;
Bjørn S

woodslanding
Member
Posts: 1327
Contact:

Unread post by woodslanding » 19 Jan 2010, 19:55

I guess I figured it might not see notes below the range of the piano. But the lower the note the longer the release, so I might want to add 60 to everything, just so there's a clean volume release.

I'll study this. I see there is a litte trickery to get the output length set to zero at the beginning....
Custom Ryzen 5900x MATX build, Win10, Fireface UFX, touchscreen
Custom 2 manual midi keyboard
Usine, Kontakt, Reaktor, Synthmaster, Byome, Arturia, Soundtoys, Unify

Post Reply

Who is online

Users browsing this forum: No registered users and 73 guests