Piano framework for VSCO piano

Hi there,

I’m new here and would like to share a piano framework with you, I created.
It uses the VSCO Upright Piano samples for playing and frees you from reading the sfz-file for using the right samples and pitching to get the right notes.
You can use the my_piano_raw* functions to play the piano and have full control on the effect hull curve or you can use the my_piano_play* functions to play a partiture directly.
I integrated a small example song part, too, for easy introduction and commented all functions and parameters to understand what they do.

To start with it you just have to edit the my_piano_dir variable to set the path to your VSCO Upright Piano samples.
I would really like to know what you think about it and especially if you have any better ideas for the tuning variables my_piano_nonleg_length, my_piano_staccato_length, my_piano_legato_length.

So, here is the framework:

# Framework for the VSCO piano
#
# Author: minced mind
# License: CC0 (see https://creativecommons.org/share-your-work/public-domain/cc0/)
# Version: 0.1

# Global variables
# Defines the directory of the piano samples from VSCO
my_piano_dir = "~/music-making/samples/VSCO-2-CE-SFZ/Keys/Upright Piano/"

# Length of a full note in seconds, often (current_bpm / 15)
my_piano_full_note_length = (current_bpm / 15)

# Relative length of a non-legato note length
my_piano_nonleg_length = 0.8

# Relative length of a staccato note length
my_piano_staccato_length = 0.5

# Relative length of a legato note length
my_piano_legato_length = 1.1



# The function my_piano_sample is used from the other framework functions to select the right sample
# Parameters:
# my_note = note value which should be played
# my_dyn = the dynamic (loudness) which should be played (1, 2, 3)
define :my_piano_sample do |my_note, my_dyn|
  my_piano_list_dyn1 = ["Player_dyn1_rr1_000", "Player_dyn1_rr1_002", "Player_dyn1_rr1_004",
                        "Player_dyn1_rr1_006", "Player_dyn1_rr1_008", "Player_dyn1_rr1_010",
                        "Player_dyn1_rr1_012", "Player_dyn1_rr1_014", "Player_dyn1_rr1_016",
                        "Player_dyn1_rr1_018", "Player_dyn1_rr1_020", "Player_dyn1_rr1_022",
                        "Player_dyn1_rr1_024", "Player_dyn1_rr1_026", "Player_dyn1_rr1_028",
                        "Player_dyn1_rr1_030", "Player_dyn1_rr1_032", "Player_dyn1_rr1_034",
                        "Player_dyn1_rr1_036", "Player_dyn1_rr1_038", "Player_dyn1_rr1_040",
                        "Player_dyn1_rr1_042", "Player_dyn1_rr1_044"]
  
  my_piano_list_dyn2 = ["Player_dyn2_rr1_000", "Player_dyn2_rr1_002", "Player_dyn2_rr1_004",
                        "Player_dyn2_rr1_006", "Player_dyn2_rr1_008", "Player_dyn2_rr1_010",
                        "Player_dyn2_rr1_012", "Player_dyn2_rr1_014", "Player_dyn2_rr1_016",
                        "Player_dyn2_rr1_018", "Player_dyn2_rr1_020", "Player_dyn2_rr1_022",
                        "Player_dyn2_rr1_024", "Player_dyn2_rr1_026", "Player_dyn2_rr1_028",
                        "Player_dyn2_rr1_030", "Player_dyn2_rr1_032", "Player_dyn2_rr1_034",
                        "Player_dyn2_rr1_036", "Player_dyn2_rr1_038", "Player_dyn2_rr1_040",
                        "Player_dyn2_rr1_042", "Player_dyn2_rr1_044"]
  
  my_piano_list_dyn3 = ["Player_dyn3_rr1_000", "Player_dyn3_rr1_002", "Player_dyn3_rr1_004",
                        "Player_dyn3_rr1_006", "Player_dyn3_rr1_008", "Player_dyn3_rr1_010",
                        "Player_dyn3_rr1_012", "Player_dyn3_rr1_014", "Player_dyn3_rr1_016",
                        "Player_dyn3_rr1_018", "Player_dyn3_rr1_020", "Player_dyn3_rr1_022",
                        "Player_dyn3_rr1_024", "Player_dyn3_rr1_026", "Player_dyn3_rr1_028",
                        "Player_dyn3_rr1_030", "Player_dyn3_rr1_032", "Player_dyn3_rr1_034",
                        "Player_dyn3_rr1_036", "Player_dyn3_rr1_038", "Player_dyn3_rr1_040",
                        "Player_dyn3_rr1_042", "Player_dyn3_rr1_044"]
  
  my_midi_num = note(my_note)
  
  if my_midi_num < 21
    assert false, "Note too low for my_piano_sample"
  end
  
  if my_midi_num > 108
    assert false, "Note too high for my_piano_sample"
  end
  
  if my_dyn < 1
    assert false, "Dynamic too low for my_piano_sample"
  end
  
  if my_dyn > 3
    assert false, "Dynamic too high for my_piano_sample"
  end
  
  case my_dyn
  when 1
    my_piano_list = my_piano_list_dyn1
  when 2
    my_piano_list = my_piano_list_dyn2
  when 3
    my_piano_list = my_piano_list_dyn3
  end
  
  case my_midi_num
  when 21
    sample my_piano_dir, my_piano_list[0]
  when 22
    sample my_piano_dir, my_piano_list[0], rpitch: 1
  when 23..106
    my_listnum = (my_midi_num - 19) / 4
    my_pitch = ((my_midi_num - 23) % 4) - 2
    
    if my_pitch == 0
      sample my_piano_dir, my_piano_list[my_listnum]
    else
      sample my_piano_dir, my_piano_list[my_listnum], rpitch: my_pitch
    end
  when 107
    sample my_piano_dir, my_piano_list[22], rpitch: -1
  when 108
    sample my_piano_dir, my_piano_list[22]
  end
end

# Play a note with a given dynamic. With this you can control the whole effect hull curve.
# Parameters:
# my_note = note value to play
# my_dyn = dynamic to play (1 = quiet, 2 = louder, 3 = loud)
#
# Please note, the sfz-file recommends a release of 0.6
#
# Examplecall:
# Play note C at the 4th octave with 1 second sustain and loud dynamic
# with_sample_defaults amp: 1.0, sustain: 1.0, release: 0.6 do
#   my_piano_raw :C4, 3
# end
define :my_piano_raw do |my_note, my_dyn|
  my_piano_sample my_note, my_dyn
end

