Sunday, October 28, 2007

Audio

Unlike most of the projects I've been working on, this one was related to a class. (But it is still thrown-together, undocumented, and uses several list comprehensions to write an audio effects in one complicated line of code). I'm in a course called Engineer's Orchestra where we build instruments and do physics. I was curious about how sound files on a computer work , so I set off learning a lot about digital audio. The first fun part was to learn the insides of a .wav file and write one starting from only bytes. Then, as a project for the class, I wrote a complete audio editor from scratch!

I wrote it in Python, because it would take me the least time to write it. My challenge was to write this without referencing any other code, like Audacity. This made it more fun.

The GUI isn't very good, specifically because there are no other dialogs, but I have most of the important features covered: Add Silence Before, Add Silence After, Chop, Append File, Mix File, Modulate, Make Louder, Make Softer. And, it loads and saves standard wav files (16 bit or 8bit), so it is actually practical for editing things you have recorded.

Then, I added a lot of audio effects. I would read the gist of an effect on Wikipedia and try to code it. This was even more fun, because if I got something wrong, it sometimes played very strange sounds. (I don't have any analog-style effects, like filters yet, but this is something I would really like to learn). In the GUI, you can use the following effects: Reverse, Octave up/down, Change pitch (the most challenging to think of how to write), Vibrato, Flanger, Echo, Reverb, Chorus, and Alien Voice. The alien voice modulates the samples with a sine wave - I came up with it while experimenting and don't know why it sounds strange.

Listen to these (the wavs were generated by my program):
Vibrato
Low
Reverb
Alien Voice

Next, I added code to synthesize sounds. Sine, triangle, square, and sawtooth waves aren't that great by themselves, so I tried combinations of them to get the most interesting voices. The secret to making a sine wave sound better is to quietly mix in frequencies at that are slightly different. Surprisingly, the subtle beat frequencies make it sound expressive and musical. I worked a long time to create an electric organ-like instrument out of sine waves, and finally came up with something.

Here are the results:
Electric Organ
Smooth
Retro
Red Noise
Squarephase

Finally, I put all of the pieces together. I had an audio editor that can modify and arbitrarily change the pitches of sounds. So, I made a sampler! I wrote a command-line interface for a program where you can type in notes and play them. It parses commands like a4 into the note A440, or 4.f#5 into "play a dotted quarter note at F# 5". As an instrument, you can use anything - samples of your voice, a sample of a real instrument, your computer's MIDI, or even the same synthesized instruments like Electric Organ or Smooth.

The program works well (the effects even work in both 16bit and 8bit sounds).

Lest you think this program is polished, I must admit that it is not. This is not a joke, it is actually in the source:

manualsynth(audiodata, nsamples, _extend([[freq, .5*0.9**(8-1)]], [[freq*(i+1),.5*0.1*0.9**((8-1)-i)] for i in range(1,8)],[[freq*1.00606, .5*0.9**(8-1)]], [[freq*1.00606*(i+1),.5*0.1*0.9**((8-1)-i)] for i in range(1,8)]))


I should get around to fixing that sometime.

Saturday, October 27, 2007

Quick Unicode Entry

This project doesn't have any direct use now, but it was fun to write, and might be useful for writing letters in Spanish. I can simply hit Alt-A to get an a with an accent, Alt-Shift-1 to get ¡, and define any other combination I want.

This program is a lightweight text editor intended for writing text in other languages. Most word processors have a "Insert Symbol" option for inserting a character, but this process is too slow. If you do all of your typing in another language, one can set the system language, but this will be a system-wide change and is not very customizable. In this program, on the other hand, it is simple to set up your own keymaps and choose what keys create which characters. One can also conveniently visualize these bindings.

Because some languages require access to more than 26 symbols, this program uses the concept of "modes." For example, the default keymap has a mode called Grave Accent. In this mode, typing a vowel like o will produce o with a grave accent. The program begins in Normal Mode, but you can press Control+L to enter Grave Accent mode. View the available modes for the current keymap by choosing List Modes from the Characters menu.

When you enter a mode, you stay in that mode until you press the key combination, typically Control+Space, to return to Normal mode.

Edit the current keymap by choosing "Edit key bindings" from the Characters menu. (Changes take place when the mode is selected from the Characters menu). Create a new map by creating a .py.js file in the keymaps directory.

The Code. Unzip and run "main.py." Works in Python 2.5. Linux is not yet supported, as it seems the keycodes are different.

I spent some time coming up with good keymaps. It comes with a map for characters in Latin-1 and a very useful Greek map.

Wednesday, October 24, 2007

Interesting Facts in Biology

Did you know?
  • Slugs are gastropods that have white flowers for a specific tissue.
  • This has produced a nuclear matrix secreted into the bird’s plasma membrane.
  • Individuals associate with polar molecules such as that of the worm through the mitochondrial and chloroplast genomes of the urethra.
I wrote a program that will analyze and automatically summarize Biology. (Very useful). It's a web app, but is on Olin's server right now. I compiled a huge amount of Biology information and made a Markov model. I did a lot of work in Python stripping unwanted data. At first, I was putting all of the data in a php file, but this turned out to be inefficient and a heavy load on the server. I ended up with a solution: I divided all of the text into 64 slices, and then created 8 medium-sized files out of shuffled slices (for variety). I then wrote Python code to generate one Markov model for each of the files and save the results as a Javascript object .js file. Then, the php code randomly picks one of the 8 .js files to include whenever the page is loaded. The Markov generation is all client-side, so you can generate many times without needing a page refresh.

Sunday, October 21, 2007

Computer Science + Lolcats

I did some photoshopping last week :)

