Run_file command in a loop

Can I make the melody stored in a file to play in loop

Can I have
run_file(‘C:\Music\song.rb’)
in a loop like a VLC media player loop.
After finishing run_file(‘C:\Music\song.rb’), again run_file(‘C:\Music\song.rb’) has to start

Hey @vinodv :slight_smile:
I’m assuming when you mention VLC, it’s just as an example to compare to.

Anyway, what you describe sounds fairly easy to try as a short experiment - do you have any ideas yet about how you might do such a thing? the kinds of commands you might need? It sounds like you’ve already seen what run_file is for, so you’re already halfway there! The great thing about Sonic Pi is that you can just try something out and get immediate feedback :slight_smile:

(Also, I should mention that I’m absolutely happy to help you! I’m just curious about any thoughts you might have had about it already).

Grosso modo @ethancrawford invites you to try :smiley:. This is the sonic pi slogan : just try it !

I have tried in_thread and 5.times. In between a sleep 1 added.
No success.

Sure, no problem :slight_smile: and the answer is yes, btw.

There are a couple of places in the documentation that are useful for what you’re after. I assume you’ve already seen the documentation for run_file in the ‘Lang’ section. Since you’re also after a way to loop, then it’s worth reading the Tutorial chapter that introduces that concept:

That alone would probably give you enough information to produce what you want.
However, the basic type of loop introduced in that chapter is only useful in limited circumstances - so it’s also (eventually) worth knowing about the more advanced type of loop, live_loop.
To fully grasp that, it helps to understand functions and threads too (since they are really what make up a live_loop) - so might be worth reading up on these in turn:

Having said all that,
If we were to think about your goal here in your original question, in terms of a kind of ‘pseudo-code’, (ie, imaginary ‘almost code’) it’s probably close to something as simple as this:

begin loop
  run_file(X)
  sleep for the total **time** length of file X
end loop

(Oh yes, since run_file executes code in a separate thread, it will act as if the loop cycles around in zero time, so instead, you need to sleep for the same time duration that all of the commands in file X take).

I’d definitely encourage you to read through the above tutorial chapters though (the whole thing is useful honestly!), as that will give you a good foundation and there are Sonic Pi examples you can test as you go along :grinning_face_with_smiling_eyes:

Feel free to do that, continue to experiment, share code that you try, and share your thoughts during the process too (it’s definitely helpful to know that) - I hope we can continue to help you to have a successful and enjoyable Sonic Pi journey :slight_smile:

Ok the best thing to do is to show us your code. Without code, we can not tested or see the issues you meet.
See you

define :foo do
run_file(‘C:\Music\song.rb’)

end
live_loop:a do
foo
sleep 1200 #total duration of file
end

The above code I tried. Did not work out.

Hmm. Ok! In what way did it not work?
Ah. Is it giving you a giant gobbledygook error similar to this? No, that would just be me using the wrong quotes :joy:

If you show us screenshots or errors, or describe the problem, it’s a lot easier to understand what’s going on beyond ‘it didn’t work’ - particularly since we can’t see what’s in the external file :slight_smile:

It is possible to repeat a run_file command, or indeed to have a series of different run_file commands, rather like in a vlc playlist. What you need to realise is that using run_file effectively sends one command to Sonic Pi instructing it to start running the code contained in the file. this effectively takes zero time tp execute, with the process being started in its own thread structure. So if you follow this with a second run_file command it will effectively start at the same time, and the outputs of the two processes will be overlaid. What you want is for the second run+file command to be issued once the first file has completed. Note this will only be possible if the first file contains commands that will finish in a certain time. If it contains for example lust a live_loop then it will continue playing indefinitely until stop is pressed.
If it is a sequence of commands that will complete, what you can do is to add a cue command right at the end. This can be be picked up by a sync statement in the programs that started the run_file and used to allow the next runfile command to start.
I used this in the largest project I have ever done in Sonic Pi which was to code the entire Mozart Requiem, with a separate program for each of the14 movements. Each movement’s code sent a cue when it completed which allowed the next one to start. In most cases I added a 4 beat pause between each one. The main program is shown below to see how this worked. (note this won’t work on its own)

