Oscilloscopes are cool. What is even cooler is using them to display arbitrary pictures instead of boring waves! We also can use them to write text (emulate a terminal) or maybe even play pong! But for now, let's focus on the basics of dot drawing using sound...
Dumb Shortest Path
An oscilloscope is a device that allows you to visualize signals (voltages) over time.
When dealing with multiple signals (channels), you usually have three viewing mode: Dual, Add, and Chop. We will work in Dual.
For us the most interesting oscilloscope feature is called X-Y. In this mode you will view a single dot. The horizontal scale will no longer be time. It will be Channel 2's voltage. The Channel 1 will move the dot up and down according to its input voltage, the Channel 2 will move the dot left and right according to its input voltage. A bit like an Etch'n'Sketch.
A speaker creates sound by pulsing air at different frequencies. Sometimes it pushes air (positive voltage), sometimes it pulls air (negative voltage). In other words a speaker is driven by alternating current.
By controlling the amplitude of the alternating current (or sound), we will be able to move the dot on the screen.
Another key element to our project is stereo sound. Stereo sound means that we have two sound signals: Left and Right, X and Y.
The oscilloscope has a sampling rate of millions of hertz. In other words it can measure the signals and move the dot accordingly millions of times per seconds, giving us impressive theoretical FPS for whatever we want to draw.
BUT, the sampling rate of most digital music player is limited to 48000 hertz (or at best 96000 Hz), greatly limiting our FPS for complex pictures.
Sound also have advantages like portability:
- You can generate the sound file and bring it with you on your cellphone to do your demo on any oscilloscope around or share it on the demoscene
- No extra hardware necessary, any sound card will work.
We will move the dot so fast that the eye will be tricked into seeing a full picture.
We set our scales at 1V and we center the dot (X-Y) on the oscilloscope screen. We will use a stereo sound source (two voltages) to move it. Left sound will move it horizontally and right sound will move it vertically. See first table up this page for an overview.
This technique will draw a complete column (Y) pixel by pixel then advance the row (X), draw a new column, advance row, loop...
Thanks to Chris I realized that this technique only works if you have an oscilloscope with low display persistence and fair sampling rate.
If your scope meet these criteria the vertical lines in the image above should be fairly invisible.
If your oscilloscope doesn't meet these criteria then dots that shouldn't be connected together will be connected vertically(red lines in image), creating random lines hiding the picture's content.
My first script creates a wave file to draw sequentially. Find it at the end of this article.
A better approach is to use a shortest past or path finding algorithm.
Basically what we want to do is compute the best(shortest) route possible to connect all our pixels. The dot on screen will follow that route and hopefully minimize the noise (random lines) connecting isolated shapes. There will still be unwanted lines to connect our shapes, similar to a neon sign or cursive writing; but they will usually be thinner and dimmer than the rest of the shapes.
My second script implements a relatively dumb path finding algorithm. The result is correct for the pictures I tested, but I'm studying real algorithms and hopefully I'll do a proper path finding script. Find the current script at the end of this article.
An elegant solution would be to generate vector graphics instead of raster. Using vectors would allow us to leverage the display persistence to form our shapes. I believe that is how Youscope was done. While it should be possible to transform a simple raster (gif/png) in vectors, it is way beyond my knowledge of image manipulation :).
A digital sound sample is a number that represent the amplitude of the audio signal.
That number will represent one pixel coordinate (X or Y).
The bits per sample, or bit depth, is the resolution of the sample. With 8 bits, we have 256 amplitudes possible (-127 to +127).
Assuming stereo sound at 8 bits, we could draw a picture of 65536 pixels (256x256). Because of output quality and oscilloscope precision, we could benefit of using 16 bit sound while keeping a low resolution and over sample it to make a sharper image.
The sampling rate is how many samples are recorded or played in one second. Most digital devices have a sampling rate of 48000Hz (CD is 44100Hz). Because we use stereo sound, this means we can move our dot 24000 per second. To trick the eye we need at least 10fps, this means that we can only draw an image of 2400 pixels.
Considering the oscilloscope's dot luminosity and our FPS limitation at this resolution, we can skip pixels. One full pixel, one empty pixel. The dot size would fill the voids.
That brings us to 16384 pixels (128x128), or an ability to fill 15% of the screen and refresh it 10 times per second.
The wave format is perfect for us. We can use arbitrary sampling rate, bits per sample, and channels. Following the header, each channel samples are interlaced. The data look like this: left sample, right sample, left sample, right sample...
For simplicity we will assume a 128x128 image with a white background. Anything that is not white will be a pixel to draw.
- We can use /dev/dsp to stream the wave directly to the sound card by setting a very high sample numbers in the header
- Using lines.
- Going back to 0V before moving to the next pixel
- Using closest pixel instead of doing line per line
- Refresh rate 44100hz
This PHP script allows you to transform an animated GIF or a directory of PNG files into a sound wave file.
It can be run locally:
php oscillo.php inputfile.gif outputfile.wav
Or from a web page: