Skip to main content

Encode and decode JSON payloads in short audio snippets.

Project description

qraudio · Python

Encode JSON payloads into audio and decode them back.

The library serializes arbitrary Python objects into an audio signal using AFSK/GFSK/MFSK modulation with HDLC framing, Reed-Solomon FEC, and optional gzip compression. Payloads survive real-world audio paths: recording to WAV, playing over a speaker, or streaming through a microphone.

Requires Python ≥ 3.9. No runtime dependencies — stdlib only.


Installation

pip install qraudio

Profiles

A profile controls the modem settings (baud rate, frequencies, modulation). All functions accept an optional profile parameter.

Profile Modulation Notes
afsk-bell AFSK Default; broadest compatibility
afsk-fifth AFSK Higher baud, shorter audio
gfsk-fifth GFSK Smoother spectrum
mfsk MFSK Multi-tone; most robust over voice channels
from qraudio import ProfileName

ProfileName.AFSK_BELL   # "afsk-bell"
ProfileName.AFSK_FIFTH  # "afsk-fifth"
ProfileName.GFSK_FIFTH  # "gfsk-fifth"
ProfileName.MFSK        # "mfsk"

Core API

encode(*, payload, **options) -> EncodeResult

Encodes any JSON-serializable Python object into a list[float] of mono audio samples.

from qraudio import encode

result = encode(payload={"hello": "world"})
# result.samples      → list[float]
# result.sample_rate  → 48000
# result.duration_ms  → ~800
# result.profile      → ProfileName.AFSK_BELL

Keyword arguments

Parameter Type Default Description
payload object Required. The value to encode
profile ProfileName | str "afsk-bell" Modem profile
sample_rate int 48000 Output sample rate (Hz)
fec bool True Reed-Solomon forward error correction
gzip bool | "auto" "auto" Compress payload; "auto" only applies if it saves ≥ 8 bytes / 8%
gzip_compress Callable[[bytes], bytes] gzip.compress Override compress function
gzip_min_savings_bytes int 8 Auto-gzip byte savings threshold
gzip_min_savings_pct float 0.08 Auto-gzip percentage savings threshold
level_db float profile default Output level in dBFS
preamble_ms float profile default Flag preamble duration
fade_ms float profile default Amplitude fade in/out
lead_in bool profile default Prepend two-tone chime before payload
lead_in_tone_ms / lead_in_gap_ms float profile default Lead-in chime timing
tail_out bool profile default Append two-tone chime after payload
tail_tone_ms / tail_gap_ms float profile default Tail chime timing

decode(*, samples, **options) -> DecodeResult

Finds and decodes the first high-confidence payload in a list[float].
Raises ValueError if nothing is found.

from qraudio import decode

result = decode(samples=samples)
# result.json         → decoded Python value
# result.profile      → ProfileName.AFSK_BELL
# result.start_sample / end_sample → position in sample list
# result.confidence   → 0.0–1.0

scan(*, samples, **options) -> list[ScanResult]

Like decode, but returns all payloads found in the audio, sorted by position. Returns an empty list when nothing is detected.

from qraudio import scan

hits = scan(samples=samples)
for hit in hits:
    print(hit.json, hit.start_sample)

Keyword arguments for decode / scan

Parameter Type Description
samples list[float] Required. The audio to decode
profile ProfileName | str Narrow search to one profile (faster)
sample_rate int Sample rate of the input (default 48000)
gzip_decompress Callable[[bytes], bytes] Override decompress function (default gzip.decompress)
min_confidence float Minimum confidence threshold for scan (default 0.8)

WAV helpers (in-memory)

Gzip is handled automatically using gzip from the standard library.

from qraudio import encodeWav, decodeWav, scanWav, prependPayloadToWav

# Encode JSON → WAV bytes
result = encodeWav(payload={"track": 1})        # EncodeWavResult
wav_bytes: bytes = result.wav

# Decode WAV bytes → JSON
result = decodeWav(wav_bytes=wav_bytes)
print(result.json)

# Find all payloads in WAV bytes
hits = scanWav(wav_bytes=wav_bytes)

# Prepend encoded payload before existing audio
result = prependPayloadToWav(wav_bytes=existing_wav_bytes, payload={"track": 1})

prependPayloadToWav accepts pad_seconds, pre_pad_seconds, and post_pad_seconds to add silence around the encoded payload (default 0.25 s).

All WAV helpers forward extra keyword arguments to encode / decode.

Low-level WAV encoding

from qraudio import encodeWavSamples, decodeWavSamples

# list[float] → WAV bytes  (fmt: "pcm16" | "float32")
wav = encodeWavSamples(samples=samples, sample_rate=48000, fmt="pcm16")

# WAV bytes → WavData(sampleRate, channels, format, samples)
data = decodeWavSamples(wav_bytes=wav)

File I/O helpers

from qraudio import encodeWavFile, decodeWavFile, scanWavFile, prependPayloadToWavFile

encodeWavFile(out_path="output.wav", payload={"hello": "world"})
result = decodeWavFile(path="output.wav")
hits   = scanWavFile(path="output.wav")
prependPayloadToWavFile(in_path="music.wav", out_path="tagged.wav", payload={"track": 1})

Paths can be str or pathlib.Path.


CLI

The package installs a qraudio command.

qraudio <command> [options]

Commands:
  encode   Encode JSON payload to a WAV file
  decode   Decode a WAV file to JSON
  scan     Scan a WAV file for all payloads
  prepend  Prepend an encoded payload to an existing WAV file

Encode

qraudio encode --file payload.json --out out.wav
qraudio encode --file payload.json --out out.wav --profile mfsk --gzip
echo '{"x":1}' | qraudio encode --out out.wav

Decode

qraudio decode --in out.wav
cat out.wav | qraudio decode

Scan

qraudio scan --in recording.wav
cat recording.wav | qraudio scan

Prepend

qraudio prepend --in music.wav --out tagged.wav --file payload.json --pad-seconds 0.5

Common flags: --profile <afsk-bell|afsk-fifth|gfsk-fifth|mfsk>, --format <pcm16|float32>, --gzip, --no-fec.
--in / --out accept - or may be omitted to read/write stdin/stdout.


Development

# Install dev dependencies (uv recommended)
uv sync

# Run tests
uv run python -m pytest

# Run a single test file
uv run python -m pytest tests/test_codec.py

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

qraudio-0.1.0.tar.gz (22.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

qraudio-0.1.0-py3-none-any.whl (23.9 kB view details)

Uploaded Python 3

File details

Details for the file qraudio-0.1.0.tar.gz.

File metadata

  • Download URL: qraudio-0.1.0.tar.gz
  • Upload date:
  • Size: 22.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for qraudio-0.1.0.tar.gz
Algorithm Hash digest
SHA256 78373525961e4b507b69c8b2f2ad040a13c155cfc6d2d840da62931217ad6f55
MD5 f1193b922708073fe4dafcf0fde5d837
BLAKE2b-256 361c9d7e4eebe26ef6739a1e05ae9b5d16273e8cba6c6b9c64384ec54ad7c031

See more details on using hashes here.

File details

Details for the file qraudio-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: qraudio-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 23.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for qraudio-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2024058983ec996050784604b48571d7d2f0abacb4bd82e9c079a5b814e9c074
MD5 8bd4789dd0a606aab881219d9654e309
BLAKE2b-256 d99d2ff96701605745db71d7eb5c7d14250f74cdee121e53fc8316fe43c176c4

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page