Tool for digitalized retro computer cassettes

bzt d71448c3f5 Better decoding when there's no sync bit 1 year ago
examples e16d7204fe Fixed typos 1 year ago
libmp3lame 244e86e116 Initial commit 1 year ago
public d71448c3f5 Better decoding when there's no sync bit 1 year ago
.gitlab-ci.yml 66c8ce2858 Added webservice 1 year ago
LICENSE 244e86e116 Initial commit 1 year ago
Makefile f1d2dc08c9 Updates, fully working with Homelap tapes 1 year ago
README.md 4e946f16fb Skip samples, better messages, more heuristics 1 year ago
decode.c d71448c3f5 Better decoding when there's no sync bit 1 year ago
encode.c 4e946f16fb Skip samples, better messages, more heuristics 1 year ago
main.c 4e946f16fb Skip samples, better messages, more heuristics 1 year ago
minimp3.h 244e86e116 Initial commit 1 year ago
wav.h 244e86e116 Initial commit 1 year ago

README.md

Cassette

This is a quick'n'dirty tool that can convert digitialized retro computer cassettes (C64, Spectrum, Homelab etc.) into binary files and vice versa.

Available as a CLI tool and as a webservice.

Compilation

Just run make, no dependencies (other than libc). Type make wasm to compile the WebAssembly version (requires emscripten).

Usage

Cassette by bzt GPLv3+
https://gitlab.com/bztsrc/cassette

./cassette [opts|-s] <in.wav|in.mp3> <out.bin>
./cassette [opts] <in.bin> <out.wav>
./cassette [opts|-b|-q] <in.bin> <out.mp3>

  -v | -vv      be verbose
Data encoding options
  -e            endianess, least significant bit comes first
  -7            assume 7 bits per byte on the tape
  -0            no sync bit (default, 0 = 0, 1 = 1)
  -1            two bits, first is sync bit (10 = 0, 11 = 1)
  -2            two bits, second is sync bit (01 = 0, 11 = 1)
  -3            three bits, two sync bits (101 = 0, 111 = 1)
  -d            FM, frequency modulated, down-edge (defaults to AM)
  -u            FM, frequency modulated, up-edge
  -du           FM, frequency modulated, both edges
Sample encoding options
  -h            do not detect but generate Homelab header
  -s            input is signed 8-bit PCM
  -S <n>        skip N samples at the beginning
  -l <usec>     one bit's length in microsec (eg. 770)
  -g <usec>     gap between bytes (AM) or diff in zero and one lengths (FM)
  -r <rate>     sample rate (eg. 44100 or 48000)
  -o <num>      bit set level (eg. 127)
  -z <num>      bit clear level (eg. -32)
MP3 encoding options
  -b <bitrate>  bit rate (eg. 96, 128 or 192)
  -q <quality>  quality (0 best, 9 worst)

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

Audio to binary

To extract binary data from a digitalized cassette audio file (where 8 bits make up a byte and most significant bit comes first), just use

./cassette audio.mp3 image.bin

If the old computer have used 7 bits in a byte, then

./cassette -7 audio.mp3 image.bin

If the least significant bits were stored first on the tape, then you can change endianess with

./cassette -e audio.mp3 image.bin

Or you can combine the two:

./cassette -e7 audio.mp3 image.bin

Normally the length (how many samples make up a bit) is autodetected by examining the up-edges. If this fails for whatever reason, then you can specify manually with

./cassette -l 10 audio.mp3 image.bin

This would assume that two consecutive set bits have a down to up edge at 10 μsec distance.

|   ___   ___       ___
|  |   | |   |     |
|  |   | |   |     |
+--|---|-|---|-----|---
| _|   |_|   |_____|
|  .     .
|  .<--->. 10 μsec

For frequency modulation, -l overrides the calculated average distance, and acts as the distance treshold length.

Normally all input PCM should have signed samples. However due to some f*ck up by Microsoft, WAVE files storing 8-bit PCM data are tend to store unsigned samples (Audacity saves unsigned 8-bit samples too). This is taken care of, but you might run into a 8-bit WAVE file that actually uses signed samples, just like the 16-bit variant. Use the -s flag in this case, to avoid unsigned to signed sample conversion.

Otherwise the WAVE reader is quite flexible, it supports 8-bit, 16-bit, 24-bit, 32-bit integer and float sample formats, with any number of channels (those would be converted to mono automatically). Additional and unknown chunks are also considered.

For MP3 decoding, Cassette uses minimp3.h (included).

Binary to audio

The other direction works as

./cassette image.bin audio.wav

This reads in image.bin, and creates an audio file that you can use with the vintage computer (or emulator).

You can also set the endianess and number of bits in a byte with -e and -7 here too, respectively. The length of one bit defaults to a duration that results in 4 samples (depending on the frequency), but you can set it with -l in microsec. Normally 1/4 of that duration is used to encode the bit's value, and 3/4 at the end is always zero, so that up-edges can be detected correctly. You can also set the bit is set level (one) between 127 and -127 with -o and bit is cleared level (zero) with -z like

./cassette -l 771 -o 127 -z 0 image.bin audio.wav

Note that encoding with MP3 introduces a significant noise which might cause trouble. To avoid this, set bit level defaults to 127 and cleared bit level defaults to -64, so that all the noise fluctuations will be in the negative range, and only true one values will be positive. You can also set the sample rate with -r, used to calculate how many samples -l and -g durations mean.

To save in MP3 format (uses LAME, also included), just use a filename that ends in .mp3:

./cassette image.bin audio.mp3

In this case you can also specify the bitrate (defaults to 96) and the quality (defaults to 0, best). For example

./cassette -b 192 -q 5 image.bin audio.mp3

Please note that MP3 is particularly inadequate for this job; it's not just the lossy compression and the added noise, but also because of poor design. Its codec introduces a mandatory silence at the beginning and at the end of the samples, however there's no standard way to tell how much silence has been added. Cassette generates the Xing header for this purpose when it creates MP3 files, which is quite common, but not guaranteed to be recognized by all software. You could also run into trouble if you try to decode an MP3 audio file with Cassette which doesn't have a Xing header. Better to stick with WAVE files if you run into trouble. (No, opening in Audacity won't solve your problem, because Audacity does not trim the MP3 samples, so you will always actually see that codec introduced silence.)

Signals handling

Amplitude Modulation

This is the default, no flags. Samples are taken at regular intervals (see the red dots above at equal distances). The interval is autodetected by examining the up-edges, but you can specify it with -l. If the given sample at one of the specified time is below the base line, then it encodes a 0 bit. If it's above the base line, then it encodes a 1 bit. (The wave is normalized, so it doesn't matter what the actual base line is. Cassette will take care of this for you).

Frequency Modulation (Down-edge)

Using -d, you can set up frequency modulation with down-edge. Here samples are taken when the wave crosses the base line from above. As you can see, the distances between the red dots aren't regular this time. All distances considered and their average is taken (or you can specify with -l). If a particular distance is shorter than the average, then it encodes a 0 bit. If it's longer than the average, then a 1 bit. When the wave is generated, you can set the length of the 0 bit with -l, and 1 bit will be 3 / 4 longer. If this does not suit you, then you can explicitly set how many microsecs longer 1 should be with the -g flag (two samples at least).

Frequency Modulation (Up-edge)

Using -u, you can set up frequency modulation with up-edge. Same as the previous, except samples are taken when the wave crosses the base line from below. Similarly distance average taken, and shorter distance means 0 bit, longer distance means 1 bit.

Frequency Modulation (Both)

Using -du, you can set up frequency modulation with both edges. Same as the previous two, but here samples are taken whenever the wave crosses the base line no matter the direction. Just like the previous two, distance average taken (or you can set it explicitly with -l), and shorter distance means 0 bit, longer distance means 1 bit.

Syncronization Bits

By default all decoded bits are handled as data bits. Using -1 you can tell that each data bit is prefixed by a sync bit, -2 tells the sync bit follows the data bit, and -3 means the data bit is surrounded by two sync bits.

License

Cassette is Free and Open Source Software, licensed under the terms of GPL version 3 or (at your opinion) any later version.

Cheers, bzt