#Mozart Requiem in D minor complete, coded by Robin Newman, December 2016
puts "Start"
run_file "~/Documents/Requiem/Requiem01Aeternam-samples-RF.rb"
sync :R2
use_bpm 60
sleep 4
run_file "~/Documents/Requiem/Requiem02Kyrie-samples-RF.rb"
sync :R3
sleep 4
run_file "~/Documents/Requiem/Requiem03DiesIrae-samples-RF.rb"
sync :R4
sleep 4
run_file "~/Documents/Requiem/Requiem04TubaMirum-samples-RF.rb"
sync :R5
sleep 4
run_file "~/Documents/Requiem/Requiem05RexTremendae-samples-RF.rb"
sync :R6
sleep 4
run_file "~/Documents/Requiem/Requiem06Recordare-samples-RF.rb"
sync :R7
sleep 4
run_file "~/Documents/Requiem/Requiem07Confutatis-samples-RF.rb"
sync :R8
#no gap. Straight on
run_file "~/Documents/Requiem/Requiem08Lacrimosa-samples-RF.rb"
sync :R9
sleep 4
run_file "~/Documents/Requiem/Requiem09DomineJesu-samples-RF.rb"
sync :R10
sleep 4
run_file "~/Documents/Requiem/Requiem10Hostias-samples-RF.rb"
sync :R11
sleep 4
run_file "~/Documents/Requiem/Requiem11Sanctus-samples-RF.rb"
sync :R12
sleep 4
run_file "~/Documents/Requiem/Requiem12Benedictus-samples-RF.rb"
sync :R13
sleep 4
run_file "~/Documents/Requiem/Requiem13AgnusDei-samples-RF.rb"
sync :R14
sleep 4
run_file "~/Documents/Requiem/Requiem14LuxAeterna-samples-RF.rb"
sync :R15
puts "Ended"

In this example the end of the first file contains the command cue :R2 then end of the second one cue :R3 etc
It is important in this case that the cues are accessed as symbols so that they can pass from one application (the running run_file back to the main program eg :R2 instead of R2

3 Likes

Fantastic reminder Robin, sync and cue are definitely a good choice if you don’t want to calculate the sleep times etc.

The file got played only once and then stopped.

Fair enough! Without the external file though, it is still pretty hard to know what exactly might be causing that :slight_smile:

loop code:

define :foo do
run_file(‘C:\Music\song.rb’)

end
live_loop:a do
foo
sleep 16
end

song.rb contents

use_bpm 120
use_synth :piano

n1 = [:r, :r, :G5, :F5]
d1 = [2, 6, 2, 6]
n2 = [:E2, :E3, :D2, :D3]
d2 = [2, 6, 2, 6]

in_thread do
play_pattern_timed n1,d1
end
play_pattern_timed n2,d2

There is a silence of 16 beats between repeated plays.

Ah, yep. I can see how this might be a little confusing at first.
It’s almost correct for what you’re after.

What’s happening here if I remember rightly (someone correct me otherwise!) is that roughly speaking, things like use_bpm only act upon the thread or live_loop that they are written in. (They are ‘thread-local’).

Where you actually call run_file, (you could say, at the ‘top level’), all that code runs in its own (top level) thread, and since there are no instructions there to change the BPM, it uses the default 60 BPM.
Since you have code in the external file to use a different BPM, when the external file runs in a separate thread, we end up with two threads using two different BPM.

A solution in this case is fairly simple: you can just add another use_bpm 120 to the top of your ‘top level’ code - at the start of the buffer for example.

Let me play the devil’s advocate this time: Unless this is a test scenario to check out how to use functions as well as the run_file command, I can not see any use of using a function or the run_file command in this case; it just makes thinks more complicated. My advice would be to start with simple constructions and then gradually use more complicated ones in case the simple ones do not provide what you need musically.

But again: this is just the devil’s advocate speaking. Everything is possible :wink: .

use_bpm 120
define :foo do
  run_file('C:\Users\vinodv\Desktop\music\Keyboard\sonic\SonicPINotation\Willow_TaylorSwift_Test.rb')
  
end
live_loop:a do
  foo
  sleep 16
end

the above code works

1 Like

Great!
Depending on your final goal for this project, there may be other ways to achieve the same result, like Martin says… (There is no right or wrong way of course, but who knows, maybe you don’t even need to call out to external files!).
If you’re happy with this one, good - but it never hurts to keep exploring! :smiley:

One thing to try is:

live_loop :repeating_file_call do
  eval_file "C:/Music/song.rb"
end

Note that run_file will run the contents of the file as if you had pressed the Run button - this essentially creates a new thread to run the code so the thing that called run_file does not wait for it to finish (which is why you were having to explicitly call sleep to wait). However eval_file runs the contents of the file within the current thread as if it was a function call - so the existing thread will wait for it to finish before moving onto the next line.

3 Likes

The code is working.