# Play a chord with a given dynamic. With this you can control the whole effect hull curve.
# Parameters:
# my_chord = chord to play
# my_dyn = dynamic to play (1 = quiet, 2 = louder, 3 = loud)
#
# Please note, the sfz-file recommends a release of 0.6
#
# Examplecall:
# Play a C-major chord of the 4th octave with 1 second sustain and loud dynamic
# with_sample_defaults amp: 1.0, sustain: 1.0, release: 0.6 do
#   my_piano_raw_chord chord(:C4, :major), 3
# end
define :my_piano_raw_chord do |my_chord, my_dyn|
  my_chord.each do |i|
    my_piano_sample i, my_dyn
  end
end

# Play a note with a given note length value
# Parameters:
# my_note = note value to play
# my_note_length = note length to play
# my_artic = articulation to use ("non" = non-legato, "stac" = staccato, "leg" = legato)
# my_dyn = dynamic to play ("ppp", "pp", "p", "mp", "mf", "f", "ff", "fff")
#
# Examplecall:
# Play a full note C at the 4th octave in non-legato with mezzo forte loudness
# my_piano :C4, 1.0, "non", "mf"
define :my_piano_play do |my_note, my_note_length, my_artic, my_dyn|
  my_sleep_length = my_note_length * my_piano_full_note_length * (current_bpm / 60)
  my_sample_dyn = 0
  
  case my_artic
  when "non"
    my_play_length = my_note_length * my_piano_full_note_length * my_piano_nonleg_length
    my_release_length = 0.6
  when "stac"
    my_play_length = my_note_length * my_piano_full_note_length * my_piano_staccato_length
    my_release_length = 0.6 * my_piano_staccato_length
  when "leg"
    my_play_length = my_note_length * my_piano_full_note_length * my_piano_legato_length
    my_release_length = 0.6 * my_piano_legato_length
  end
  
  case my_dyn
  when "ppp"
    my_sample_dyn = 1
  when "pp"
    my_sample_dyn = 1
  when "p"
    my_sample_dyn = 1
  when "mp"
    my_sample_dyn = 1
  when "mf"
    my_sample_dyn = 2
  when "f"
    my_sample_dyn = 2
  when "ff"
    my_sample_dyn = 2
  when "fff"
    my_sample_dyn = 3
  end
  
  if my_sample_dyn == 0
    assert false, "Dynamic unknown for my_piano_play"
  end
  
  if my_note == "pause"
    sleep my_sleep_length
  else
    with_merged_sample_defaults sustain: my_play_length, release: my_release_length do
      my_piano_sample my_note, my_sample_dyn
      sleep my_sleep_length
    end
  end
end

# Play a chord with a given note length value
# Parameters:
# my_chord = chord to play
# my_note_length = note length to play
# my_artic = articulation to use ("non" = non-legato, "stac" = staccato, "leg" = legato)
# my_dyn = dynamic to play ("ppp", "pp", "p", "mp", "mf", "f", "ff", "fff")
#
# Examplecall:
# Play a full note length C-major chord of the 4th octave in non-legato with mezzo forte loudness
# my_piano_chord chord(:C4, :major), 1.0, "non", "mf"
define :my_piano_play_chord do |my_chord, my_note_length, my_artic, my_dyn|
  my_sleep_length = my_note_length * my_piano_full_note_length * (current_bpm / 60)
  my_sample_dyn = 0
  
  case my_artic
  when "non"
    my_play_length = my_note_length * my_piano_full_note_length * my_piano_nonleg_length
    my_release_length = 0.6
  when "stac"
    my_play_length = my_note_length * my_piano_full_note_length * my_piano_staccato_length
    my_release_length = 0.6 * my_piano_staccato_length
  when "leg"
    my_play_length = my_note_length * my_piano_full_note_length * my_piano_legato_length
    my_release_length = 0.6 * my_piano_legato_length
  end
  
  case my_dyn
  when "ppp"
    my_sample_dyn = 1
  when "pp"
    my_sample_dyn = 1
  when "p"
    my_sample_dyn = 1
  when "mp"
    my_sample_dyn = 1
  when "mf"
    my_sample_dyn = 2
  when "f"
    my_sample_dyn = 2
  when "ff"
    my_sample_dyn = 2
  when "fff"
    my_sample_dyn = 3
  end
  
  if my_sample_dyn == 0
    assert false, "Dynamic unknown for my_piano_play_chord"
  end
  
  with_merged_sample_defaults sustain: my_play_length, release: my_release_length do
    my_chord.each do |i|
      my_piano_sample i, my_sample_dyn
    end
    sleep my_sleep_length
  end
end

# Hints for note and chord notation:
# Notenames 4th octave: :C4, :Cs4, :D4, :Ds4, :E4, :F4, :Fs4, :G4, :Gs4, :A4, :Bb4, :B4
# Chordnames can be printed out with the following command:
#puts chord_names
#
# You can also use [:C4, :E4, :G4] as chord notation (here it is C-major chord of 4th octave)

#
# Examplesong
#
# "Für Elise" from Ludwig van Beethoven, first 9 tacts
my_artic = "leg"
my_dyn = "pp"

# Right hand
in_thread do
  my_piano_play :E5, 1.0/16, my_artic, my_dyn
  my_piano_play :Ds5, 1.0/16, my_artic, my_dyn
  
  my_piano_play :E5, 1.0/16, my_artic, my_dyn
  my_piano_play :Ds5, 1.0/16, my_artic, my_dyn
  my_piano_play :E5, 1.0/16, my_artic, my_dyn
  my_piano_play :B4, 1.0/16, my_artic, my_dyn
  my_piano_play :D5, 1.0/16, my_artic, my_dyn
  my_piano_play :C5, 1.0/16, my_artic, my_dyn
  
  my_piano_play :A4, 1.0/8, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
  my_piano_play :C4, 1.0/16, my_artic, my_dyn
  my_piano_play :E4, 1.0/16, my_artic, my_dyn
  my_piano_play :A4, 1.0/16, my_artic, my_dyn
  
  my_piano_play :B4, 1.0/8, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
  my_piano_play :E4, 1.0/16, my_artic, my_dyn
  my_piano_play :Gs4, 1.0/16, my_artic, my_dyn
  my_piano_play :B4, 1.0/16, my_artic, my_dyn
  
  my_piano_play :C5, 1.0/8, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
  my_piano_play :E4, 1.0/16, my_artic, my_dyn
  my_piano_play :E5, 1.0/16, my_artic, my_dyn
  my_piano_play :Ds5, 1.0/16, my_artic, my_dyn
  
  my_piano_play :E5, 1.0/16, my_artic, my_dyn
  my_piano_play :Ds5, 1.0/16, my_artic, my_dyn
  my_piano_play :E5, 1.0/16, my_artic, my_dyn
  my_piano_play :B4, 1.0/16, my_artic, my_dyn
  my_piano_play :D5, 1.0/16, my_artic, my_dyn
  my_piano_play :C5, 1.0/16, my_artic, my_dyn
  
  my_piano_play :A4, 1.0/8, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
  my_piano_play :C4, 1.0/16, my_artic, my_dyn
  my_piano_play :E4, 1.0/16, my_artic, my_dyn
  my_piano_play :A4, 1.0/16, my_artic, my_dyn
  
  my_piano_play :B4, 1.0/8, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
  my_piano_play :D4, 1.0/16, my_artic, my_dyn
  my_piano_play :C5, 1.0/16, my_artic, my_dyn
  my_piano_play :B4, 1.0/16, my_artic, my_dyn
  
  my_piano_play :A4, 1.0/8, my_artic, my_dyn
  my_piano_play "pause", 1.0/8, my_artic, my_dyn
