Added word finder and sound programming articles.
This commit is contained in:
parent
6c244ddd70
commit
286588366a
|
@ -0,0 +1,71 @@
|
||||||
|
#+title: Hardware freedom
|
||||||
|
#&summary
|
||||||
|
A discussion about hardware freedom. Contains comparisons with software
|
||||||
|
freedom.
|
||||||
|
#&
|
||||||
|
#+license: wtfpl
|
||||||
|
|
||||||
|
|
||||||
|
https://www.fsf.org/resources/hw/endorsement/criteria
|
||||||
|
https://www.fsf.org/campaigns/free-bios.html
|
||||||
|
https://www.gnu.org/philosophy/android-and-users-freedom.html
|
||||||
|
http://projects.goldelico.com/p/gta04-main/page/FirmwareInjector/?rev=322
|
||||||
|
http://www.oreillynet.com/linux/blog/2007/08/the_four_freedoms_applied_to_h.html
|
||||||
|
http://www.boingboing.net/2010/09/19/intel-drm-a-crippled.html
|
||||||
|
http://lists.en.qi-hardware.com/pipermail/discussion/2010-January/001635.html
|
||||||
|
http://www.ofb.biz/safari/article/353.html
|
||||||
|
http://arstechnica.com/business/news/2006/12/8428.ars
|
||||||
|
http://distrowatch.com/weekly.php?issue=20100614#feature
|
||||||
|
http://libreplanet.org/wiki/When_should_firmware_be_free
|
||||||
|
http://www.datamation.com/osrc/article.php/3787736/Proprietary-Firmware-and-the-Pursuit-of-a-Free-Kernel.htm
|
||||||
|
https://lwn.net/Articles/352555/
|
||||||
|
https://lwn.net/Articles/460654/
|
||||||
|
http://lists.goldelico.com/pipermail/gta04-owner/2011-October/000375.html
|
||||||
|
http://lists.goldelico.com/pipermail/gta04-owner/2011-September/000325.html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
centralisering
|
||||||
|
|
||||||
|
I don't want to depend on something which I cannot control.
|
||||||
|
|
||||||
|
I don't want to depend on something which I cannot influence.
|
||||||
|
|
||||||
|
|
||||||
|
depend on:
|
||||||
|
+ data durability
|
||||||
|
+ privacy
|
||||||
|
|
||||||
|
I like to follow this rule: When I create something substantial which I want to
|
||||||
|
share, I host it myself. But why? Because I don't want to depend on something
|
||||||
|
which I cannot control.
|
||||||
|
|
||||||
|
Downside loss of social
|
||||||
|
|
||||||
|
Requires widespread use of a decentralized social network to share private data
|
||||||
|
with a select few.
|
||||||
|
|
||||||
|
The difficulty of leaving a service is determined by the complexity of the
|
||||||
|
social features of the service.
|
||||||
|
|
||||||
|
I only host my own works somewhere else than on my own host if I feel that I
|
||||||
|
have a social obligation to do so, or if it's a link that points out of the
|
||||||
|
service. I don't mind if what Facebook ends up being is the new digg.
|
||||||
|
|
||||||
|
The more difficult it is to leave a service, the more I feel I have a *social*
|
||||||
|
obligation to host my works on the service. However, the more difficult it is to
|
||||||
|
leave a service, the more I also feel that I have a *moral* obligation to /not/
|
||||||
|
host my works on the service.
|
||||||
|
|
||||||
|
I don't wish to host substantial works, even public ones, on e.g. Facebook. Not
|
||||||
|
because Facebook gets to know them (I have made sure I don't mind them being
|
||||||
|
public), but because I use a service which I don't like, and that may fuel the
|
||||||
|
use in general, especially for my friends. As such, it doesn't matter what I
|
||||||
|
publish on the service, it will no matter what (in varying degrees) accelerate
|
||||||
|
the use of the service, which I do not want to happen.
|
||||||
|
|
||||||
|
In general, it's a balance. I try not to make others depend on Facebook because
|
||||||
|
of me; I do that by not uploading large photo galleries to Facebook. However,
|
||||||
|
photo galleries on Facebook have quite complex features.
|
||||||
|
|
||||||
|
Collaborative sites
|
|
@ -0,0 +1,377 @@
|
||||||
|
#+title: Old junk code: Word finder
|
||||||
|
#+summary: Less than perfect C code
|
||||||
|
#+license: wtfpl, unless otherwise noted
|
||||||
|
#+startup: showall
|
||||||
|
#&toc
|
||||||
|
|
||||||
|
|
||||||
|
* Old junk code: Word finder
|
||||||
|
|
||||||
|
#+caption: Based on [[https://commons.wikimedia.org/wiki/File:2001-91-1_Computer,_Laptop,_Pentagon_(5891422370).jpg][this]], CC BY 2.0
|
||||||
|
#&img;url=sadcomputer.png, float=right
|
||||||
|
|
||||||
|
If you ever get tired of looking at your own junk code, take a look at this.
|
||||||
|
|
||||||
|
In August 2008, when I was still learning to program in C, I created a program
|
||||||
|
"ordfinder" (eng: word finder) which, given a word and a dictionary, prints the
|
||||||
|
words from the dictionary which can be created from the letters from the given
|
||||||
|
word in any order. Incredibly, it ended up compiling and works perfectly for any
|
||||||
|
word whose length does not exceed 8 characters, although it is a bit slow.
|
||||||
|
|
||||||
|
But why not more than 8 characters? My view of memory might have been a bit
|
||||||
|
naive back then, because the first step in my algorithm is to generate and
|
||||||
|
store all permutations of all subsequences of the given word. That is, if the
|
||||||
|
string is "me", my program stores the array `{ "m", "e", "me", "em" }` in
|
||||||
|
memory before going on to reading the dictionary and looking for matches.
|
||||||
|
|
||||||
|
If the string is "you", the program stores `{ "y", "o", "yo", "oy", "u", "yu",
|
||||||
|
"uy", "ou", "uo", "you", "yuo", "oyu", "ouy", "uyo", "uoy" }`.
|
||||||
|
|
||||||
|
If the string is "computer", the program stores the 109600 permutations of the
|
||||||
|
subsequences of "computer".
|
||||||
|
|
||||||
|
If the string is "difficult", the length of 9 characters means that the program
|
||||||
|
attempts to store 986409 strings of lengths 1 to 9. That probably takes up not
|
||||||
|
more than 10 MB, so it shouldn't be a problem. However, my program seems to
|
||||||
|
store the list of words on the stack instead of in memory, so words with length
|
||||||
|
9 or above cause a stack overflow to happen.
|
||||||
|
|
||||||
|
In any case, a word length of 10 would require about 100 MB, a word length of 11
|
||||||
|
about 1.2 GB, a word length of 12 about 15.6 GB, and a word length of 17 (like
|
||||||
|
"inconspicuousness") about 16,5 Petabytes (16500000 GB). That's 6,5 Petabytes
|
||||||
|
*more* than [[http://archive.org/web/petabox.php][what the Internet Archive uses]] to store millions of websites, books,
|
||||||
|
video and audio.
|
||||||
|
|
||||||
|
So perhaps neither my algorithm nor my implementation was that good.
|
||||||
|
|
||||||
|
|
||||||
|
* The code
|
||||||
|
|
||||||
|
Note that this code doesn't actually compile, because of all the wrong
|
||||||
|
code. However, it did compile back in 2008 which means that either I added the
|
||||||
|
wrong code after I had compiled it, or I used an overfriendly compiler (I don't
|
||||||
|
remember which compiler it was, but it ran on Windows). I have run the old
|
||||||
|
executable with `wine`, and that works.
|
||||||
|
|
||||||
|
It's not necesarry to know C to laugh at this code, but it helps.
|
||||||
|
|
||||||
|
We'll start with some basic `#include`s.
|
||||||
|
|
||||||
|
#+BEGIN_SRC c
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <math.h>
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
So far, so good. Then the global variables with descriptive names. And let's
|
||||||
|
declare four strings of length 0 to be statically allocated, because we'll just
|
||||||
|
extend them later on...?
|
||||||
|
|
||||||
|
#+BEGIN_SRC c
|
||||||
|
char os[0],s[0],r[0],t[0];
|
||||||
|
int l,c,rc,k,sk,i,ii,iii,ri;
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
The next step is to define our own version of C's builtin `strstr` function
|
||||||
|
(almost). I was used to PHP, so I wanted the same return values as PHP's
|
||||||
|
`strpos`.
|
||||||
|
|
||||||
|
#+BEGIN_SRC c
|
||||||
|
int strpos (const char *haystack, const char *needle) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (strlen (haystack) < strlen (needle))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for (i = 0; i <= (strlen (haystack) - strlen(needle)); i++) {
|
||||||
|
if (!strncmp (&haystack[i], needle, strlen(needle)))
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
Then it's time for the main function. We don't want to separate it into
|
||||||
|
auxiliary functions, because that's just ugly!
|
||||||
|
|
||||||
|
Indentation? Too much wastes too much space.
|
||||||
|
|
||||||
|
#+BEGIN_SRC c
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
if (argc>1) {
|
||||||
|
strcpy(os,argv[1]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf("Indtast ord: ");
|
||||||
|
gets(os);
|
||||||
|
}
|
||||||
|
printf("T\x91nker...\n");
|
||||||
|
strcpy(s,os);
|
||||||
|
for(i=0;s[i];i++) {
|
||||||
|
s[i]=tolower(s[i]);
|
||||||
|
}
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
Wait, what? We use `strcpy` to copy the string `argv[1]`, which contains the
|
||||||
|
word we want to permute, into the statically allocated `os` with length 0? Or we
|
||||||
|
read a line from standard in and save in `os`? And almost the same for `s`?
|
||||||
|
That's... not good.
|
||||||
|
|
||||||
|
At least these two lines aren't that bad.
|
||||||
|
|
||||||
|
#+BEGIN_SRC c
|
||||||
|
l=strlen(s);
|
||||||
|
c=pow(l,l);
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
But then begins the actual permutation generation logic. I have tried to
|
||||||
|
re-understand it, with no success.
|
||||||
|
|
||||||
|
#+BEGIN_SRC c
|
||||||
|
rc=1;
|
||||||
|
i=0;
|
||||||
|
while (i<l-1) {
|
||||||
|
rc=rc*(l-i);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
While we're at it, why not declare two to-be-statically-allocated arrays with
|
||||||
|
dynamically-generated ints as lengths?
|
||||||
|
|
||||||
|
#+BEGIN_SRC c
|
||||||
|
int ca[l];
|
||||||
|
char ra[rc][l+1];
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
And then some more assignments and `while` loops...
|
||||||
|
|
||||||
|
#+BEGIN_SRC c
|
||||||
|
ri=0;
|
||||||
|
i=0;
|
||||||
|
while (i<c) {
|
||||||
|
k=1;
|
||||||
|
ii=0;
|
||||||
|
while (ii<l && k==1) {
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
This formula does something. I'm not sure what.
|
||||||
|
|
||||||
|
#+BEGIN_SRC c
|
||||||
|
ca[ii]=floor(i/pow(l,l-ii-1))-floor(i/pow(l,l-ii))*l;
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
More `while` loops, now also with `if` statements.
|
||||||
|
|
||||||
|
#+BEGIN_SRC c
|
||||||
|
iii=0;
|
||||||
|
while (iii<ii) {
|
||||||
|
if (ca[ii]==ca[iii]) {k=0;}
|
||||||
|
iii++;
|
||||||
|
}
|
||||||
|
ii++;
|
||||||
|
}
|
||||||
|
if (k==1) {
|
||||||
|
strcpy(ra[ri],"");
|
||||||
|
ii=0;
|
||||||
|
while (ii<l) {
|
||||||
|
strncpy(t,s+ca[ii],1);
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
Let's concatenate `t` onto `ra[ri]`, a string which hardly exists due to the
|
||||||
|
`char ra[rc][l+1];` magic above.
|
||||||
|
|
||||||
|
#+BEGIN_SRC c
|
||||||
|
strcat(ra[ri],t);
|
||||||
|
ii++;
|
||||||
|
}
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
And why not concatenate an end-of-string mark onto a string which, if it
|
||||||
|
doesn't have an end-of-string mark, will make `strcat` fail miserably?
|
||||||
|
|
||||||
|
#+BEGIN_SRC c
|
||||||
|
strcat(ra[ri],"\0");
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
And then more junk.
|
||||||
|
|
||||||
|
#+BEGIN_SRC c
|
||||||
|
sk=1;
|
||||||
|
ii=0;
|
||||||
|
while (ii<ri && sk==1) {
|
||||||
|
if (strcmp(ra[ri],ra[ii])==0) {sk=0;}
|
||||||
|
ii++;
|
||||||
|
}
|
||||||
|
if (sk==1) {
|
||||||
|
//printf("%s\n",ra[ri]);
|
||||||
|
ri++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
//printf("\nOrd: %s\nOrdl\x91ngde: %d\nOrdkombinationer: %d\n",os,l,ri);
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
Phew... At this point, I'm certain that `ra` is supposed to be an array of all
|
||||||
|
word permutations. So let's open our dictionary "ord.txt" and look for matches.
|
||||||
|
|
||||||
|
#+BEGIN_SRC c
|
||||||
|
FILE *f;
|
||||||
|
char wrd[128];
|
||||||
|
if (f=fopen("ord.txt","r")) {
|
||||||
|
FILE *fw;
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
Everything is written both to output.txt *and* standard out. Anything else would
|
||||||
|
be stupid.
|
||||||
|
|
||||||
|
#+BEGIN_SRC c
|
||||||
|
fw=fopen("output.txt","w");
|
||||||
|
printf("Ord dannet af \"%s\":\n\n",os);
|
||||||
|
fprintf(fw,"Ord dannet af \"%s\":\n\n",os);
|
||||||
|
int wc=0;
|
||||||
|
while(!feof(f)) {
|
||||||
|
if(fgets(wrd,126,f)) {
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
The words each end with a newline, so let's replace the newline with an
|
||||||
|
end-of-string mark.
|
||||||
|
|
||||||
|
#+BEGIN_SRC c
|
||||||
|
wrd[strlen(wrd)-1]=0;
|
||||||
|
//printf("%s\n",wrd);
|
||||||
|
k=0;
|
||||||
|
ii=0;
|
||||||
|
while (ii<ri && k==0) {
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
The magical core of the matching logic, using our own `strpos`:
|
||||||
|
|
||||||
|
#+BEGIN_SRC c
|
||||||
|
if (strpos(ra[ii],wrd)>-1) {k=1;}
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
If ~k == 1~, something good happens. But it doesn't happen at once for some
|
||||||
|
reason.
|
||||||
|
|
||||||
|
#+BEGIN_SRC c
|
||||||
|
ii++;
|
||||||
|
}
|
||||||
|
if (k==1) {
|
||||||
|
printf("%s\n",wrd);
|
||||||
|
fprintf(fw,"%s\n",wrd);
|
||||||
|
wc++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("\nI alt %d ord\n",wc);
|
||||||
|
fprintf(fw,"\nI alt %d ord",wc);
|
||||||
|
fclose(fw);
|
||||||
|
fclose(f);
|
||||||
|
system("output.txt");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
And that's my pretty C code.
|
||||||
|
|
||||||
|
|
||||||
|
* The SML equivalent
|
||||||
|
|
||||||
|
To make my inefficient algorithm a bit clearer, I have made a few SML functions
|
||||||
|
to do the same as above:
|
||||||
|
|
||||||
|
#+BEGIN_SRC ocaml
|
||||||
|
open List
|
||||||
|
|
||||||
|
(* Removes an element from a list. *)
|
||||||
|
fun remove x (y :: ys) = if x = y
|
||||||
|
then ys
|
||||||
|
else y :: remove x ys
|
||||||
|
|
||||||
|
(* Tails of a list. Stolen from Haskell's Data.List. *)
|
||||||
|
fun tails [] = [[]]
|
||||||
|
| tails (xxs as (_ :: xs)) = xxs :: tails xs
|
||||||
|
|
||||||
|
(* Non-empty subsequences of a list. Stolen from Haskell's Data.List. *)
|
||||||
|
fun nonEmptySubsequences [] = []
|
||||||
|
| nonEmptySubsequences (x :: xs) =
|
||||||
|
let
|
||||||
|
fun f (ys, r) = ys :: (x :: ys) :: r
|
||||||
|
in
|
||||||
|
[x] :: foldr f [] (nonEmptySubsequences xs)
|
||||||
|
end
|
||||||
|
|
||||||
|
(* All permutations of a list. *)
|
||||||
|
fun permutations [] = [[]]
|
||||||
|
| permutations xs =
|
||||||
|
let
|
||||||
|
fun subPermutations x = map (fn ys => x :: ys) (permutations (remove x xs))
|
||||||
|
in
|
||||||
|
concat (map subPermutations xs)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
(* Permutations of subsequences of a list. *)
|
||||||
|
fun subsequencePermutations xs = concat (map permutations (nonEmptySubsequences xs))
|
||||||
|
|
||||||
|
(* The same, but for a string. *)
|
||||||
|
fun stringSubsequencePermutations s = map implode (subsequencePermutations (explode s))
|
||||||
|
|
||||||
|
(* Finds words in `wordList` which matches any permutation of any subsequence
|
||||||
|
* of `word`. *)
|
||||||
|
fun findMatchingWords word wordList =
|
||||||
|
let
|
||||||
|
val wordPermutations = stringSubsequencePermutations word
|
||||||
|
in
|
||||||
|
filter (fn testWord =>
|
||||||
|
exists (fn word => word = testWord)
|
||||||
|
wordPermutations) wordList
|
||||||
|
end
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
As well as some SML functions to calculate the number of permutations and bytes:
|
||||||
|
|
||||||
|
#+BEGIN_SRC ocaml
|
||||||
|
(* Calculates the factorial. *)
|
||||||
|
fun factorial 0 = 1
|
||||||
|
| factorial n = n * factorial (n - 1)
|
||||||
|
|
||||||
|
(* Calculates the binomial coeffecient. *)
|
||||||
|
fun binomc n k = factorial n div (factorial k * factorial (n - k))
|
||||||
|
|
||||||
|
(* Gives [m, m + 1, ..., n]. *)
|
||||||
|
fun upTo m n = if m < n
|
||||||
|
then m :: upTo (m + 1) n
|
||||||
|
else [m]
|
||||||
|
|
||||||
|
(* Gives the total number of word subsequence permutations for a given word
|
||||||
|
* length. *)
|
||||||
|
fun nPermutations len = foldl op+ 0 (map (fn n => factorial n * binomc len n)
|
||||||
|
(upTo 1 len))
|
||||||
|
|
||||||
|
(* Gives the size in bytes for storing all word subsequence permutations for a
|
||||||
|
* given word length in a space-saving way: there are `len` arrays, each taking
|
||||||
|
* up space for the pointer to the array and the permutations of subsequences of
|
||||||
|
* length n where `1 <= n <= len` and n is unique.
|
||||||
|
*)
|
||||||
|
fun nSize len = 8 * len + foldl op+ 0 (
|
||||||
|
map (fn n => (n + 1) * factorial n * binomc len n)
|
||||||
|
(upTo 1 len))
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
* The alternative
|
||||||
|
|
||||||
|
Preprocess the dictionary into a clever data structure and don't use up all the
|
||||||
|
memory.
|
||||||
|
|
||||||
|
|
||||||
|
#&line
|
||||||
|
|
||||||
|
Originally published [[http://dikutal.dk/artikler/old-junk-code-word-finder][here]].
|
Binary file not shown.
After Width: | Height: | Size: 33 KiB |
|
@ -0,0 +1,291 @@
|
||||||
|
#+title: Sound Programming
|
||||||
|
#+summary: Programming sound in Live-Sequencer and ChucK
|
||||||
|
#+license: wtfpl, unless otherwise noted
|
||||||
|
#+startup: showall
|
||||||
|
#&toc
|
||||||
|
|
||||||
|
Much can be programmed, and that includes sound. In the digital world, sound is
|
||||||
|
typically represented by sequences of about 90 kB per second, so "printing"
|
||||||
|
sound is merely a matter of printing bytes. As such, any general purpose
|
||||||
|
language can be used to generate sounds.
|
||||||
|
|
||||||
|
However, it's boring to create a program that does nothing but print bytes, and
|
||||||
|
it's potentially difficult to make those bytes sound nice; we want abstractions
|
||||||
|
to simplify matters for us: instruments, drums, musical notes, and a high-level
|
||||||
|
program structure. Many programming languages have good libraries that allow us
|
||||||
|
to achieve just that, but to keep it simple we'll focus on how to program sound
|
||||||
|
in two languages designed to output sound: ChucK and Live-Sequencer.
|
||||||
|
|
||||||
|
Let's create some sounds.
|
||||||
|
|
||||||
|
|
||||||
|
* The square wave
|
||||||
|
|
||||||
|
We'll start with ChucK and a small square wave program:
|
||||||
|
|
||||||
|
#+BEGIN_SRC c
|
||||||
|
// Connect a square oscillator to the sound card.
|
||||||
|
SqrOsc s => dac;
|
||||||
|
|
||||||
|
// Set its frequency to 440 Hz.
|
||||||
|
440 => s.freq;
|
||||||
|
|
||||||
|
// Lower the volume.
|
||||||
|
0.1 => s.gain;
|
||||||
|
|
||||||
|
// Let it run indefinitely.
|
||||||
|
while (true) {
|
||||||
|
1000::second => now;
|
||||||
|
}
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
ChucK is an imperative language. Instructions on how to install and run it can
|
||||||
|
be found on its [[http://chuck.cs.princeton.edu/][website]], along with other useful information. You can listen to
|
||||||
|
the above sound [[square.flac][here]].
|
||||||
|
|
||||||
|
To do the same in Live-Sequencer, we must find a square wave "instrument" and use
|
||||||
|
that.
|
||||||
|
|
||||||
|
#+BEGIN_SRC haskell
|
||||||
|
module SquareWave where
|
||||||
|
|
||||||
|
-- Basic imports.
|
||||||
|
import Prelude
|
||||||
|
import List
|
||||||
|
import Midi
|
||||||
|
import Instrument
|
||||||
|
import Pitch
|
||||||
|
|
||||||
|
-- The function "main" is run when the program is run.
|
||||||
|
-- It returns a list of MIDI actions.
|
||||||
|
main = concat [ program lead1Square -- Use a square wave instrument.
|
||||||
|
, cycle ( -- Keep playing the following forever.
|
||||||
|
note 1000000 (a 4) -- Play 1000000 milliseconds of the musical note A4
|
||||||
|
) -- about 440 Hz.
|
||||||
|
]; -- End with a semicolon.
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
Live-Sequencer differs from ChucK in that it is functional, but another major
|
||||||
|
difference is that while ChucK (in general) generates raw sound bytes,
|
||||||
|
Live-Sequencer generates so-called MIDI codes, which another program converts to
|
||||||
|
the actual audio. Live-Sequencer has a couple of funky features such as
|
||||||
|
highlighting which part of one's program is played; read about it and how to
|
||||||
|
install and run it at [[http://www.haskell.org/haskellwiki/Live-Sequencer][this wiki]]. You can listen to the above sound [[squarewave.flac][here]].
|
||||||
|
|
||||||
|
|
||||||
|
* Something more advanced
|
||||||
|
|
||||||
|
Let's try to create a small piece of music which can be expressed easily in
|
||||||
|
Live-Sequencer (listen [[melodyexample.flac][here]]):
|
||||||
|
|
||||||
|
#+BEGIN_SRC haskell
|
||||||
|
module MelodyExample where
|
||||||
|
|
||||||
|
import Prelude
|
||||||
|
import List
|
||||||
|
import Midi
|
||||||
|
import Instrument
|
||||||
|
import Pitch
|
||||||
|
|
||||||
|
-- Durations (in milliseconds).
|
||||||
|
en = 100;
|
||||||
|
qn = 2 * en;
|
||||||
|
hn = 2 * qn;
|
||||||
|
wn = 2 * hn;
|
||||||
|
|
||||||
|
twice x = concat [x, x];
|
||||||
|
|
||||||
|
main = cycle rpgMusic;
|
||||||
|
|
||||||
|
rpgMusic = concat [ partA g
|
||||||
|
, [Wait hn]
|
||||||
|
, twice (partB [b, d])
|
||||||
|
, partB [a, c]
|
||||||
|
, partA b
|
||||||
|
];
|
||||||
|
|
||||||
|
partA t = concat [ program frenchHorn
|
||||||
|
, mel2 c e 4
|
||||||
|
, mel2 c e 5 -- The '=:=' operator merges two lists of actions
|
||||||
|
=:= -- so that they begin simultaneously.
|
||||||
|
(concat [ [Wait wn]
|
||||||
|
, mel2 d t 3
|
||||||
|
])
|
||||||
|
];
|
||||||
|
|
||||||
|
partB firsts = concat [ program trumpet
|
||||||
|
, concat (map mel0 [c, e])
|
||||||
|
=:=
|
||||||
|
mergeMany (map mel1 firsts)
|
||||||
|
];
|
||||||
|
|
||||||
|
-- Instrument-independent melodies.
|
||||||
|
mel0 x = concat [ note wn (x 3)
|
||||||
|
, note hn (x 4)
|
||||||
|
, note en (x 2)
|
||||||
|
, [Wait wn]
|
||||||
|
, twice (note qn (x 2))
|
||||||
|
];
|
||||||
|
|
||||||
|
mel1 x = concat [ note (wn + hn) (x 5)
|
||||||
|
, note (hn + qn) (x 4)
|
||||||
|
];
|
||||||
|
|
||||||
|
mel2 x y n = concat [ twice (note qn (x 3))
|
||||||
|
, concatMap (note hn . y) [3, 4, 4]
|
||||||
|
, note wn (x n) =:= note wn (y n)
|
||||||
|
];
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
When you play the program from the Live-Sequencer GUI, the code in use is
|
||||||
|
highlighted:
|
||||||
|
|
||||||
|
#&img;url=sound-highlight.png, width=640, center, caption=Highlighting of sound, screenshot
|
||||||
|
|
||||||
|
The same could be expressed in ChucK, but the comparison wouldn't be fair. While
|
||||||
|
Live-Sequencer is designed for describing melodies, ChucK's purpose is sound
|
||||||
|
synthesis, which is more general. We'll create something more fitting of ChucK's
|
||||||
|
capabilities, while still focusing on the use of instruments (listen [[more_advanced.flac][here]]):
|
||||||
|
|
||||||
|
#+BEGIN_SRC c
|
||||||
|
// Background music for an old sci-fi horror B movie.
|
||||||
|
|
||||||
|
// Filters.
|
||||||
|
Gain g;
|
||||||
|
NRev reverb;
|
||||||
|
|
||||||
|
// Connect the Gain to the sound card.
|
||||||
|
g => dac;
|
||||||
|
|
||||||
|
// Connect the data sent to the sound card through the reverb filter back to the
|
||||||
|
// sound card.
|
||||||
|
adc => reverb => dac;
|
||||||
|
|
||||||
|
// Instruments.
|
||||||
|
Mandolin mandolin;
|
||||||
|
0.2 => mandolin.gain;
|
||||||
|
Sitar sitar;
|
||||||
|
0.8 => sitar.gain;
|
||||||
|
Moog moog;
|
||||||
|
|
||||||
|
// Instrument connections to the Gain.
|
||||||
|
mandolin => g;
|
||||||
|
sitar => g;
|
||||||
|
moog => reverb => g;
|
||||||
|
|
||||||
|
// Play a frequency 'freq' for duration 'dura' on instrument 'inst'.
|
||||||
|
fun void playFreq(StkInstrument inst, dur dura, int freq) {
|
||||||
|
freq => inst.freq; // Set the frequency.
|
||||||
|
0.1 => inst.noteOn; // Start playing with a velocity of 0.1.
|
||||||
|
dura => now;
|
||||||
|
0.1 => inst.noteOff; // Stop playing.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Play a melody.
|
||||||
|
fun void a_melody(StkInstrument inst, int freq_offset) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
// Fork the command to play "in the background".
|
||||||
|
spork ~ playFreq(moog, 600::ms, 400 - freq_offset);
|
||||||
|
|
||||||
|
for (0 => i; i < 3; i++) {
|
||||||
|
playFreq(inst, 200::ms, 220 + freq_offset + 10 * i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an array and use every element in it.
|
||||||
|
[3, 4, 4, 5, 3] @=> int ns[];
|
||||||
|
for (0 => i; i < ns.cap(); i++)
|
||||||
|
{
|
||||||
|
playFreq(inst, 100::ms, ns[i] * 132 + freq_offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infinite sound loop of pseudo-random frequencies.
|
||||||
|
while (true) {
|
||||||
|
spork ~ a_melody(moog, Math.random2(0, 30));
|
||||||
|
Math.random2f(0.4, 0.9) => g.gain; // Adjust the gain.
|
||||||
|
a_melody(mandolin, Math.random2(0, 350));
|
||||||
|
a_melody(sitar, Math.random2(200, 360));
|
||||||
|
}
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
|
||||||
|
* Algorithmic composition
|
||||||
|
|
||||||
|
Why not have the computer generate the melody as well as the sound? That
|
||||||
|
*sounds* like a great idea!
|
||||||
|
|
||||||
|
Enter [[https: / / en.wikipedia.org / wiki / L-system][L-systems]]. An L-system has an alphabet and a set of rules, where each rule
|
||||||
|
is used to transform the symbol on the left-hand side to the sequence of symbols
|
||||||
|
on the right-hand side. We'll use this L-system to generate music:
|
||||||
|
|
||||||
|
#+BEGIN_SRC c
|
||||||
|
-- Based on https://en.wikipedia.org/wiki/L-system#Example_7:_Fractal_plant
|
||||||
|
Alphabet: X, F, A, B, P, M
|
||||||
|
Rules:
|
||||||
|
X -> FMAAXBPXBPFAPFXBMX
|
||||||
|
F -> FF
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
If we evaluate a L-system on a list, the system's rules are applied to each
|
||||||
|
element in the list, and results are concatenated to make a new list. If we
|
||||||
|
assign each symbol to a sequence of sounds and run the L-system a few times, we
|
||||||
|
get [[lsystem.flac][this]].
|
||||||
|
|
||||||
|
#+BEGIN_SRC haskell
|
||||||
|
module LSystem where
|
||||||
|
|
||||||
|
import Prelude
|
||||||
|
import List
|
||||||
|
import Midi
|
||||||
|
import Instrument
|
||||||
|
import Pitch
|
||||||
|
|
||||||
|
en = 100;
|
||||||
|
qn = 2 * en;
|
||||||
|
hn = 2 * qn;
|
||||||
|
wn = 2 * hn;
|
||||||
|
|
||||||
|
-- Define the L-System.
|
||||||
|
data Alphabet = X | F | A | B | P | M;
|
||||||
|
expand X = [F, M, A, A, X, B, P, X, B, P, F, A, P, F, X, B, M, X];
|
||||||
|
expand F = [F, F];
|
||||||
|
expand _ = [];
|
||||||
|
|
||||||
|
-- Map different instruments to different channels.
|
||||||
|
channelInsts = concat [ channel 0 (program celesta)
|
||||||
|
, channel 1 (program ocarina)
|
||||||
|
, channel 2 (program sitar)
|
||||||
|
];
|
||||||
|
|
||||||
|
-- Define the mappings between alphabet and audio.
|
||||||
|
interpret X = [Wait hn];
|
||||||
|
interpret F = channel 0 (note qn (c 4));
|
||||||
|
interpret A = channel 1 (note qn (a 5));
|
||||||
|
interpret B = channel 1 (note qn (f 5));
|
||||||
|
interpret P = channel 2 (note hn (a 3));
|
||||||
|
interpret M = channel 2 (note hn (f 3));
|
||||||
|
|
||||||
|
-- A lazily evaluated list of all iterations.
|
||||||
|
runLSystem expand xs = xs : runLSystem expand (concatMap expand xs);
|
||||||
|
|
||||||
|
-- The sound from a L-System.
|
||||||
|
getLSystemSound expand interpret iterations start
|
||||||
|
= concatMap interpret (runLSystem expand start !! iterations);
|
||||||
|
|
||||||
|
-- Use the third iteration of the L-System, and start with just X.
|
||||||
|
main = channelInsts ++ getLSystemSound expand interpret 3 [X];
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
Using an L-system is one of many ways to take composition to a high
|
||||||
|
level. L-systems can be used to generate fractals, which are nice.
|
||||||
|
|
||||||
|
|
||||||
|
* And so on
|
||||||
|
|
||||||
|
Many abstractions in sound generation allow for fun sounds to happen. Interested
|
||||||
|
people might want to also take a look at e.g. [[http://haskell.cs.yale.edu/euterpea-2/][Euterpea]], [[http://puredata.info/][Pure Data]], or [[http://csounds.com/][Csound]].
|
||||||
|
|
||||||
|
#&line
|
||||||
|
|
||||||
|
Originally published [[http://dikutal.dk/artikler/sound-programming][here]].
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 139 KiB |
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue