An acoustic modem that transmits images over sound. An image is encoded into an audio signal, played through a speaker, recorded by a microphone on another device, and reconstructed on the receiving end.
Transmission is based on frequency-shift keying (FSK), with the receiver reading data via the FFT. The pipeline includes chirp-based synchronisation, a self-describing packet header, optional DCT compression (grayscale and color), and Reed-Solomon forward error correction.
- FSK modulation with FFT demodulation
- Chirp preamble synchronisation via cross-correlation
- Six raw image formats (RGB888, RGB565, RGB332, GRAY8, GRAY4, BW1)
- DCT compression (grayscale and YCbCr 4:2:0 color)
- Reed-Solomon error correction
- Parallel transmission (multiple bytes per window)
- Transmit to a WAV file or play directly through a speaker
- Receive from a WAV file or record live from a microphone
- Python 3.10+
numpyscipypillowsounddevicereedsolo
Install with:
pip install numpy scipy pillow sounddevice reedsolo
python send.py <image> <output.wav | --play> [options]
Encode an image to a WAV file:
python send.py images/test.png out.wav
Play directly through the speaker instead of writing a file:
python send.py images/test.png --play
python receive.py <input.wav | --record <seconds>> <output_image> [options]
Decode from a WAV file:
python receive.py out.wav recovered.png
Record live from the microphone for a given number of seconds:
python receive.py --record 10 recovered.png
| Option | Description | Default |
|---|---|---|
--format F |
Raw image format, F from 0 to 5 (see below) |
0 (RGB888) |
--dct Q |
Grayscale DCT compression at quality Q (1–100) |
off |
--dct-color Q |
Color DCT compression (YCbCr 4:2:0) at quality Q |
off |
--compress |
Run-length encoding (RLE) | off |
--fec N |
Reed-Solomon with N ECC bytes, corrects up to N/2 errors per block |
0 (off) |
--parallel N |
Send N bytes per window in parallel (faster, wider band) |
1 |
--spec PATH |
Save a spectrogram of the signal to PATH |
off |
--show-spec |
Display the spectrogram interactively | off |
| Value | Format | Bits/pixel |
|---|---|---|
| 0 | RGB888 | 24 |
| 1 | GRAY8 | 8 |
| 2 | RGB332 | 8 |
| 3 | RGB565 | 16 |
| 4 | GRAY4 | 4 |
| 5 | BW1 | 1 |
The header records the format and all other parameters, so the receiver needs no options to match; it reads everything from the transmitted header.
| Option | Description |
|---|---|
--spec PATH |
Save a spectrogram of the recorded signal to PATH |
--show-spec |
Display the spectrogram interactively |
Color DCT at quality 50 with error correction, written to a file:
python send.py images/test.png out.wav --dct-color 50 --fec 32
python receive.py out.wav recovered.png
Live transmission over the air (run on two machines):
# Receiver (start first)
python receive.py --record 15 recovered.png
# Sender
python send.py images/test.png --play --format 0 --fec 32
send.py Encoder entry point
receive.py Decoder entry point
modem/ Core library
protocol.py Constants (sample rate, window size, frequencies)
encoder.py FSK modulation
decoder.py FFT demodulation
sync.py Chirp generation and cross-correlation
header.py Self-describing packet header
image_codec.py Image loading and raw format packing
dct_codec.py DCT compression (grayscale and color)
compress.py Run-length encoding
fec.py Reed-Solomon error correction
audio_io.py WAV I/O, playback, recording
specplot.py Spectrogram plotting
sweep/ Frequency-response measurement tools
sweep_send.py Generate and play a frequency sweep
sweep_record.py Record the sweep
sweep_analyze.py Analyse the recorded sweep (frequency response)
Core parameters live in modem/protocol.py:
FS— sample rate (44100 Hz)N— samples per window (294; larger means slower but finer frequency resolution)BASE_FREQ,SPACING_HZ— carrier band layoutIMG_SIZE— default image size images are scaled to (32)