end

# Left hand
in_thread do
  my_piano_play "pause", 1.0/8, my_artic, my_dyn
  
  my_piano_play "pause", 3.0/8, my_artic, my_dyn
  
  my_piano_play :A2, 1.0/16, my_artic, my_dyn
  my_piano_play :E3, 1.0/16, my_artic, my_dyn
  my_piano_play :A3, 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/8, my_artic, my_dyn
  
  my_piano_play :E2, 1.0/16, my_artic, my_dyn
  my_piano_play :E3, 1.0/16, my_artic, my_dyn
  my_piano_play :Gs3, 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/8, my_artic, my_dyn
  
  my_piano_play :A2, 1.0/16, my_artic, my_dyn
  my_piano_play :E3, 1.0/16, my_artic, my_dyn
  my_piano_play :A3, 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/8, my_artic, my_dyn
  
  my_piano_play "pause", 3.0/8, my_artic, my_dyn
  
  my_piano_play :A2, 1.0/16, my_artic, my_dyn
  my_piano_play :E3, 1.0/16, my_artic, my_dyn
  my_piano_play :A3, 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/8, my_artic, my_dyn
  
  my_piano_play :E2, 1.0/16, my_artic, my_dyn
  my_piano_play :E3, 1.0/16, my_artic, my_dyn
  my_piano_play :Gs3, 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/8, my_artic, my_dyn
  
  my_piano_play :A2, 1.0/16, my_artic, my_dyn
  my_piano_play :E3, 1.0/16, my_artic, my_dyn
  my_piano_play :A3, 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
end
2 Likes

Nice work. I found that the sound level was very low, even when using “fff” so I had to wrap the playing section with_fx :level,amp: 12 do ..... end in order to get reasonable volume on my Mac. Apart from that you get good control over the sounds, although it takes quite a lot of text to enter what to play (as in your fur elise example) and the whole of your well commented program is already too long to play in a buffer on a mac, and I had to use run_file to use it.

However, having written a similar exercise myself to play samples from the Sonatina Symphonic Orchestra eg this example which also needs to be used with run_file I know that this is probably inevitable.

I would be interested to see how easily adaptable your system might be to handle a range of different instruments.

EDIT PS Welcome to in_thread btw. A great first post from you!

1 Like

Hi Robin,

thank you for your feedback.
I left the amp control out of the framework, as most likely more than one instrument is often used and the amp control is then used for relative loudness of each instrument. You can use use_sample_defaults to set a default amp of 6 to 12 for example at the beginning of your song or function.
It would be possible to calculate at least relative amps in the my_piano_play* functions for the different dynamics but I don’t know which values would be fitting for the different dynamics.
I especially wrote the framework with people in mind, which are more accustomed to music than programming or sonic-pi at all. So, it is very descriptive and one can easily transform a piano partiture to a sonic-pi project, therefor it seems quite a lot of text. One could wright the example song in a more efficient way but that wouldn’t maybe help so much for beginners.
Seems strange you already need to use run_file just for that, plays fine here on an older linux system.

Kind regards

I find that the max buffer size is smaller on Macs than on Linux having tried both. Not worked out why.

Hi again,

@robin.newman
That’s bad that it gets quirky on Macs. If you know any method of reducing this, I’am all ears.

I polished some comments and integrated a dynamics handling in the framework, so you can control the loudness more easily. I added the global variables my_piano_dyn_* to control the relative loudness of each dynamic.
As I’m not a pianist, I don’t know how to tweak the relative loudness of the different dynamics and the samples already cover 3 different dynamic levels, so I kept them all to 1 (no change at all).
I would be really interested in some feedback maybe from a pianist.
Thanks you so far for your feedback.

Now, here is the next version of the framework:

# Framework for the VSCO piano
#
# Author: minced mind
# License: CC0 (see https://creativecommons.org/share-your-work/public-domain/cc0/)
# Version: 0.2

# Global variables
# Defines the directory of the piano samples from VSCO
my_piano_dir = "~/music-making/samples/VSCO-2-CE-SFZ/Keys/Upright Piano/"

# Length of a full note in seconds, often (current_bpm / 15)
# Make sure to set the bpm value first with "use_bpm <bpm-value>" otherwise it is 60 by default
my_piano_full_note_length = (current_bpm / 15)

# Relative length of a non-legato note length
my_piano_nonleg_length = 0.8

# Relative length of a staccato note length
my_piano_staccato_length = 0.5

# Relative length of a legato note length
my_piano_legato_length = 1.1

# Dynamics control:
#
# Hint for dynamics:
# To set a base loudness you can use for example the following code:
use_sample_defaults amp: 1

# Relative loudness of the different dynamics (ppp, pp, p, mp, mf, f, ff, fff):
my_piano_dyn_ppp = 1
my_piano_dyn_pp = 1
my_piano_dyn_p = 1
my_piano_dyn_mp = 1
my_piano_dyn_mf = 1
my_piano_dyn_f = 1
my_piano_dyn_ff = 1
my_piano_dyn_fff = 1


