Shift time forwards or backwards for the given block

time_warp  delta_time (number)

The code within the given block is executed with the specified delta time shift specified in beats. For example, if the delta value is 0.1 then all code within the block is executed with a 0.1 beat delay. Negative values are allowed which means you can move a block of code backwards in time. For example a delta value of -0.1 will execute the code in the block 0.1 beats ahead of time. The time before the block started is restored after the execution of the block.

Given a list of times, run the block once after waiting each given time. If passed an optional params list, will pass each param individually to each block call. If size of params list is smaller than the times list, the param values will act as rings (rotate through). If the block is given 1 arg, the times are fed through. If the block is given 2 args, both the times and the params are fed through. A third block arg will receive the index of the time.

Note that the code within the block is executed synchronously with the code before and after, so all thread locals will be modified inline - as is the case for with_fx. However, as time is always restored to the value before time_warp started, you can use it to schedule events for the future in a similar fashion to a thread (via at or in_thread) without having to use an entirely fresh and distinct set of thread locals - see examples.

Also, note that you cannot travel backwards in time beyond the current_sched_ahead_time.

If the time_warp block is within a density block, the delta time is not affected (although all the other times such as sleep and phase durations will be affected) - see example.

time_warp is ahead-of-time scheduling within the current thread. See at for just-in-time scheduling using multiple isolated threads.

Introduced in v2.11

Examples

# Example 1


play 70           
sleep 1
play 75           
time_warp 0.1 do
                  
  play 80         
  sleep 0.5
  play 80         
end               
                  
                  
                  
play 70           



# shift forwards in time
#=> plays at time 0
 
#=> plays at time 1
 
# time shifts forward by 0.1 beats
#=> plays at 1.1
 
#=> plays at 1.6
# time shifts back by 0.6 beats
# we now honour the original sleep 1 and the
# sleep 0.5 within the time_warp block is
# ignored including the 0.1 shift offset
#=> plays at 1



# Example 2


play 70           
sleep 1
play 75           
time_warp -0.1 do
                  
  play 80         
  sleep 0.5
  play 80         
                  
end
                  
                  
                  
play 70           



# shift backwards in time
#=> plays at time 0
 
#=> plays at time 1
 
# time shifts backwards by 0.1 beats
#=> plays at 0.9
 
#=> plays at 1.4
# time shifts forward by 0.1 beats
 
# we now honour the original sleep 1 and the
# sleep 0.5 within the time_warp block is
# ignored, including the -0.1 offset
#=> plays at 1



# Example 3


puts tick         
sleep 1
time_warp 2 do
  puts tick       
end
sleep 0.5
puts tick         



# Ticks count linearly through time_warp
#=> prints 0 (at time 0)
 
 
#=> prints 1 (at time 3)
 
 
#=> prints 2 (at time 1.5)



# Example 4


puts tick         
sleep 0.5
puts tick         
time_warp 2 do
  puts tick       
  sleep 0.5
  puts tick       
end
at 3 do           
  puts tick       
  sleep 0.5
  puts tick       
end
sleep 0.5
puts tick         



# Comparing time_warp with at
#=> prints 0 (at time 0)
 
#=> prints 1 (at time 0.5)
 
#=> prints 2 (at time 2.5)
 
#=> prints 3 (at time 3)
 
# the at will reset all thread locals
#=> prints 0 (At time 3.5)
 
#=> prints 1 (At time 4)
 
 
#=> prints 4 (at time 1)



# Example 5


density 2 do                       
                                   
  time_warp 0.5 do                 
    with_fx :slicer, phase: 0.5 do 
      play 60
      sleep 1                      
    end
  end
end



# Time Warp within Density
# Typically this will double the BPM and affect all times
# in addition to looping the internal block twice
# However, this time is *not* affected and will remain 0.5
# This phase duration *is* affected and will be 0.25
 
# This time *will* be affected by the density and be 0.5
 
 
 



# Example 6


time_warp [0, 1, 2, 3] do
  puts "hello"               
end
                               
                               
                               
                               
                               
                               
                               



# Time Warp with lists of times
 
# Will print "hello" at 0, 1, 2, and 3 seconds
 
# Notice that the run completes before all the
# messages have been delivered. This is because it
# schedules all the messages at once so the program
# can complete immediately. This is unlike at which
# would appear to behave similarly, but would wait
# for all messages to be delivered (on time) before
# allowing the program to complete.



# Example 7

time_warp [1, 2, 4] do 
    play 75               
  end                     



# plays a note after waiting 1 beat,
# then after 1 more beat,
# then after 2 more beats (4 beats total)



# Example 8

time_warp [1, 2, 3], [75, 76, 77] do |n| 
    play n
  end



# plays 3 different notes
 
 



# Example 9

time_warp [1, 2, 3],
      [{:amp=>0.5}, {:amp=> 0.8}] do |p|
    sample :drum_cymbal_open, p         
  end



 
# alternate soft and loud
# cymbal hits three times
 



# Example 10

time_warp [0, 1, 2] do |t|
    puts t
  end



# when no params are given to at, the times are fed through to the block
#=> prints 0, 1, then 2
 



# Example 11

time_warp [0, 1, 2], [:a, :b] do |t, b| 
    puts [t, b]
  end



# If you specify the block with 2 args, it will pass through both the time and the param
#=> prints out [0, :a], [1, :b], then [2, :a]
 



# Example 12

time_warp [0, 0.5, 2] do |t, idx| 
    puts [t, idx]
  end



# If you specify the block with 2 args, and no param list to at, it will pass through both the time and the index
#=> prints out [0, 0], [0.5, 1], then [2, 2]
 



# Example 13

time_warp [0, 0.5, 2], [:a, :b] do |t, b, idx| 
    puts [t, b, idx]
  end



# If you specify the block with 3 args, it will pass through the time, the param and the index
#=> prints out [0, :a, 0], [0.5, :b, 1], then [2, :a, 2]
 



# Example 14


puts "main: ", rand 
rand_back
time_warp 1 do        
                      
  puts "time_warp:", rand
  puts "time_warp:", rand
  rand_back          
end
sleep 2
puts "main: ", rand



# time_warp consumes & interferes with the outer random stream
# 0.75006103515625
 
# the random stream inside the at block is the
# same as the one in the outer block
# 0.75006103515625
# 0.733917236328125
# undo last call to rand
 
 
# value is now 0.733917236328125 again



# Example 15


           
           
time_warp [0, 2] do
           
  puts tick
  puts tick
end
           
           
           



# Each block run inherits the same thread locals from the previous one.
# This means things like the thread local counters can flow through
# time warp iterations:
 
# first time round (after 1 beat) prints:
# 0
# 1
 
# second time round (after 2 beats) prints:
# 2
# 3