Little help with music script

Discussion in 'CommandHelper' started by Oboist, Feb 16, 2014.

  1. Oboist

    Oboist Member

    Hello again, CH masters! =))
    So i have this little script


    Code (Text):
    /music $ = >>>
     
    @music = array()
     
    @music = parse_args(replace($, '.', ' '))
     
    @ins = 'PIANO'
     
    @oct = 0
     
    for(assign(@x, 0), @x < array_size(@music), @x++,
     
    switch(@music[@x],
     
    'G', play_note(@ins, array(octave: @oct, tone: 'G')),
     
    ...another lines for each note, changing instrument and octave...
     
    continue()))
     
    <<<

    So, if you type "/music O1.IP.A.H.C.D.E.E.E" it should play a simple melody
    One thing that i cannot understand is, how can i implement pauses? Should be all "switch" part in set_interval() or it possible to make set_timeout(50, closure(continue())) inside "switch"? I never used scheluding before, so, please, give me a little help =)))
  2. Tom028

    Tom028 Member

    Queue_push and queue_delay are what you're looking for. They allow you to set up a separate queue of tasks and then set a delay before the task is executed.

    A queue has to be specified in order to delay it otherwise the tasks are put onto the main thread. I've named the queue in the example "music_queue".

    PHP:

    /music $ = >>>
    @music = parse_args(replace($, '.', ' '))
    @ins = 'PIANO'
    @oct = 0
    for(@x = 0, @x < array_size(@music), @x++){
        queue_push(
            closure(
                play_note(@ins, array(octave: @oct, tone: @music[@x]))
            ),
            'music_queue'
        )
        queue_delay(50, 'music_queue')#This is your delay between notes in milliseconds.
    )
    <<<
     
  3. Oboist

    Oboist Member

    Thank you so much! I think i got it but... i haven't succeeded =)

    PHP:
    proc(_mus, @text, @target,
    @music = parse_args(replace(@text, '.', ' '))
    @ins = 'NOTE_PIANO'
    @vol = 1
    for(assign(@x, 0), @x < array_size(@music), @x++,
    queue_push(closure(switch(@music[@x],
    'C', play_sound(@target, array(sound: @ins, volume: @vol, pitch: 1)),
    'C#', play_sound(@target, array(sound: @ins, volume: @vol, pitch: 1.05)),
    'D', play_sound(@target, array(sound: @ins, volume: @vol, pitch: 1.1)),
    'P', queue_delay(1000, musika),
    )), musika)))
    queue_delay is not working... Where i made a mistake? If _mus receives 'C.P.C#' it just plays 'C.C#'...
  4. PseudoKnight

    PseudoKnight Well-Known Member Developer

    Your queue_delay is inside the queue_push. That's going to put the delay at the end of the queue. I think what you want is to add a delay after every single beat, and not even queue a note if it's a 'P'.

    While demonstrating what I mean, I updated your code to use some newer and more readable syntax. There are many ways to write methodscript, but this is how I'm doing it right now.
    PHP:
    proc(_mus, @text, @target) {
        @music = parse_args(replace(@text, '.', ' '))
        @ins = 'NOTE_PIANO'
        @vol = 1
        for(@i = 0, @i < array_size(@music), @i++) {
            if(@music[@i] != 'P') {
                queue_push(closure(
                    switch(@music[@i]) {
                        case 'C':
                            play_sound(@target, array(sound: @ins, volume: @vol, pitch: 1))
                           
                        case 'C#':
                            play_sound(@target, array(sound: @ins, volume: @vol, pitch: 1.05))
                           
                        case 'D':
                            play_sound(@target, array(sound: @ins, volume: @vol, pitch: 1.1))
                    }
                ), musika)
            }
            queue_delay(500, musika)
        }
    }
    I wish the queueID wasn't after the closure.
  5. Oboist

    Oboist Member

    Wow, thank you! Now i think i understand =)
    And thanks for showing "switch" syntax, it's much clearer indeed.
    You're my hero XD
  6. PseudoKnight

    PseudoKnight Well-Known Member Developer

    Ah, shucks. :p
    LadyCailin likes this.
  7. Oboist

    Oboist Member

    Funny, that was so long ago...
    So, i had music script for a long time and now players can write melodies inside books...
    "Score" can be like this:
    Code (Text):
    t110l16.o2.e.d#.e.d#.e.<h.>d.c.<ai5<a.>e.a.i1c.e.a.hi5e.e.g#.i1e.g#.h.>ci5<<a.>e.a.i1e.>e.d#.e.d#.e.<h.>d.c.<ai5<a.+e.a.i1c.e.a.hi5e.e.g#.i1e.>c.<h.aci5<aa
    (what sounds then you left-click with a book in hand)

    But what i'm trying to achieve is a good tool for writing complex songs. And then you have long song with multiple instruments, it would be a LOT easier not to write it in one line and constantly switch instruments/octaves/note lengths/volume, but split it in separate tracks for each instrument.
    Code (Text):
    t100l16o2e.d#.e.d#.e.<h.>d.c.<ai5<a.>e.a.i1c.e.a.h...e.g#.h.>c...<e.>e.d#.e.d#.e.<h.>d.c.<a...c.e.a.h...e.>c.<h.ac
    Code (Text):
    t100l16i5........<a.>e.a....e.e.g#....<<a.>e.a..........<a.>e.a....e.e.g#....<aa
    Easier to write, read, add more (if tracks are on different pages).

    I thought that will be easy - i just need to make several queues. And second example sounded just like the first one. But then i tried to get rid of this '......' just by changing note lengths for those pauses or just tried to write songs with note lengths that are not the same on each track - all goes wild, synchronization disappears. And this ONLY happens if you change length or tempo.

    Finally I remembered that, alas, minimum queue_delay "resolution" that we can get is 50 ms. Then I tried to use tempo that gives l4 = 500ms and l8 = 250ms. And still got weird results (tracks start together and slowly begin to get out of sync). Using set_timeout() for each note with growing delay gave me better results, but still... "unstable" =)

    So, i suppose, there's no guaranties that queue or timeout will execute at desired time, but, maybe, you have some theoretical thoughts on how to make this work the best way?
  8. PseudoKnight

    PseudoKnight Well-Known Member Developer

    Use set_interval() and process both in the same interval.
  9. Oboist

    Oboist Member

    Works excellent =) Thank you!