# The function my_piano_sample is used from the other framework functions to select the right sample
# Parameters:
# my_note = note value which should be played
# my_dyn = the dynamic (loudness) which should be played (1, 2, 3)
define :my_piano_sample do |my_note, my_dyn|
  my_piano_list_dyn1 = ["Player_dyn1_rr1_000", "Player_dyn1_rr1_002", "Player_dyn1_rr1_004",
                        "Player_dyn1_rr1_006", "Player_dyn1_rr1_008", "Player_dyn1_rr1_010",
                        "Player_dyn1_rr1_012", "Player_dyn1_rr1_014", "Player_dyn1_rr1_016",
                        "Player_dyn1_rr1_018", "Player_dyn1_rr1_020", "Player_dyn1_rr1_022",
                        "Player_dyn1_rr1_024", "Player_dyn1_rr1_026", "Player_dyn1_rr1_028",
                        "Player_dyn1_rr1_030", "Player_dyn1_rr1_032", "Player_dyn1_rr1_034",
                        "Player_dyn1_rr1_036", "Player_dyn1_rr1_038", "Player_dyn1_rr1_040",
                        "Player_dyn1_rr1_042", "Player_dyn1_rr1_044"]
  
  my_piano_list_dyn2 = ["Player_dyn2_rr1_000", "Player_dyn2_rr1_002", "Player_dyn2_rr1_004",
                        "Player_dyn2_rr1_006", "Player_dyn2_rr1_008", "Player_dyn2_rr1_010",
                        "Player_dyn2_rr1_012", "Player_dyn2_rr1_014", "Player_dyn2_rr1_016",
                        "Player_dyn2_rr1_018", "Player_dyn2_rr1_020", "Player_dyn2_rr1_022",
                        "Player_dyn2_rr1_024", "Player_dyn2_rr1_026", "Player_dyn2_rr1_028",
                        "Player_dyn2_rr1_030", "Player_dyn2_rr1_032", "Player_dyn2_rr1_034",
                        "Player_dyn2_rr1_036", "Player_dyn2_rr1_038", "Player_dyn2_rr1_040",
                        "Player_dyn2_rr1_042", "Player_dyn2_rr1_044"]
  
  my_piano_list_dyn3 = ["Player_dyn3_rr1_000", "Player_dyn3_rr1_002", "Player_dyn3_rr1_004",
                        "Player_dyn3_rr1_006", "Player_dyn3_rr1_008", "Player_dyn3_rr1_010",
                        "Player_dyn3_rr1_012", "Player_dyn3_rr1_014", "Player_dyn3_rr1_016",
                        "Player_dyn3_rr1_018", "Player_dyn3_rr1_020", "Player_dyn3_rr1_022",
                        "Player_dyn3_rr1_024", "Player_dyn3_rr1_026", "Player_dyn3_rr1_028",
                        "Player_dyn3_rr1_030", "Player_dyn3_rr1_032", "Player_dyn3_rr1_034",
                        "Player_dyn3_rr1_036", "Player_dyn3_rr1_038", "Player_dyn3_rr1_040",
                        "Player_dyn3_rr1_042", "Player_dyn3_rr1_044"]
  
  my_midi_num = note(my_note)
  
  if my_midi_num < 21
    assert false, "Note too low for my_piano_sample"
  end
  
  if my_midi_num > 108
    assert false, "Note too high for my_piano_sample"
  end
  
  if my_dyn < 1
    assert false, "Dynamic too low for my_piano_sample"
  end
  
  if my_dyn > 3
    assert false, "Dynamic too high for my_piano_sample"
  end
  
  case my_dyn
  when 1
    my_piano_list = my_piano_list_dyn1
  when 2
    my_piano_list = my_piano_list_dyn2
  when 3
    my_piano_list = my_piano_list_dyn3
  end
  
  case my_midi_num
  when 21
    sample my_piano_dir, my_piano_list[0]
  when 22
    sample my_piano_dir, my_piano_list[0], rpitch: 1
  when 23..106
    my_listnum = (my_midi_num - 19) / 4
    my_pitch = ((my_midi_num - 23) % 4) - 2
    
    if my_pitch == 0
      sample my_piano_dir, my_piano_list[my_listnum]
    else
      sample my_piano_dir, my_piano_list[my_listnum], rpitch: my_pitch
    end
  when 107
    sample my_piano_dir, my_piano_list[22], rpitch: -1
  when 108
    sample my_piano_dir, my_piano_list[22]
  end
end

# Play a note with a given dynamic. With this you can control the whole effect hull curve.
# Parameters:
# my_note = note value to play
# my_dyn = dynamic to play (1 = quiet, 2 = louder, 3 = loud)
#
# Please note, the sfz-file recommends a release of 0.6
#
# Examplecall:
# Play note C at the 4th octave with 1 second sustain and loud dynamic
# with_sample_defaults amp: 1.0, sustain: 1.0, release: 0.6 do
#   my_piano_raw :C4, 3
# end
define :my_piano_raw do |my_note, my_dyn|
  my_piano_sample my_note, my_dyn
end

# Play a chord with a given dynamic. With this you can control the whole effect hull curve.
# Parameters:
# my_chord = chord to play
# my_dyn = dynamic to play (1 = quiet, 2 = louder, 3 = loud)
#
# Please note, the sfz-file recommends a release of 0.6
#
# Examplecall:
# Play a C-major chord of the 4th octave with 1 second sustain and loud dynamic
# with_sample_defaults amp: 1.0, sustain: 1.0, release: 0.6 do
#   my_piano_raw_chord chord(:C4, :major), 3
# end
define :my_piano_raw_chord do |my_chord, my_dyn|
  my_chord.each do |i|
    my_piano_sample i, my_dyn
  end
end

# Play a note with a given note length value
# Parameters:
# my_note = note value to play, use special note "pause" to set a pause
# my_note_length = note length to play
# my_artic = articulation to use ("non" = non-legato, "stac" = staccato, "leg" = legato)
# my_dyn = dynamic to play ("ppp", "pp", "p", "mp", "mf", "f", "ff", "fff")
#
# Examplecall:
# Play a full note C at the 4th octave in non-legato with mezzo forte loudness
# my_piano :C4, 1.0, "non", "mf"
define :my_piano_play do |my_note, my_note_length, my_artic, my_dyn|
  my_sleep_length = my_note_length * my_piano_full_note_length * (current_bpm / 60)
  my_sample_dyn = 0
  my_amp_level = 1
  
  case my_artic
  when "non"
    my_play_length = my_note_length * my_piano_full_note_length * my_piano_nonleg_length
    my_release_length = 0.6
  when "stac"
    my_play_length = my_note_length * my_piano_full_note_length * my_piano_staccato_length
    my_release_length = 0.6 * my_piano_staccato_length
  when "leg"
    my_play_length = my_note_length * my_piano_full_note_length * my_piano_legato_length
    my_release_length = 0.6 * my_piano_legato_length
  end
  
  case my_dyn
  when "ppp"
    my_sample_dyn = 1
    my_amp_level = my_piano_dyn_ppp
  when "pp"
    my_sample_dyn = 1
    my_amp_level = my_piano_dyn_pp
  when "p"
    my_sample_dyn = 1
    my_amp_level = my_piano_dyn_p
  when "mp"
    my_sample_dyn = 1
    my_amp_level = my_piano_dyn_mp
  when "mf"
    my_sample_dyn = 2
    my_amp_level = my_piano_dyn_mf
  when "f"
    my_sample_dyn = 2
    my_amp_level = my_piano_dyn_f
  when "ff"
    my_sample_dyn = 2
    my_amp_level = my_piano_dyn_ff
  when "fff"
    my_sample_dyn = 3
    my_amp_level = my_piano_dyn_fff
  end
  
  if my_sample_dyn == 0
    assert false, "Dynamic unknown for my_piano_play"
  end
  
  if my_note == "pause"
    sleep my_sleep_length
  else
    with_merged_sample_defaults sustain: my_play_length, release: my_release_length do
      with_fx :level, amp: my_amp_level do
        my_piano_sample my_note, my_sample_dyn
      end
      sleep my_sleep_length
    end
  end
