Clapping music: Difference between revisions

From XPUB & Lens-Based wiki
No edit summary
No edit summary
 
(18 intermediate revisions by the same user not shown)
Line 1: Line 1:
... continuing from the [[sedsongs]] exercise
[[Category:Prototyping]]


See the page on [[Bash]] for help with using loops & variables
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.


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 with some sort of shift.
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:
To start, create the first two bars "by hand" as a midge file:
Line 81: Line 81:
done
done
</source>
</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
</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