Clapping music: Difference between revisions

From XPUB & Lens-Based wiki
No edit summary
No edit summary
 
(24 intermediate revisions by the same user not shown)
Line 1: Line 1:
See [[Bash]] for help with loops & variables
[[Category:Prototyping]]


Template for midge, each channel is one of the parts.
This is an exercise to create a Bash version of [[wikipedia:Steve Reich|Steve Reich]]'s [[wikipedia:Clapping Music|Clapping Music]] using Bash and a pipeline using midge and timidity.


See the [[sedsongs]] exercise for some background, and the page on [[Bash]] for help with using loops & variables.
If you look at a copy of the musical notation for the piece, you can start to see that in fact it's based on a pattern, and could be easily (better?) expressed as an algorithm, namely a loop in which a pattern slowly shifts.
To start, create the first two bars "by hand" as a midge file:
<source lang="text">
<source lang="text">
@head {
@head {
Line 13: Line 18:
         $length 16
         $length 16
         $octave 4
         $octave 4
        $pan 0


    # the main pattern (3, 2, 1, 2 claps with rests in between)
     c c c r c c r c r c c r
     c c c r c c r c r c c r
    # the first performer simply repeats the pattern...
     c c c r c c r c r c c r
     c c c r c c r c r c c r


     }
     }
     @channel 2 {
     @channel 2 {
         $patch 2
         $patch 1
         $length 16
         $length 16
         $octave 7
         $octave 5
        $pan 127


    # the 2nd performer starts with the main pattern
     c c c r c c r c r c c r
     c c c r c c r c r c c r
    # .. and (eventually) shifts the pattern
     c c r c c r c r c c r c
     c c r c c r c r c c r c


     }
     }
}</source>
}
</source>


The trivial bashified version, note the need to escape (backslash) the $s.
Now, we convert the midge file to a minimal bash script that does nothing but outputting the text (using a [[heredoc]] and [[cat]]). Note the need to [[backslash]] the $'s.


<source lang="bash">
<source lang="bash">
Line 44: Line 54:
         \$length 16
         \$length 16
         \$octave 4
         \$octave 4
        \$pan 0


     c c c r c c r c r c c r
     c c c r c c r c r c c r
     c c c r c c r c r c c r
     c c c r c c r c r c c r


     }
     }
     @channel 2 {
     @channel 2 {
         \$patch 2
         \$patch 1
         \$length 16
         \$length 16
         \$octave 7
         \$octave 5
        \$pan 127


     c c c r c c r c r c c r
     c c c r c c r c r c c r
     c c r c c r c r c c r c
     c c r c c r c r c c r c


     }
     }
Line 64: Line 74:
</source>
</source>