If you've taken a Computer Science course, you may find this humorous. It's my Powerpoint presentation of Computer Science + Lolcats.

(Feel free to copy these but give me credit).

Saturday, October 13, 2007

Beep

This weekend I thought it would be cool to learn about digital audio. Specifically, I wanted to look inside of audio files and know how they work on the level of bytes. Also, I'm going to do my exploring without looking at other source, like Audacity, to make things more interesting.

It turns out that digital audio files are split up into frames that are typically either 8-bits or 16-bits. So a wave file is essentially list of 8-bit or 16-bit numbers that represent the pressure on the microphone. The frame rate dictates how many of these frames go by per second.

In thirty minutes, I was successfully able to come up with code for creating a .wav file at any frequency:
import wave
import math
import array

#Create a 16 bit wave
width = 2 #16 bits is 2 bytes
maxval = 65535
sample_rate=44100
channels = 1
samples = array.array('h') #Signed short

# Create a sine wav, of frequency fFreq
nSamples = sample_rate*2 #two seconds long
fFreq = 440
w = fFreq*2*math.pi / sample_rate
amplitude = 65535/2 #Loudest amplitude
samples.extend( (amplitude * math.sin(w*x)) for x in xrange(nSamples) )

f = wave.open('c:\\a440.wav', 'wb')
f.setnchannels(channels)
f.setframerate(sample_rate)
f.setsampwidth(width)
f.writeframes(samples.tostring())
f.close()
Some of my ideas: The array type works perfectly for storing the samples - it is far more efficient than a Python list. Note also that I am using a "generator expression", which works similarly to a list comprehension but does not need to actually create the list. In the future I plan on using generators for creating the signals.

It is weird that 16-bit sound is in the "signed short" format, instead of being unsigned.

On a less serious note, I then saw the beep method winsound module, and was amused:
import winsound
# so retro!
winsound.Beep(440,10)
winsound.Beep(600,10)
#bonus stage!
for i in range(40,400,30):
    winsound.Beep(i,90)
There's no way to control the volume, though, so this can get annoying.

Next I'll be writing some sweet audio effects.

Thursday, October 11, 2007

Recursive Shapes v0.2

This project took far longer than half an hour, but it's pretty cool.

While doodling in Bio, I thought of a way to programmatically draw any recursively-defined shape. By this, I mean any shape that contains a scaled-down version of the entire shape. This is a property found in many fractals.

Traditionally, and in all of the fractal-drawing software of this type I've seen, the shapes are defined with turtle-style commands (turn right, draw a line, turn left, and so on). There exists a specific system called L-System that can define recursive pictures, and with a lot of work can probably define everything my program can, with the exception of curves. However, when looking at the shape, I do not always find the long list of turtle instructions or L-System rules to be intuitive. I decided to instead use traditional Euclidean x and y coordinates, and Python code as the rule defining the next iteration. This way, I can sketch out a shape on a piece of paper and enter its code very quickly and clearly. It is much easier to me to enter a shape in terms of x and y.

I had an idea for drawing curved lines that turned out very well. The code actually draws an arc of a circle, but positioned so that the arc passes through the two points. A parameter specifies how much curvature should be given.

Looking at the still images is only half of it: I added an easy way to draw animations! There are a lot of really cool animations defined in the samples. (Play them by removing the # and pressing draw.)

Here is the code (71k). (Requires Python 2.5, cross-platform). All you need to do is unzip the file, open main.py, load a shape from the shapes menu and start playing with it! The argument to the .draw() method is the number of iterations (don't make this too big). You are encouraged to tweak the code or create your own shapes.
I wrote this just for fun, and it's (unfortunately) not connected with anything I'm doing at school.
Planned for the next version:
Drawing images, ellipses, text. Save images as PNG files, anti-alias option. Making variable names consistent... and a revolutionary improvement that is still in the works.

Sunday, October 7, 2007

So Good It's Bad!

I was playing Windows Entertainment Pack Tetris. After the 10th level, it doesn't get any faster (of course it is fast enough) - and so I was playing for a while, when suddenly I noticed that I had a negative score!!! The integer representing my score had overflowed! The Windows Entertainment Pack version of Tetris must display your score as a signed 16-bit integer (I think it's even a 16 bit program). Because of 2s complement, an integer will after being large positive will be large negative. The number still sorted as larger than the other scores, and counted as a high score, though. Cool.

Wednesday, October 3, 2007

Unreadable Python


exec(")))'YLRJHOUVO[`WYLO[VUH',dro(pam,)52+c(rhc:c adbmal(pam,__dda__.rts(ecuder tnirp"[::-1])

Actually, I could have made it a lot worse. But it was still fun. Figured it out? Try this one:

print reduce(str.__add__,map(lambda c:chr(c+25),[(72-sum([ord(c)-74 for c in '=IEVM=LAOVCKPQHBP='][:i])) for i in range(19)]))