end

# Play a chord with a given note length value
# Parameters:
# my_chord = chord to play
# my_note_length = note length to play
# my_artic = articulation to use ("non" = non-legato, "stac" = staccato, "leg" = legato)
# my_dyn = dynamic to play ("ppp", "pp", "p", "mp", "mf", "f", "ff", "fff")
#
# Examplecall:
# Play a full note length C-major chord of the 4th octave in non-legato with mezzo forte loudness
# my_piano_chord chord(:C4, :major), 1.0, "non", "mf"
define :my_piano_play_chord do |my_chord, my_note_length, my_artic, my_dyn|
  my_sleep_length = my_note_length * my_piano_full_note_length * (current_bpm / 60)
  my_sample_dyn = 0
  my_amp_level = 1
  
  case my_artic
  when "non"
    my_play_length = my_note_length * my_piano_full_note_length * my_piano_nonleg_length
    my_release_length = 0.6
  when "stac"
    my_play_length = my_note_length * my_piano_full_note_length * my_piano_staccato_length
    my_release_length = 0.6 * my_piano_staccato_length
  when "leg"
    my_play_length = my_note_length * my_piano_full_note_length * my_piano_legato_length
    my_release_length = 0.6 * my_piano_legato_length
  end
  
  case my_dyn
  when "ppp"
    my_sample_dyn = 1
    my_amp_level = my_piano_dyn_ppp
  when "pp"
    my_sample_dyn = 1
    my_amp_level = my_piano_dyn_pp
  when "p"
    my_sample_dyn = 1
    my_amp_level = my_piano_dyn_p
  when "mp"
    my_sample_dyn = 1
    my_amp_level = my_piano_dyn_mp
  when "mf"
    my_sample_dyn = 2
    my_amp_level = my_piano_dyn_mf
  when "f"
    my_sample_dyn = 2
    my_amp_level = my_piano_dyn_f
  when "ff"
    my_sample_dyn = 2
    my_amp_level = my_piano_dyn_ff
  when "fff"
    my_sample_dyn = 3
    my_amp_level = my_piano_dyn_fff
  end
  
  if my_sample_dyn == 0
    assert false, "Dynamic unknown for my_piano_play_chord"
  end
  
  with_merged_sample_defaults sustain: my_play_length, release: my_release_length do
    with_fx :level, amp: my_amp_level do
      my_chord.each do |i|
        my_piano_sample i, my_sample_dyn
      end
    end
    sleep my_sleep_length
  end
end

# Hints for note and chord notation:
# Notenames 4th octave: :C4, :Cs4, :D4, :Ds4, :E4, :F4, :Fs4, :G4, :Gs4, :A4, :Bb4, :B4
# Chordnames can be printed out with the following command:
#puts chord_names
#
# You can also use [:C4, :E4, :G4] as chord notation (here it is C-major chord of 4th octave)

#
# Examplesong
#
# "Für Elise" from Ludwig van Beethoven, first 9 tacts

# Articulation to play
my_artic = "leg"
# Dynamic to play
my_dyn = "pp"

# Right hand
in_thread do
  my_piano_play :E5, 1.0/16, my_artic, my_dyn
  my_piano_play :Ds5, 1.0/16, my_artic, my_dyn
  
  my_piano_play :E5, 1.0/16, my_artic, my_dyn
  my_piano_play :Ds5, 1.0/16, my_artic, my_dyn
  my_piano_play :E5, 1.0/16, my_artic, my_dyn
  my_piano_play :B4, 1.0/16, my_artic, my_dyn
  my_piano_play :D5, 1.0/16, my_artic, my_dyn
  my_piano_play :C5, 1.0/16, my_artic, my_dyn
  
  my_piano_play :A4, 1.0/8, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
  my_piano_play :C4, 1.0/16, my_artic, my_dyn
  my_piano_play :E4, 1.0/16, my_artic, my_dyn
  my_piano_play :A4, 1.0/16, my_artic, my_dyn
  
  my_piano_play :B4, 1.0/8, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
  my_piano_play :E4, 1.0/16, my_artic, my_dyn
  my_piano_play :Gs4, 1.0/16, my_artic, my_dyn
  my_piano_play :B4, 1.0/16, my_artic, my_dyn
  
  my_piano_play :C5, 1.0/8, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
  my_piano_play :E4, 1.0/16, my_artic, my_dyn
  my_piano_play :E5, 1.0/16, my_artic, my_dyn
  my_piano_play :Ds5, 1.0/16, my_artic, my_dyn
  
  my_piano_play :E5, 1.0/16, my_artic, my_dyn
  my_piano_play :Ds5, 1.0/16, my_artic, my_dyn
  my_piano_play :E5, 1.0/16, my_artic, my_dyn
  my_piano_play :B4, 1.0/16, my_artic, my_dyn
  my_piano_play :D5, 1.0/16, my_artic, my_dyn
  my_piano_play :C5, 1.0/16, my_artic, my_dyn
  
  my_piano_play :A4, 1.0/8, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
  my_piano_play :C4, 1.0/16, my_artic, my_dyn
  my_piano_play :E4, 1.0/16, my_artic, my_dyn
  my_piano_play :A4, 1.0/16, my_artic, my_dyn
  
  my_piano_play :B4, 1.0/8, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
  my_piano_play :D4, 1.0/16, my_artic, my_dyn
  my_piano_play :C5, 1.0/16, my_artic, my_dyn
  my_piano_play :B4, 1.0/16, my_artic, my_dyn
  
  my_piano_play :A4, 1.0/8, my_artic, my_dyn
  my_piano_play "pause", 1.0/8, my_artic, my_dyn
