Store data on analog media - cassette tapes, minimodem

An interesting yet meaningless thing to do is to store data on analog media, like audio cassette tapes. Commodore 64 among other vintage systems managed it, so why not try it with a modern machine?

The thing with analog storage is that you need to convert the signal from digital to analog when writing the data and back again from analog to digital when reading the data. So you need some kind of hardware converter.  A regular computer sound card with line in and line out will do fine here. I also assume you have connected a cassette tape deck with a tape to the sound card:s line in and line out ports.

You will also need some kind of program to perform the actual conversion.

minimodem is such program on Linux systems, install it in Debian by doing:

sudo apt-get install minimodem

You can now encode and decode data:

Encode:
echo Hello | minimodem -t 1200

Decode:
minimodem -r 1200

Encoding will result in noise in your speakers, decoding will result in that minimodem listens to the microphone port.
To avoid that and go by WAV files, add -f file.wav to the commands. You may also raise the speed - if the media allows you to and you can also compress the data with gzip and encode and decode textfiles :

Encode:
gzip -c textfile.txt | minimodem -t 1200 -f file.wav

Decode:
minimodem -r 1200 -f file.wav | gzip -cd

If you want to change playback and record devices, supply --alsa=x,y to minimodem
Replace x,y with the values of aplay -l and arecord -l (I guess the card number and the device number, --alsa=1,0 for example).

Make sure you avoid distortion, volume may be decreased by using the -v parameter, like this to set it to 50%:
gzip -c anyfile | minimodem -v 0.5 -t 1200 -f file.wav

To encode, you can also first encode it and then play it through mplayer instead if you like:

cat test.txt | minimodem --tx 1200 -f out.wav
mplayer out.wav

Input/output audio levels are VERY important. To adjust input to tape I adjusted levels according to the meters on the cassette deck.

For output from tape to computer adjustment I recorded silence on a cassette using no input at all.  Then I asked minimodem to decode it using minimodem --rx 1200. I found out that minimodem detected a lot of false carriers.

So I went to the input controls and decreased the input level till no carrier was detected.

Another method seems to be to raise the barrier of minimodem for when it detects something, use parameter -c 1.5, increase it to 1.6 and then 1.7 until you get no extra carrier data when there is silence.

Encoding and decoding WAV files is no problem. The problems come when the tape recorder is introduced into the chain, making noise, skew and audio levels need to be adjusted.

Time is a problematic factor too. Small files occupy too much time.

10,6 kB @ 9600: ~11 seconds
10,6 kB @ 4800: ~22 seconds
10,6 kB @ 2400: ~44 seconds
10,6 kB @ 1200: ~88 seconds (1,47 min)
10,6 kB @ 600: ~176 seconds
10,6 kB @ 300: ~352 seconds (5,87 min)

300 baud seems to be standard for a normal C64 tape according to this thread:
http://www.lemon64.com/forum/viewtopic.php?t=54041

For C64, "Datasettes could typically store about 100 kByte per 30 minute side":
https://en.wikipedia.org/wiki/Commodore_Datasette

3,52 (for 300 baud, see above) * 10 = 35,2 min for 106 kB, so 300 baud seems right.



Encode and decode multiple files

gzip works for simple texts, but if you want to store a whole directory you can use tar together with gzip (-z is for gzip and may be substituted with gzip| after and before tar):

Encode - this encodes all jpegs in the current directory:
tar -czf - ./*.jpg|minimodem -t 1200 -f file.wav

Decode - this outputs files to the current directory:
minimodem -r 1200 -f file.wav --rx-one|tar -C ./ -xzvf -

Note --rx-one, which makes minimodem halt after the first Carrier-No carrier-event.

Again, there is very much time used for very little data:
For example, for 3 JPEG:s  of total 900 kB you get a WAV file of about 31 minutes at 4800 baud.

If you decrease the baud from 4800 to 300 which is what Datasette used, you get 2 hours and 36 minutes (900 MB WAV) for a SINGLE jpeg of about 280 kB. For a JPEG of 85 kB you pay 47 minutes at 300 baud, push the tape to 1200 bps and you get 12 min.



Storing and running PHP programs 

What is storing data onto analog media without running programs directly from it?

Make a simple PHP script to calculate 1+1:
echo '<?php echo "1+1=".(1+1)."\n"; ?>' > test.php

Make sure the code is clean and works:
php -l test.php

Make sure the cassette player is recording and that the levels are not going beyond 0dB on the meters.

Let gzip compress and minimodem do the conversion to tones:
gzip -c test.php | minimodem -t 1200

Stop the recording, rewind.

Ask minimodem to listen, send the data to gzip which forwards it to PHP for execution:
minimodem -r 1200 -c 2.3 --rx-one| gzip -cd|/usr/bin/php

To make it more advanced, you could write a little loader.
Put this into loader.php, then run it using /usr/bin/php loader.php

Adjust the -c parameter to the noise level of your tape.

<?php
	$tmp = tempnam('/tmp', 'wavread');
	echo 'Waiting for data...'."\n";
	$c = 'minimodem -r 1200 --rx-one -c 2.5| gzip -cd|cat > '.$tmp;
	exec($c, $o, $r);

	if (!filesize($tmp)) {
		unlink($tmp);
		echo 'No file found.'."\n";
		die(1);
	}

	unset($c, $o, $r);
	exec('/usr/bin/php -l '.$tmp, $o, $r);

	if ($r!==0) {
		echo 'File did not pass PHP validation.'."\n";
		unlink($tmp);
		die(1);
	}


	echo 'Program found and ready, stop tape, press y and return to run program: ';
	$input = fgetc(STDIN);
	if ($input != 'y') {
		echo 'Aborted.'."\n";
		unlink($tmp);
		die(1);
	}


	echo 'Running program '."\n";
	require_once($tmp);
	unlink($tmp);
?>



Real test

I wanted to try this out for real, so using a cassette deck, a sound card and a well used cassette I tried to read and then write files.

It turned out that 1200 bps worked, although it is very important that you use a part of the tape that does not have any loss of sound. Loss results in that you get carrier loss or tar does not want to work.

minimodem continues in an endless loop to decode data if you do not tell it to stop with --rx-one.
Then it stops after the FIRST Carrier-No-carrier event, which leads to -c which sets the sensitivity level of when a carrier is detected.

I made up the two following small snippets, to read files from /input/ and to write the files to /output/.
Make sure the correct sound card is selected. Alternative working speeds are commented out.

Important is that the volume level is NOT beyond 100% on the volume meters when recording. Any distortion because of hitting 0dB will mess up the tones.

write.sh:

#!/bin/bash
killall minimodem
tar -czf - ./input/*|minimodem -t 1200 -v 1
# tar -czf - ./input/*|minimodem -t 2400 -v 1

read.sh:

#!/bin/bash
killall minimodem
minimodem -r 1200 -c 2.1 --rx-one|tar -C ./output/ -xzvf -
# minimodem -r 2400 -c 2.3 --rx-one|tar -C ./output/ -xzvf -



More reading:
http://www.raspberrypi.org/forums/viewtopic.php?t=49495&p=385554
http://www.mvcsys.de/doc/casioutil.html
http://dabeaz.blogspot.se/2010/08/using-python-to-encode-cassette.html
http://www.dabeaz.com/py-kcs/index.html
http://www.instructables.com/id/Storing-files-on-an-audio-cassette/
https://github.com/windytan/ctape/

This is a personal note. Last updated: 2016-03-02 21:51:17.



GitHub

My

GitLab

My

LinkedIn

My

Klebe.se

Don't forget to pay my friend a visit too. Joakim