Periklis Ntanasis:
Master's Touch

fade out

Signal generation by math formulas for the greater good!

My first encounter with computers was by playing video games. The first video game I remember playing was pacman in an 80386 machine maybe somewhere around 1994.

Back then computers were equipped with sacred resources. The smartphones we are carrying around in our back pockets would seem like super computers.

Today, we don’t have such limitations most of the times.

Just for fun, let’s see how we could store and playback audio, animation or other media by using as little storage as possible.

Generation of signals using mathematical formulas

Before I go to animated images let’s talk about audio because I think it is more straightforward to understand the concept.

Let’s say you want to write some sound effects or a little soundtrack for a game. What options do we have for distributing this audio?

Of course an uncompressed audio clip could be distributed in .wav format but this is totally uncompressed and would be unnecessary big.

Let’s talk compression then. FLAC is a nice option. It is lossless and would more or less make the file 3 times smaller. This is still too big.

After some research at the end we would probably find out that OGG would produce good lossy compression and after some research about the quality and compression options we would probably end with something like 10 times smaller than the original sound.

This is pretty good and I guess this is what most game developers would do nowadays.

What other options are there though? We could produce the audio by code of course!

OK, yes this won’t do for all the cases but this was more or less how audio was created in the NES era.

By using an oscillator various audio waves could be produced and combined in order to create the desired outcome.

Later devices such as SNES permitted the playback of prerecorded sampled audio. Of course the limited RAM of that systems was restricting the artists of the time who had to think out of the box in order to produce beautiful songs.

Today, I guess there are a few people who do similar stuff in some extend for fun. I can think of 3 main categories where this could be observed:

  • People writing emulators for old systems or reverse engineering old video games (think 8/16-bit)

  • The Demoscene community

  • The Competitive Programming community

MIDI standard is another example where audio can be produced by code. A .midi file is way smaller than the equivalent .wav as it contains roughly speaking notes and their duration and not the actual sampled audio.

Software generated waveforms

From the above methods, producing audio by using one or more mathematical functions is probably the one that would produce the smallest outcome.

Building on the idea of the oscillator, there could potentially be a mathematical formula that would produce the exact analog audio waveform (or any other signal) we would like to reproduce.

This could be the best technique of lossless compression. However, finding such a formula for most cases other than the very simple ones is way hard.

However, it is relatively easy to create simple and well-defined signals. A sine wave for example or a circle.

For example there is a Python module that let’s you create and playback sine waves!

Of course there is a hidden cost of intermediate libraries but this should be considered as common infrastructure.

Generating pacman with code

The circle happens to be the pacman character, more or less!

One way to create an animated image for a 2D game is to have a set of images and by changing them in the right intervals produce the effect of animation.

These images are usually part of one bigger image file called sprite.

The pacman however is a partial circle filled with color, probably yellow!

Here is an example of some lua(LÖVE) code that demonstrates a moving pacman created by a mathematical formula and by interchanging images for comparison.

wak wak wak…

For reference here is the code. Don’t mind it too much, it is a quick and dirty approach :-)

local x = 20

-- image code
local images = {}
local image
local quad
local t = 0
-- gen code
local PI_QUARTER = math.pi/4
local TWO_PI = 2*math.pi
local arc_start = PI_QUARTER
local arc_end = TWO_PI - PI_QUARTER
local animation_value = PI_QUARTER
local animation_state = 1

function love.load()
        love.window.setTitle('Pacman')
        love.window.setMode( 800, 150)
        -- image code
        images[1] = love.graphics.newImage('pacman1.png')
        images[2] = love.graphics.newImage('pacman2.png')
        images[3] = love.graphics.newImage('pacman3.png')
        quad = love.graphics.newQuad(0, 0, 20, 20, 20, 20)
        image = images[1]
end

function love.draw()
        -- image code
        love.graphics.draw(image, quad, x, 100)
        -- gen code
        love.graphics.setColor(unpack({255, 255, 0}))
        love.graphics.arc("fill", x + 10, 60, 10, arc_start-animation_value,
        arc_end+animation_value, 100)
end

function love.update(dt)
        -- image code
        t = t + dt * 6
        if t < 1 then
                image = images[1]
        elseif t < 2 then
                image = images[2]
        elseif t < 3 then
                image = images[3]
        elseif t < 4 then
                image = images[2]
        else
                t = 0
        end
        -- gen code
        arc_start = PI_QUARTER
        arc_end = TWO_PI - PI_QUARTER
        if animation_value >= PI_QUARTER then
                animation_state = 2
        elseif animation_value <= 0 then
                animation_state = 1
        end
        if animation_state == 1 then
                -- close mouth
                animation_value = animation_value + dt * 3
        elseif animation_state == 2 then
                -- open mouth
                animation_value = animation_value - dt * 3
        end
        x = x + 50 * dt
        if x > 800 then
                x = -20
        end
end

The point is that the 3 images used to create the pacman animation are a bit less that 6kb in total while the code version is uncompressed less than 1kb.

In that spirit BeatKeeper is less than 30kb in size. The audio produced by the metronome is a sine wave created in code.

As a bonus, animating pacman that way produces a more fluent motion. In order to achieve this smoothness the other way it is required to move faster in order to trick the eye or use more images that would result to an even bigger sprite.

Conclusion

Today we don’t have to worry if our program will fit in one floppy disk or be able to fit whole in our computer’s RAM.

On the other hand it would be a great deal if we could easily find mathematical formulas that produce a desired signal because then we could achieve very efficient compression. This is pretty hard though and maybe it will never be achieved.

What is important however and we can achieve by doing this kind of stuff is reminding to ourselves that programming is fun!

So, until the next time… have fun!

Comments

fade out