end

# Left hand
in_thread do
  my_piano_play "pause", 1.0/8, my_artic, my_dyn
  
  my_piano_play "pause", 3.0/8, my_artic, my_dyn
  
  my_piano_play :A2, 1.0/16, my_artic, my_dyn
  my_piano_play :E3, 1.0/16, my_artic, my_dyn
  my_piano_play :A3, 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/8, my_artic, my_dyn
  
  my_piano_play :E2, 1.0/16, my_artic, my_dyn
  my_piano_play :E3, 1.0/16, my_artic, my_dyn
  my_piano_play :Gs3, 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/8, my_artic, my_dyn
  
  my_piano_play :A2, 1.0/16, my_artic, my_dyn
  my_piano_play :E3, 1.0/16, my_artic, my_dyn
  my_piano_play :A3, 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/8, my_artic, my_dyn
  
  my_piano_play "pause", 3.0/8, my_artic, my_dyn
  
  my_piano_play :A2, 1.0/16, my_artic, my_dyn
  my_piano_play :E3, 1.0/16, my_artic, my_dyn
  my_piano_play :A3, 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/8, my_artic, my_dyn
  
  my_piano_play :E2, 1.0/16, my_artic, my_dyn
  my_piano_play :E3, 1.0/16, my_artic, my_dyn
  my_piano_play :Gs3, 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/8, my_artic, my_dyn
  
  my_piano_play :A2, 1.0/16, my_artic, my_dyn
  my_piano_play :E3, 1.0/16, my_artic, my_dyn
  my_piano_play :A3, 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
end

Hello again,

I tuned the relative dynamics a bit and put in some humanization which can be enabled or disabled. This makes repeating sounds more natural.

So, here is the code:

# Framework for the VSCO piano
#
# Author: minced mind
# License: CC0 (see https://creativecommons.org/share-your-work/public-domain/cc0/)
# Version: 0.3

# Global variables
# Defines the directory of the piano samples from VSCO
my_piano_dir = "~/music-making/samples/VSCO-2-CE-SFZ/Keys/Upright Piano/"

# Length of a full note in seconds, often (current_bpm / 15)
# Make sure to set the bpm value first with "use_bpm <bpm-value>" otherwise it is 60 by default
my_piano_full_note_length = (current_bpm / 15)

# Relative length of a non-legato note length
my_piano_nonleg_length = 0.8

# Relative length of a staccato note length
my_piano_staccato_length = 0.5

# Relative length of a legato note length
my_piano_legato_length = 1.1

# Dynamics control:
#
# Hint for dynamics:
# To set a base loudness you can use for example the following code:
#use_sample_defaults amp: 1

# Relative loudness of the different dynamics (ppp, pp, p, mp, mf, f, ff, fff):
my_piano_dyn_ppp = 0.25
my_piano_dyn_pp = 0.5
my_piano_dyn_p = 0.75
my_piano_dyn_mp = 1
my_piano_dyn_mf = 0.5
my_piano_dyn_f = 0.75
my_piano_dyn_ff = 1
my_piano_dyn_fff = 1

# Humanization:
# Control if humanization shall be enabled (0 = no, 1 = yes)
my_piano_use_humanization = 1
# Control maximum relative random amplitudechanges
my_piano_humanization_amp = 0.1
# Control maximum relative random sustainchanges
my_piano_humanization_sustain = 0.1


# The function my_piano_sample is used from the other framework functions to select the right sample
# Parameters:
# my_note = note value which should be played
# my_dyn = the dynamic (loudness) which should be played (1, 2, 3)
define :my_piano_sample do |my_note, my_dyn|
  my_piano_list_dyn1 = ["Player_dyn1_rr1_000", "Player_dyn1_rr1_002", "Player_dyn1_rr1_004",
                        "Player_dyn1_rr1_006", "Player_dyn1_rr1_008", "Player_dyn1_rr1_010",
                        "Player_dyn1_rr1_012", "Player_dyn1_rr1_014", "Player_dyn1_rr1_016",
                        "Player_dyn1_rr1_018", "Player_dyn1_rr1_020", "Player_dyn1_rr1_022",
                        "Player_dyn1_rr1_024", "Player_dyn1_rr1_026", "Player_dyn1_rr1_028",
                        "Player_dyn1_rr1_030", "Player_dyn1_rr1_032", "Player_dyn1_rr1_034",
                        "Player_dyn1_rr1_036", "Player_dyn1_rr1_038", "Player_dyn1_rr1_040",
                        "Player_dyn1_rr1_042", "Player_dyn1_rr1_044"]
  
  my_piano_list_dyn2 = ["Player_dyn2_rr1_000", "Player_dyn2_rr1_002", "Player_dyn2_rr1_004",
                        "Player_dyn2_rr1_006", "Player_dyn2_rr1_008", "Player_dyn2_rr1_010",
                        "Player_dyn2_rr1_012", "Player_dyn2_rr1_014", "Player_dyn2_rr1_016",
                        "Player_dyn2_rr1_018", "Player_dyn2_rr1_020", "Player_dyn2_rr1_022",
                        "Player_dyn2_rr1_024", "Player_dyn2_rr1_026", "Player_dyn2_rr1_028",
                        "Player_dyn2_rr1_030", "Player_dyn2_rr1_032", "Player_dyn2_rr1_034",
                        "Player_dyn2_rr1_036", "Player_dyn2_rr1_038", "Player_dyn2_rr1_040",
                        "Player_dyn2_rr1_042", "Player_dyn2_rr1_044"]
  
  my_piano_list_dyn3 = ["Player_dyn3_rr1_000", "Player_dyn3_rr1_002", "Player_dyn3_rr1_004",
                        "Player_dyn3_rr1_006", "Player_dyn3_rr1_008", "Player_dyn3_rr1_010",
                        "Player_dyn3_rr1_012", "Player_dyn3_rr1_014", "Player_dyn3_rr1_016",
                        "Player_dyn3_rr1_018", "Player_dyn3_rr1_020", "Player_dyn3_rr1_022",
                        "Player_dyn3_rr1_024", "Player_dyn3_rr1_026", "Player_dyn3_rr1_028",
                        "Player_dyn3_rr1_030", "Player_dyn3_rr1_032", "Player_dyn3_rr1_034",
                        "Player_dyn3_rr1_036", "Player_dyn3_rr1_038", "Player_dyn3_rr1_040",
                        "Player_dyn3_rr1_042", "Player_dyn3_rr1_044"]
  
  my_midi_num = note(my_note)
  
  if my_midi_num < 21
    assert false, "Note too low for my_piano_sample"
  end
  
  if my_midi_num > 108
    assert false, "Note too high for my_piano_sample"
  end
  
  if my_dyn < 1
    assert false, "Dynamic too low for my_piano_sample"
  end
  
  if my_dyn > 3
    assert false, "Dynamic too high for my_piano_sample"
  end
  
  case my_dyn
  when 1
    my_piano_list = my_piano_list_dyn1
  when 2
    my_piano_list = my_piano_list_dyn2
  when 3
    my_piano_list = my_piano_list_dyn3
  end
  
  case my_midi_num
  when 21
    sample my_piano_dir, my_piano_list[0]
  when 22
    sample my_piano_dir, my_piano_list[0], rpitch: 1
  when 23..106
    my_listnum = (my_midi_num - 19) / 4
    my_pitch = ((my_midi_num - 23) % 4) - 2
    
    if my_pitch == 0
      sample my_piano_dir, my_piano_list[my_listnum]
    else
      sample my_piano_dir, my_piano_list[my_listnum], rpitch: my_pitch
    end
  when 107
    sample my_piano_dir, my_piano_list[22], rpitch: -1
  when 108
    sample my_piano_dir, my_piano_list[22]
  end
