< Back to IRCAM Forum

Controlling process variables with curves

Hi,

I’d like to know if it is possible to use a curve to control a variable inside a process?

Let’s say I have a process that sends MIDI notes from a given list in a loop with an interval of time, ::repeat($noteList, $loopInterval). Sometimes I want to give the second parameter a static value, but other times I’d like to change it progressively, so I don’t want to have a curve inside the process. Is it possible to create a curve that controls this variable alone?

I’ve tried to do something like

curve myCurve @grain := 0.05s, @action := ::repeat([60,61,62], $x) 
{ $x *etc*}

but that returns a syntax error and I guess it would initiate a new process every 0.05s, so that’s not it.

Hello.
You are just missing a pair of curly brackets for the action in the curve. The following program seems to do what you describes :

@proc_def ::repeat($l, $x)
{
    @local $i := 0
    loop $x
    {
         midout ($l[$i])
         $i += 1
    } during[$l.size()#]
}

curve myCurve 
@grain := 0.5s
@action { ::repeat([60,61,62], $x) }
{
     $x {{1} 10 {10}}
}

For each sample of the Cuve (i.e., each 0.05s), a process is spanned and the list of pitch is sent to midiout, with an interval of $x where $x is the value of the sample.

Note that a Curve can be seen as a NIM reader and A nim is a value that can be passed as an argument or directly used to specify a tempo. For example:

@proc_def ::repeat($l, $tempo)
{
  @local $i := 0
  loop 1
    @tempo := $tempo
  {
    midiout ($l[$i]) at $NOW
     $i += 1
  } during [$l.size()#]
}

::repeat([61, 62, 63], NIM{ 0 60, 5 60 })

10
$tempo :=  NIM{ $RNOW 60, ($RNOW+3) 120 "exp_out"}
::repeat([81, 82, 83], $tempo)

The execution will provide something like:

midiout 61 at 0.0 
midiout 62 at 1.0 
midiout 63 at 2.0 
midiout 81 at 10.0 
midiout 82 at 10.8285 
midiout 83 at 11.4781 

The first call is done with a nim specifying a constant tempo at 60. This tempo is used to interpret the period of the loop which is 1 beat.

The second nim is a tempo going from 60 to 90 in 3 beats. The nim is used for the tempo and the second process starts after 10 beats. Instead of keeping track the date of the start of the process, we use $RNOW to query the current date and use it to compute the relevant tempo curve. The curve is an exponential one: it starts slowly and accelerate at the end, so the second note is sent 0.82 sec after the first and the third 0.65 after the second.

Perhaps this approach is not the better way to control the duration of the loop (because it is hard to specify the length of the tempo curve : the tempo curve is read in the ‘ambiant’ time (by default $RNOW, from the musician) and the loop duration is 3 beats but in the temporal reference frame driven by the tempo so its duration in the mlusician reference frame is not obvious.

Perhaps this is not what you want. Can you be more specific on the expected result ?

1 Like

I’m trying to create process that imitate gestures on a piano for a project with a Disklavier, algorithmically generating material with Antescofo’s language. The processes have more details to them, but the example with repeated notes should give an idea.

Thanks for the correction on the syntax and for the NIM suggestion! I think the NIM in the process call will be the most efficient choice for me, I’ll use that.

Thank you very much,
Raphael

I actually tried the solutions and while the NIM solutions did work perfectly for this situation, I could not make it work for another variables. Let’s say I want to increase velocity of midi notes independently during the process with another variable. Can I use a NIM to control the variable along time? I tried and got this error

antescofo~: Internal error: Bad type: expect a string and get a Nim (in get_error)
Does it only work with some attributes, like @tempo?

The curve solution actually doesn’t work. As I expected, it does creates a new instance of the process every grain period and I wanted it to change the value inside the one process.

The output from the code

curve myCurve 
@grain := 0.5s
@action { ::repeat([60,61,62], $x) }
{
     $x {{1} 10 {10}}
}

until it goes to the first cycle is:

print: 60
print: 60
print: 61
print: 60
print: 60
print: 61
print: 62
print: 60
print: 60
print: 61
print: 60
print: 62

Which I believe is caused by the same process beginning while the other instances have being created. If I make the grain smaller, it does increases the number of "60"s before the first “61”

Can you send me the program that causes the “internal error”? Without the program, it is hard to tell what’s going wrong.

The solution with the curve outside the process launches the process at each sample, as noted, and this is not what you expect. If I understand correctly, the solution with the NIM passed as an argument works as you intended but you weren’t able to extend it to handle the velocity. Notice however that I have made a mistake inine previous code. Instead of writing

$tempo :=  NIM{ $RNOW 60, ($RNOW+3) 120 "exp_out"}

you probably should write

$tempo :=  NIM{ $RNOW 60, 3 120 "exp_out"}

because the nim start at $RNOW but then you specify the duration of the breakpoints, not there absolute date.

It is possible to use a second NIM to control the velocity, in parallel to the NIM used for the delay between note. I give an example below. But perhaps here it is mores simple to have a vector of inter-onset and a vector of velocity to specify these quantity alongside the vector of pitches:

@proc_def ::repeat($l, $del, $vel)
{
  @local $i := 0
  loop $del
  {
      midiout ($l[$i]) vel ($vel[$i]) at $NOW 
      $i += 1
  } during [$l.size()#]
}

::repeat([61, 62, 63], [1, 1.2], [100, 120, 90])

will produce

midiout 61 vel 100 at 0.0
midiout 62 vel 120 at 1.0
midiout 63 vel 90 at 2.2

If you have the duration of each note instead of their inter-onset, it is easy to modify the process:

@proc_def ::repeat($l, $del, $vel)
{
  @local $i := 0
  loop $del
  {
    midiout ($l[$i]) dur ($del[$i]) vel ($vel[$i]) at $NOW 
    $i += 1
  } during [$l.size()#]
}

::repeat([61, 62, 63], [1, 1.2, 0.9], [100, 120, 90])

You can mix the nim based approach for the tempo with the vector based approach for the velocity (the vector of velocities is accessed with the same index as the vector of midi pitches).

As I mentioned before, you can also use a nim to compute the duration of each note. You have to decide how to query the nim for the velocity. If you use $RNOW, you can write:

@proc_def ::repeat($l, $tempo, $vel)
{
    @local $i := 0
    loop 1
      @tempo := $tempo 
    {
        midiout ($l[$i]) vel ($vel($RNOW)) at $NOW
        $i += 1
    } during [$l.size()#]
}

::repeat([61, 62, 63],
	 NIM{ 0 60, 5 60 },
	 NIM{ 0 90, 2 110, 3 100}
	 )

10
$tempo :=  NIM{ $RNOW 60, 3 120 "exp_out"}
$vel := NIM{ $RNOW 90, 0.8285 130, 1 100}
::repeat([81, 82, 83], $tempo, $vel)

and the trace is:

midiout 61 vel 90.0 at 0.0
midiout 62 vel 100.0 at 1.0
midiout 63 vel 110.0 at 2.0
midiout 81 vel 90.0 at 10.0
midiout 82 vel 121.151 at 10.6452
midiout 83 vel 120.19 at 11.1555

But as I mentioned in my first post, this approach is not the better way to control the duration and the velocity because you have to specify a continuous curve for defining a set of discrete values (duration and velocity of each note). The vector based approach is perhaps more suited ? what can of algorithmics do you envision to generates these data?

These NIM applications work perfectly!

I couldn’t use the first solution with the velocity or delay for each note because the process repeats many times with the velocity changing independently. Like a three note sequence that repeats with a big crescendo along time, for example.

The internal error was caused because I didn’t know how to access the NIM value at a given time, so I thought if @ tempo := $tempo worked, something like $vel (instead of $vel($RNOW)) would work too.

This recreates the error, if you want to see it for yourself

@proc_def ::repeat($l, $tempo, $vel)
{
    @local $i := 0
    loop 1
      @tempo := $tempo 
    {
        midiout ($l[$i]) vel ($vel) at $NOW
        $i += 1
    } during [$l.size()#]
}


::repeat([61, 62, 63],
	 NIM{ 0 60, 5 60 },
	 NIM{ 0 90, 2 110, 3 100}
	 )

Thank you very much for your help!! :slight_smile:

Fine :slight_smile:

If needed, it is easy to manage two indices, to scan the pitch and the velocity list independently, and so having lists of different lengths.

But if the NIM approach works well, don’t change it.

All the best,
Jl.