Added word finder and sound programming articles.

This commit is contained in:
Niels G. W. Serup 2013-02-11 13:40:29 +01:00
parent 6c244ddd70
commit 286588366a
10 changed files with 739 additions and 0 deletions

View File

@ -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

View File

@ -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

View File

@ -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.