end

# Play a note with a given dynamic. With this you can control the whole effect hull curve.
# Parameters:
# my_note = note value to play
# my_dyn = dynamic to play (1 = quiet, 2 = louder, 3 = loud)
#
# Please note, the sfz-file recommends a release of 0.6
#
# Examplecall:
# Play note C at the 4th octave with 1 second sustain and loud dynamic
# with_sample_defaults amp: 1.0, sustain: 1.0, release: 0.6 do
#   my_piano_raw :C4, 3
# end
define :my_piano_raw do |my_note, my_dyn|
  my_piano_sample my_note, my_dyn
end

# Play a chord with a given dynamic. With this you can control the whole effect hull curve.
# Parameters:
# my_chord = chord to play
# my_dyn = dynamic to play (1 = quiet, 2 = louder, 3 = loud)
#
# Please note, the sfz-file recommends a release of 0.6
#
# Examplecall:
# Play a C-major chord of the 4th octave with 1 second sustain and loud dynamic
# with_sample_defaults amp: 1.0, sustain: 1.0, release: 0.6 do
#   my_piano_raw_chord chord(:C4, :major), 3
# end
define :my_piano_raw_chord do |my_chord, my_dyn|
  my_chord.each do |i|
    my_piano_sample i, my_dyn
  end
end

# Play a note with a given note length value
# Parameters:
# my_note = note value to play, use special note "pause" to set a pause
# my_note_length = note length to play
# my_artic = articulation to use ("non" = non-legato, "stac" = staccato, "leg" = legato)
# my_dyn = dynamic to play ("ppp", "pp", "p", "mp", "mf", "f", "ff", "fff")
#
# Examplecall:
# Play a full note C at the 4th octave in non-legato with mezzo forte loudness
# my_piano :C4, 1.0, "non", "mf"
define :my_piano_play do |my_note, my_note_length, my_artic, my_dyn|
  my_sleep_length = my_note_length * my_piano_full_note_length * (current_bpm / 60)
  my_sample_dyn = 0
  my_amp_level = 1
  
  case my_artic
  when "non"
    my_play_length = my_note_length * my_piano_full_note_length * my_piano_nonleg_length
    my_release_length = 0.6
  when "stac"
    my_play_length = my_note_length * my_piano_full_note_length * my_piano_staccato_length
    my_release_length = 0.6 * my_piano_staccato_length
  when "leg"
    my_play_length = my_note_length * my_piano_full_note_length * my_piano_legato_length
    my_release_length = 0.6 * my_piano_legato_length
  end
  
  case my_dyn
  when "ppp"
    my_sample_dyn = 1
    my_amp_level = my_piano_dyn_ppp
  when "pp"
    my_sample_dyn = 1
    my_amp_level = my_piano_dyn_pp
  when "p"
    my_sample_dyn = 1
    my_amp_level = my_piano_dyn_p
  when "mp"
    my_sample_dyn = 1
    my_amp_level = my_piano_dyn_mp
  when "mf"
    my_sample_dyn = 2
    my_amp_level = my_piano_dyn_mf
  when "f"
    my_sample_dyn = 2
    my_amp_level = my_piano_dyn_f
  when "ff"
    my_sample_dyn = 2
    my_amp_level = my_piano_dyn_ff
  when "fff"
    my_sample_dyn = 3
    my_amp_level = my_piano_dyn_fff
  end
  
  if my_sample_dyn == 0
    assert false, "Dynamic unknown for my_piano_play"
  end
  
  if my_note == "pause"
    sleep my_sleep_length
  else
    if my_piano_use_humanization == 1
      my_play_length = my_play_length * (1 + my_piano_humanization_sustain * rrand(-1, 1))
      my_amp_level = my_amp_level * (1 + my_piano_humanization_amp * rrand(-1, 1))
    end
    
    with_merged_sample_defaults sustain: my_play_length, release: my_release_length do
      with_fx :level, amp: my_amp_level do
        my_piano_sample my_note, my_sample_dyn
      end
    end
    sleep my_sleep_length
  end
end

# Play a chord with a given note length value
# Parameters:
# my_chord = chord to play
# my_note_length = note length to play
# my_artic = articulation to use ("non" = non-legato, "stac" = staccato, "leg" = legato)
# my_dyn = dynamic to play ("ppp", "pp", "p", "mp", "mf", "f", "ff", "fff")
#
# Examplecall:
# Play a full note length C-major chord of the 4th octave in non-legato with mezzo forte loudness
# my_piano_chord chord(:C4, :major), 1.0, "non", "mf"
define :my_piano_play_chord do |my_chord, my_note_length, my_artic, my_dyn|
  my_sleep_length = my_note_length * my_piano_full_note_length * (current_bpm / 60)
  my_sample_dyn = 0
  my_amp_level = 1
  
  case my_artic
  when "non"
    my_play_length = my_note_length * my_piano_full_note_length * my_piano_nonleg_length
    my_release_length = 0.6
  when "stac"
    my_play_length = my_note_length * my_piano_full_note_length * my_piano_staccato_length
    my_release_length = 0.6 * my_piano_staccato_length
  when "leg"
    my_play_length = my_note_length * my_piano_full_note_length * my_piano_legato_length
    my_release_length = 0.6 * my_piano_legato_length
  end
  
  case my_dyn
  when "ppp"
    my_sample_dyn = 1
    my_amp_level = my_piano_dyn_ppp
  when "pp"
    my_sample_dyn = 1
    my_amp_level = my_piano_dyn_pp
  when "p"
    my_sample_dyn = 1
    my_amp_level = my_piano_dyn_p
  when "mp"
    my_sample_dyn = 1
    my_amp_level = my_piano_dyn_mp
  when "mf"
    my_sample_dyn = 2
    my_amp_level = my_piano_dyn_mf
  when "f"
    my_sample_dyn = 2
    my_amp_level = my_piano_dyn_f
  when "ff"
    my_sample_dyn = 2
    my_amp_level = my_piano_dyn_ff
  when "fff"
    my_sample_dyn = 3
    my_amp_level = my_piano_dyn_fff
  end
  
  if my_sample_dyn == 0
    assert false, "Dynamic unknown for my_piano_play_chord"
  end
  
  if my_piano_use_humanization == 1
    my_play_length = my_play_length * (1 + my_piano_humanization_sustain * rrand(-1, 1))
    my_amp_level = my_amp_level * (1 + my_piano_humanization_amp * rrand(-1, 1))
  end
  
  with_merged_sample_defaults sustain: my_play_length, release: my_release_length do
    with_fx :level, amp: my_amp_level do
      my_chord.each do |i|
        my_piano_sample i, my_sample_dyn
      end
    end
  end
  sleep my_sleep_length
end

# Hints for note and chord notation:
# Notenames 4th octave: :C4, :Cs4, :D4, :Ds4, :E4, :F4, :Fs4, :G4, :Gs4, :A4, :Bb4, :B4
# Chordnames can be printed out with the following command:
#puts chord_names
#
# You can also use [:C4, :E4, :G4] as chord notation (here it is C-major chord of 4th octave)




#
# Examplesong
#
# "Für Elise" from Ludwig van Beethoven, first 9 tacts

# Set base loudness
use_sample_defaults amp: 3
# Articulation to play
my_artic = "leg"
# Dynamic to play
my_dyn = "pp"

# Right hand
in_thread do
  my_piano_play :E5, 1.0/16, my_artic, my_dyn
  my_piano_play :Ds5, 1.0/16, my_artic, my_dyn
  
  my_piano_play :E5, 1.0/16, my_artic, my_dyn
  my_piano_play :Ds5, 1.0/16, my_artic, my_dyn
  my_piano_play :E5, 1.0/16, my_artic, my_dyn
  my_piano_play :B4, 1.0/16, my_artic, my_dyn
  my_piano_play :D5, 1.0/16, my_artic, my_dyn
  my_piano_play :C5, 1.0/16, my_artic, my_dyn
  
  my_piano_play :A4, 1.0/8, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
  my_piano_play :C4, 1.0/16, my_artic, my_dyn
  my_piano_play :E4, 1.0/16, my_artic, my_dyn
  my_piano_play :A4, 1.0/16, my_artic, my_dyn
  
  my_piano_play :B4, 1.0/8, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
  my_piano_play :E4, 1.0/16, my_artic, my_dyn
  my_piano_play :Gs4, 1.0/16, my_artic, my_dyn
  my_piano_play :B4, 1.0/16, my_artic, my_dyn
  
  my_piano_play :C5, 1.0/8, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
  my_piano_play :E4, 1.0/16, my_artic, my_dyn
  my_piano_play :E5, 1.0/16, my_artic, my_dyn
  my_piano_play :Ds5, 1.0/16, my_artic, my_dyn
  
  my_piano_play :E5, 1.0/16, my_artic, my_dyn
  my_piano_play :Ds5, 1.0/16, my_artic, my_dyn
  my_piano_play :E5, 1.0/16, my_artic, my_dyn
  my_piano_play :B4, 1.0/16, my_artic, my_dyn
  my_piano_play :D5, 1.0/16, my_artic, my_dyn
  my_piano_play :C5, 1.0/16, my_artic, my_dyn
  
  my_piano_play :A4, 1.0/8, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
  my_piano_play :C4, 1.0/16, my_artic, my_dyn
  my_piano_play :E4, 1.0/16, my_artic, my_dyn
  my_piano_play :A4, 1.0/16, my_artic, my_dyn
  
  my_piano_play :B4, 1.0/8, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
  my_piano_play :D4, 1.0/16, my_artic, my_dyn
  my_piano_play :C5, 1.0/16, my_artic, my_dyn
  my_piano_play :B4, 1.0/16, my_artic, my_dyn
  
  my_piano_play :A4, 1.0/8, my_artic, my_dyn
  my_piano_play "pause", 1.0/8, my_artic, my_dyn
end

# Left hand
in_thread do
  my_piano_play "pause", 1.0/8, my_artic, my_dyn
  
  my_piano_play "pause", 3.0/8, my_artic, my_dyn
  
  my_piano_play :A2, 1.0/16, my_artic, my_dyn
  my_piano_play :E3, 1.0/16, my_artic, my_dyn
  my_piano_play :A3, 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/8, my_artic, my_dyn
  
  my_piano_play :E2, 1.0/16, my_artic, my_dyn
  my_piano_play :E3, 1.0/16, my_artic, my_dyn
  my_piano_play :Gs3, 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/8, my_artic, my_dyn
  
  my_piano_play :A2, 1.0/16, my_artic, my_dyn
  my_piano_play :E3, 1.0/16, my_artic, my_dyn
  my_piano_play :A3, 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/8, my_artic, my_dyn
  
  my_piano_play "pause", 3.0/8, my_artic, my_dyn
  
  my_piano_play :A2, 1.0/16, my_artic, my_dyn
  my_piano_play :E3, 1.0/16, my_artic, my_dyn
  my_piano_play :A3, 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/8, my_artic, my_dyn
  
  my_piano_play :E2, 1.0/16, my_artic, my_dyn
  my_piano_play :E3, 1.0/16, my_artic, my_dyn
  my_piano_play :Gs3, 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/8, my_artic, my_dyn
  
  my_piano_play :A2, 1.0/16, my_artic, my_dyn
  my_piano_play :E3, 1.0/16, my_artic, my_dyn
  my_piano_play :A3, 1.0/16, my_artic, my_dyn
  my_piano_play "pause", 1.0/16, my_artic, my_dyn
end