We will make use of a simple counting loop (see [[Bash#Loops]]) ...
<source lang="bash">
<source lang="bash">
for ((i=0; i<13;i++))
for ((i=0; i<13;i++))
do
do
# ...
done
</source>
== Shifting the pattern ==
Consider what happens when the pattern shifts:
xxx_xx_x_xx_ ==>
xx_xx_x_xx_x
or as Bash:
pat="xxx_xx_x_xx_"
pat="xx_xx_x_xx_x"
Now consider what one does "by hand" to make this change:
[[File:Shift.png]]
Remember how Bash allows you to make ''substring'' selections using a [[Bash#Variables|special form]]:
${variable:offset:length}
In code you can express the shift by:
<source lang="bash">
pat=${pat:1}${pat:0:1}
</source>
or, more minimally (leaving out the 0 offset):
<source lang="bash">
pat=${pat:1}${pat::1}
</source>
== Nested loops ==
When a loop appears inside another loop, it is said to be a ''nested loop''. Note that the ''inner'' or nested loop is ''fastest'', in other words that it happens the most often (it loops itself, then loops again for each of the outer loops).
<source lang="bash">
for ((hours=1; hours<=12; hours++))
do
echo ding dong it\'s $hours o\' clock
for ((minutes=0; minutes<60; minutes++))
do
echo the time is $hours hours and $minutes minutes.
done
done
done
</source>
</source>
== Final code ==
A final script:
<source lang="bash">
pat="xxx_xx_x_xx_"
cat << end
@head {
    \$tempo 120
    \$time_sig 4/4
}
@body {
    @channel 1 {
        \$patch 1
        \$length 16
        \$octave 4
        \$pan 0
end
for ((bar=0; bar<13; bar++))
do
    for ((repeat=0; repeat<12; repeat++))
    do
        echo $pat | sed 's/x/c /g; s/_/r /g'
    done
done
cat << end
    }
    @channel 2 {
        \$patch 1
        \$length 16
        \$octave 5
        \$pan 127
end
       
for ((bar=0; bar<13; bar++))
do
    for ((repeat=0; repeat<12; repeat++))
    do
        echo $pat | sed 's/x/c /g; s/_/r /g'
    done
    pat=${pat:1}${pat:0:1}
done
cat << end
    }
}
end
</source>
To play the result:
<source lang="bash">
bash clappingmusic.sh | midge -o clappingmusic.mid
timidity clappingmusic.mid
</source>
[[File:Clappingmusic.ogg]]

Latest revision as of 10:34, 19 October 2010


This is an exercise to create a Bash version of Steve Reich's Clapping Music using Bash and a pipeline using midge and timidity.

See the sedsongs exercise for some background, and the page on Bash for help with using loops & variables.

If you look at a copy of the musical notation for the piece, you can start to see that in fact it's based on a pattern, and could be easily (better?) expressed as an algorithm, namely a loop in which a pattern slowly shifts.

To start, create the first two bars "by hand" as a midge file:

@head {
    $tempo 120
    $time_sig 4/4
}
@body {
    @channel 1 {
        $patch 1
        $length 16
        $octave 4
        $pan 0

    # the main pattern (3, 2, 1, 2 claps with rests in between)
    c c c r c c r c r c c r
    # the first performer simply repeats the pattern...
    c c c r c c r c r c c r

    }
    @channel 2 {
        $patch 1
        $length 16
        $octave 5
        $pan 127

    # the 2nd performer starts with the main pattern
    c c c r c c r c r c c r
    # .. and (eventually) shifts the pattern
    c c r c c r c r c c r c

    }
}

Now, we convert the midge file to a minimal bash script that does nothing but outputting the text (using a heredoc and cat). Note the need to backslash the $'s.

cat << END
@head {
    \$tempo 120
    \$time_sig 4/4
}
@body {
    @channel 1 {
        \$patch 1
        \$length 16
        \$octave 4
        \$pan 0

    c c c r c c r c r c c r
    c c c r c c r c r c c r

    }
    @channel 2 {
        \$patch 1
        \$length 16
        \$octave 5
        \$pan 127

    c c c r c c r c r c c r
    c c r c c r c r c c r c

    }
}
END

We will make use of a simple counting loop (see Bash#Loops) ...

for ((i=0; i<13;i++))
do
# ...
done

Shifting the pattern

Consider what happens when the pattern shifts:

xxx_xx_x_xx_ ==> 
xx_xx_x_xx_x

or as Bash:

pat="xxx_xx_x_xx_"
pat="xx_xx_x_xx_x"

Now consider what one does "by hand" to make this change:

Shift.png

Remember how Bash allows you to make substring selections using a special form:

${variable:offset:length}

In code you can express the shift by:

pat=${pat:1}${pat:0:1}

or, more minimally (leaving out the 0 offset):

pat=${pat:1}${pat::1}

Nested loops

When a loop appears inside another loop, it is said to be a nested loop. Note that the inner or nested loop is fastest, in other words that it happens the most often (it loops itself, then loops again for each of the outer loops).

for ((hours=1; hours<=12; hours++))
do

echo ding dong it\'s $hours o\' clock

for ((minutes=0; minutes<60; minutes++))
do
echo the time is $hours hours and $minutes minutes.
done

done

Final code

A final script:

pat="xxx_xx_x_xx_"

cat << end
@head {
    \$tempo 120
    \$time_sig 4/4
}
@body {
    @channel 1 {
        \$patch 1
        \$length 16
        \$octave 4
        \$pan 0
end

for ((bar=0; bar<13; bar++))
do
    for ((repeat=0; repeat<12; repeat++))
    do
        echo $pat | sed 's/x/c /g; s/_/r /g'
    done
done

cat << end
    }
    @channel 2 {
        \$patch 1
        \$length 16
        \$octave 5
        \$pan 127
end
         
for ((bar=0; bar<13; bar++))
do
    for ((repeat=0; repeat<12; repeat++))
    do
        echo $pat | sed 's/x/c /g; s/_/r /g'
    done
    pat=${pat:1}${pat:0:1}
done

cat << end 
    }
}
end

To play the result:

bash clappingmusic.sh | midge -o clappingmusic.mid
timidity clappingmusic.mid

File:Clappingmusic.ogg